Commit 2af5c910dac6e2d55503d8927e5b98af2ee0f084

Authored by Marcos Pereira
1 parent 4b11b708

Push Notification plugin

Enables push notification for mobile platforms using Google Cloud Message infrastructure.

- Administrators can choose among all available notification
- Users can activate notifications through API and GUI
- Users devices MUST be registered through API to use push notifications
- Other plugins can add users to be notified for each notification.
- Available notifications can be found in the file "notification_settings.rb"

    Signed-off-by: Marcos Ramos <ms.ramos@outlook.com>
    Signed-off-by: Marcos Ronaldo <marcos.rpj2@gmail.com>
Showing 45 changed files with 1579 additions and 0 deletions   Show diff stats
app/models/article.rb
... ... @@ -12,6 +12,7 @@ class Article &lt; ActiveRecord::Base
12 12 :author, :display_preview, :published_at, :person_followers
13 13  
14 14 acts_as_having_image
  15 + include Noosfero::Plugin::HotSpot
15 16  
16 17 SEARCHABLE_FIELDS = {
17 18 :name => {:label => _('Name'), :weight => 10},
... ...
app/models/task.rb
... ... @@ -31,6 +31,8 @@ class Task &lt; ActiveRecord::Base
31 31 end
32 32 end
33 33  
  34 + include Noosfero::Plugin::HotSpot
  35 +
34 36 belongs_to :requestor, :class_name => 'Profile', :foreign_key => :requestor_id
35 37 belongs_to :target, :foreign_key => :target_id, :polymorphic => true
36 38 belongs_to :responsible, :class_name => 'Person', :foreign_key => :responsible_id
... ... @@ -253,6 +255,7 @@ class Task &lt; ActiveRecord::Base
253 255 end
254 256  
255 257 def environment
  258 + return target if target.kind_of?(Environment)
256 259 self.target.environment unless self.target.nil?
257 260 end
258 261  
... ...
plugins/push_notification/Gemfile 0 → 100644
... ... @@ -0,0 +1 @@
  1 +gem "gcm"
... ...
plugins/push_notification/README 0 → 100644
... ... @@ -0,0 +1,140 @@
  1 +README - GCM - Google Cloud Message Plugin
  2 +==========================================
  3 +
  4 +This plugin enables push notifications for mobile platforms
  5 +using Google Cloud Message infrastructure.
  6 +
  7 +INSTALL
  8 +=======
  9 +
  10 +Enable Plugin
  11 +-------------
  12 +
  13 +You need to enable GCM Plugin on your Noosfero:
  14 +
  15 +cd <your_noosfero_dir>
  16 +./script/noosfero-plugins install gcm
  17 +./script/noosfero-plugins enable gcm
  18 +
  19 +Activation and Plugin Configuration
  20 +-----------------------------------
  21 +
  22 +As a Noosfero administrator user, go to administrator panel:
  23 +
  24 +- Click on "Enable/disable plugins" option
  25 +- Click on "Google Cloud Message Plugin" check-box
  26 +- Click on "Configurations" just below the previous step
  27 +- *Fill in the form with your server API key registered for GCM
  28 +- Select from the check-boxes the notifications you want GCM plugin to send.
  29 +
  30 +*API key can be obtained creating a project in the GCM console, needed when preparing the application to use push notification.
  31 +Link below:
  32 +
  33 +- https://console.developers.google.com/start
  34 +
  35 +More information can be found in the following links:
  36 +- https://developers.google.com/cloud-messaging/gcm
  37 +- https://developers.google.com/cloud-messaging/server
  38 +- https://developers.google.com/cloud-messaging/registration
  39 +- https://developers.google.com/instance-id/
  40 +
  41 +After that, the mobile application need to take care of device registration to GCM and send the device token
  42 +to GCM plugin through noosfero API. There are endpoints to add, see and remove device tokens.
  43 +
  44 +- post request to /api/v1/push_notification_plugin/device_tokens with "tokens" parameter like token1,token2,token3
  45 +will register the tokens token1, token2 and token3 to the api current logged user, defined by noosfero's private token
  46 +also passed as parameter
  47 +
  48 +- get request to /api/v1/push_notification_plugin/device_tokens
  49 +will return a list with all the tokens for the current user logged in the api
  50 +
  51 +- delete request to /api/v1/push_notification_plugin/device_tokens with "tokens" parameter like token1,token2
  52 +will remove token1 and token2 from the list of tokens for the logged user
  53 +
  54 +- get request to /api/v1/possible_notifications
  55 +will return a list with all possible notifications available for user to activate/deactivate
  56 +
  57 +- get request to /api/v1/notification_settings
  58 +will return the notification settings for the logged user
  59 +
  60 +- post request to /api/v1/notification_settings
  61 +will activate/deactivate each notification passed as parameter. For instance, when receiving "new comment" as a parameter with value 1,
  62 +the notifications for "new comment" will be activated for the logged user
  63 +
  64 +- get request to /api/v1/active_notifications
  65 +will return a list with all active notifications for the logged user
  66 +
  67 +- get request to /api/v1/inactive_notifications
  68 +will return a list with all inactive notifications for the logged user
  69 +
  70 +In any endpoint, there is an option to pass target_id so you can add, see and delete tokens of other users.
  71 +However, only the admin has permission to do so. Normal users can pass target_id option with it's own id,
  72 +but the behavior is the same as passing no parameter.
  73 +
  74 +As a noosfero user, you can change what notifications you want to receive through web interface as well. In your profile control panel,
  75 +go to "Notification" and activate/deactivate notifications from there.
  76 +
  77 +
  78 +Add Users to be notificated from another plugin
  79 +------------------------------------------------
  80 +
  81 +As a noosfero plugin developer, you can have your plugin to add users to be notified in some notifications implemented here.
  82 +The way to do it is following the steps:
  83 +
  84 + 1 - Create a class with a callback for the specific notification you want to add users to. For instance, add users to the
  85 +new comment notification creating a class with a method named** "push_notification_new_comment_additional_users". This method needs to
  86 +return an Array with the list of users to be notified. Don't worry, no user will be notified twice.
  87 + 2 - Subscribe the class calling the method PushNotificationPlugin::subscribe(environment,notification,klass). For a class CommentCallBack,
  88 +call "PushNotificationPlugin::subscribe(<environment>, "new_comment", CommentCallBack)", with the environment object instead of
  89 +<environment>.
  90 +
  91 +- Unsubscribe at any time calling PushNotificationPlugin::unsubscribe(environment, notification, klass)
  92 +- Check the list of subscribers for any notification calling PushNotificationPlugin::subscribers(environment,notification)
  93 +
  94 +**The method will always be called "push_notification_" + <notification_name> + "additional_users". The class name does not matter, but it's
  95 +important to use the full class name, with namespace, so there is no future problem with the callback. The method need to be a class method,
  96 +not an instance method.
  97 +
  98 +Some details about the subscription methods:
  99 + - subscribe:
  100 + -> return null if the klass passed do not implement the method for the notification passed
  101 + -> return null if the notification is not implemented in PushNotificationPlugin
  102 + -> return true/false otherwise with the return of the save method
  103 + - unsubscribe:
  104 + -> return null if the notification is not implemented in PushNotificationPlugin
  105 + -> return true if the klass was sucessfully subscribed
  106 + -> return false if the klass was not subscribed or the unsubscription fails by any means
  107 + - subscribers:
  108 + -> return null if the notification is not implemented in PushNotificationPlugin
  109 + -> return empty array if there is no subscribers for that notification in that environment
  110 + -> return an array with all the subscribers otherwise
  111 +
  112 +Translate Plugin
  113 +------------------
  114 +
  115 +To translate the strings used in the plugin, create a copy of the file "en-US.yml" with your locale and
  116 +translate the strings in there for each key. These files are stored in the folder "locales".
  117 +
  118 +Get Involved
  119 +============
  120 +
  121 +If you find any bug and/or want to collaborate, please send an e-mail to marcos.rpj2@gmail.com
  122 +
  123 +LICENSE
  124 +=======
  125 +
  126 +Copyright (c) The Author developers.
  127 +
  128 +See Noosfero license.
  129 +
  130 +
  131 +AUTHORS
  132 +=======
  133 +
  134 +Marcos Ronaldo Pereira Junior (marcos.rpj2 at gmail.com)
  135 +Marcos da Silva Ramos (msramos at outlook.com)
  136 +
  137 +ACKNOWLEDGMENTS
  138 +===============
  139 +
  140 +The authors have been supported by SNJ (Secretaria Nacional da Juventude)
... ...
plugins/push_notification/after_enable.rb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +puts "\n"
  2 +puts "I: \e[33mMake sure you configure your Google Cloud Message's API key in the administration page!\e[0m"
  3 +puts "\n"
... ...
plugins/push_notification/controllers/push_notification_plugin_admin_controller.rb 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +require_relative "../lib/notification_settings"
  2 +
  3 +class PushNotificationPluginAdminController < PluginAdminController
  4 + append_view_path File.join(File.dirname(__FILE__) + '/../views')
  5 +
  6 + def index
  7 + @server_settings = Noosfero::Plugin::Settings.new(environment, PushNotificationPlugin)
  8 + @settings = @server_settings.notifications || PushNotificationPlugin::NotificationSettings.default_hash_flags
  9 + end
  10 +
  11 + def update
  12 + data = params[:server_settings]
  13 + data[:notifications] = params[:settings]
  14 + @server_settings = Noosfero::Plugin::Settings.new(environment, PushNotificationPlugin, data)
  15 + @server_settings.save!
  16 + redirect_to :action => "index"
  17 + end
  18 +
  19 +end
... ...
plugins/push_notification/controllers/push_notification_plugin_myprofile_controller.rb 0 → 100644
... ... @@ -0,0 +1,36 @@
  1 +require_relative "../lib/notification_settings"
  2 +require_relative "../lib/device_token"
  3 +
  4 +class PushNotificationPluginMyprofileController < MyProfileController
  5 +
  6 + append_view_path File.join(File.dirname(__FILE__) + '/../views')
  7 +
  8 + def index
  9 + @devices = current_user.device_tokens
  10 + @settings = filter_notifications current_user.notification_settings.hash_flags
  11 + end
  12 +
  13 + def delete_device
  14 + device = PushNotificationPlugin::DeviceToken.find(params["device"])
  15 + device.delete
  16 + redirect_to :action => "index"
  17 + end
  18 +
  19 + def update_settings
  20 + current_user.notification_settings.set_notifications(params["settings"] || {})
  21 + current_user.save
  22 + redirect_to :action => "index"
  23 + end
  24 +
  25 + private
  26 +
  27 + def filter_notifications hash_flags
  28 + server_settings = Noosfero::Plugin::Settings.new(environment, PushNotificationPlugin)
  29 + server_notifications = server_settings.notifications || {}
  30 + filtered_settings = {}
  31 + hash_flags.each do |notification, enabled|
  32 + filtered_settings[notification] = enabled if server_notifications[notification] == "1"
  33 + end
  34 + filtered_settings
  35 + end
  36 +end
... ...
plugins/push_notification/db/migrate/20160109195305_create_device_tokens.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +class CreateDeviceTokens < ActiveRecord::Migration
  2 + def change
  3 + create_table :push_notification_plugin_device_tokens do |t|
  4 + t.integer :user_id, null: false
  5 + t.string :device_name, null:false
  6 + t.string :token, null: false, unique: true
  7 + t.timestamps null: false
  8 + t.index :user_id
  9 + end
  10 + end
  11 +end
... ...
plugins/push_notification/db/migrate/20160110125119_create_notification_settings.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +class CreateNotificationSettings < ActiveRecord::Migration
  2 + def change
  3 + create_table :push_notification_plugin_notification_settings do |t|
  4 + t.integer :user_id, null: false
  5 + t.integer :notifications, null: false, :default => 0
  6 + t.timestamps null: false
  7 + t.index :user_id
  8 + end
  9 + end
  10 +end
... ...
plugins/push_notification/db/migrate/20160118192037_create_notification_subscriptions.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class CreateNotificationSubscriptions < ActiveRecord::Migration
  2 + def change
  3 + create_table :push_notification_plugin_notification_subscriptions do |t|
  4 + t.string :notification, :unique => true
  5 + t.integer :environment_id, null: false
  6 + t.text :subscribers
  7 + end
  8 + end
  9 +end
... ...
plugins/push_notification/dependencies.rb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +require 'gcm'
... ...
plugins/push_notification/features/push_notification_admin.feature 0 → 100644
... ... @@ -0,0 +1,46 @@
  1 +Feature: push notification administration
  2 + As an administrator
  3 + I want to configure the push notification plugin
  4 +
  5 + Background:
  6 + Given plugin PushNotification is enabled on environment
  7 + Given I am logged in as admin
  8 +
  9 + Scenario: configure the api key
  10 + Given I go to /admin/plugin/push_notification
  11 + And I fill in "Server API key" with "ABCDEFGH1234567890"
  12 + When I press "Save"
  13 + Then I should be on /admin/plugin/push_notification
  14 + Then the "Server API key" field should contain "ABCDEFGH1234567890"
  15 +
  16 + Scenario: change the api key
  17 + Given that "old_key" is the server api key
  18 + Given I go to /admin/plugin/push_notification
  19 + Then the "Server API key" field should contain "old_key"
  20 + When I fill in "Server API key" with "new_key"
  21 + And I press "Save"
  22 + Then I should be on /admin/plugin/push_notification
  23 + Then the "Server API key" field should contain "new_key"
  24 +
  25 + Scenario: enable notifications
  26 + Given I go to /admin/plugin/push_notification
  27 + And I check "settings_add_member"
  28 + And I check "settings_new_article"
  29 + When I press "Save"
  30 + Then the "settings_add_member" checkbox should be checked
  31 + Then the "settings_new_article" checkbox should be checked
  32 +
  33 + Scenario: disable notifications
  34 + Given the following notifications
  35 + |name|
  36 + |add_friend|
  37 + |add_member|
  38 + And I go to /admin/plugin/push_notification
  39 + Then the "settings_add_friend" checkbox should be checked
  40 + Then the "settings_add_member" checkbox should be checked
  41 + And I uncheck "settings_add_friend"
  42 + And I uncheck "settings_add_member"
  43 + When I press "Save"
  44 + Then I should be on /admin/plugin/push_notification
  45 + Then the "settings_add_friend" checkbox should not be checked
  46 + Then the "settings_add_member" checkbox should not be checked
... ...
plugins/push_notification/features/push_notification_user_config.feature 0 → 100644
... ... @@ -0,0 +1,73 @@
  1 +Feature: push notification user config
  2 + As an user
  3 + I want to configure the notification I want to receive
  4 +
  5 + Background:
  6 + Given plugin PushNotification is enabled on environment
  7 + Given the following users
  8 + |login | name |
  9 + |joaosilva| Joao Silva |
  10 +
  11 + Scenario: the user should see his devices
  12 + Given that the user joaosilva has the following devices
  13 + | token | name |
  14 + | "token_1" | "Banana Phone" |
  15 + | "token_2" | "Zamzung Phone" |
  16 + Given I am logged in as "joaosilva"
  17 + Given I go to joaosilva's control panel
  18 + When I follow "Notifications"
  19 + Then I should see "Banana Phone"
  20 + And I should see "Zamzung Phone"
  21 +
  22 + Scenario: the user should delete his devices
  23 + Given that the user joaosilva has the following devices
  24 + | token | name |
  25 + | "token_1" | "Zamzung Phone" |
  26 + Given I am logged in as "joaosilva"
  27 + Given I go to joaosilva's control panel
  28 + When I follow "Notifications"
  29 + When I follow "delete_device_1"
  30 + Then I should be on /myprofile/joaosilva/plugin/push_notification
  31 + And I should not see "Zamzung Phone"
  32 +
  33 + Scenario: the user should only see the notifications enabled in the environment
  34 + Given the following notifications
  35 + |name |
  36 + |add_friend |
  37 + |new_article |
  38 + Given I am logged in as "joaosilva"
  39 + Given I go to joaosilva's control panel
  40 + When I follow "Notifications"
  41 + Then I should see "Add Friend"
  42 + And I should see "New Article"
  43 + And I should not see "New Member"
  44 + And I should not see "Approve Article"
  45 +
  46 + Scenario: the user should be able to enable notifications
  47 + Given the following notifications
  48 + |name |
  49 + |add_friend |
  50 + |new_article |
  51 + Given I am logged in as "joaosilva"
  52 + Given I go to joaosilva's control panel
  53 + When I follow "Notifications"
  54 + And I check "settings_add_friend"
  55 + And I check "settings_new_article"
  56 + When I press "Save"
  57 + Then the "settings_add_friend" checkbox should be checked
  58 + Then the "settings_new_article" checkbox should be checked
  59 +
  60 + Scenario: the user should be able to disable notifications
  61 + Given the following notifications
  62 + | name |
  63 + | add_friend |
  64 + | new_article |
  65 + Given that the user joaosilva has the following notifications
  66 + | name |
  67 + | add_friend |
  68 + Given I am logged in as "joaosilva"
  69 + Given I go to joaosilva's control panel
  70 + When I follow "Notifications"
  71 + And I uncheck "settings_add_friend"
  72 + When I press "Save"
  73 + Then the "settings_add_friend" checkbox should not be checked
... ...
plugins/push_notification/features/step_definitions/push_notification_steps.rb 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +Given /^the following notifications$/ do |table|
  2 + settings = {}
  3 + table.hashes.each do |item|
  4 + settings[item[:name]] = "1"
  5 + end
  6 + data = {:notifications => settings}
  7 + server_settings = Noosfero::Plugin::Settings.new(Environment.default, PushNotificationPlugin, data)
  8 + server_settings.save!
  9 +end
  10 +
  11 +Given /^that the user ([^\"]*) has the following devices$/ do |user_name,table|
  12 + user = User.find_by(:login => user_name)
  13 + table.hashes.each do |item|
  14 + PushNotificationPlugin::DeviceToken.create(:user => user, :token => item[:token], :device_name => item[:name])
  15 + end
  16 +end
  17 +
  18 +Given /^that the user ([^\"]*) has the following notifications$/ do |user_name,table|
  19 + user = User.find_by(:login => user_name)
  20 + table.hashes.each do |item|
  21 + user.notification_settings.activate_notification item[:name]
  22 + end
  23 + user.save!
  24 +end
  25 +
  26 +Given /^that "([^\"]*)" is the server api key$/ do |key|
  27 + data = {:server_api_key => key}
  28 + server_settings = Noosfero::Plugin::Settings.new(Environment.default, PushNotificationPlugin, data)
  29 + server_settings.save!
  30 +end
... ...
plugins/push_notification/lib/device_token.rb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +class PushNotificationPlugin::DeviceToken < ActiveRecord::Base
  2 + belongs_to :user
  3 + attr_accessible :token, :device_name, :user
  4 +
  5 + after_save :check_notification_settings
  6 +
  7 + private
  8 +
  9 + def check_notification_settings
  10 + if user.notification_settings.nil?
  11 + user.notification_settings=PushNotificationPlugin::NotificationSettings.new
  12 + user.save
  13 + end
  14 + end
  15 +
  16 +end
... ...
plugins/push_notification/lib/ext/user.rb 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +require_dependency "user"
  2 +require_relative "../notification_subscription"
  3 +require_relative "../notification_settings"
  4 +require_relative "../device_token"
  5 +
  6 +class User
  7 + has_many :device_tokens, class_name: "PushNotificationPlugin::DeviceToken", :dependent => :destroy
  8 + has_one :notification_settings, class_name: "PushNotificationPlugin::NotificationSettings", :dependent => :destroy, :autosave => true
  9 +
  10 + after_save :save_notification_settings
  11 + after_initialize :setup_notification_settings
  12 +
  13 + def device_token_list
  14 + device_tokens.map { |t| t.token }
  15 + end
  16 +
  17 + def enabled_notifications
  18 + notification_settings.notifications
  19 + end
  20 +
  21 + def enabled_notifications=(notifications)
  22 + notification_settings.notifications=notifications
  23 + end
  24 +
  25 + private
  26 +
  27 + def save_notification_settings
  28 + notification_settings.save if notification_settings
  29 + end
  30 +
  31 + def setup_notification_settings
  32 + self.notification_settings = PushNotificationPlugin::NotificationSettings.new(:user => self) if self.notification_settings.nil?
  33 + end
  34 +
  35 +end
... ...
plugins/push_notification/lib/notification_settings.rb 0 → 100644
... ... @@ -0,0 +1,69 @@
  1 +class PushNotificationPlugin::NotificationSettings < ActiveRecord::Base
  2 +
  3 + NOTIFICATIONS= {
  4 + "add_friend" => 0x1,
  5 + "new_comment" => 0x2,
  6 + "add_member" => 0x4,
  7 + "suggest_article" => 0x8,
  8 + "new_article" => 0x10,
  9 + "approve_article" => 0x20,
  10 + "add_friend_result" => 0x40,
  11 + "add_member_result" => 0x80,
  12 + "approve_article_result" => 0x100,
  13 + "suggest_article_result" => 0x200
  14 + }
  15 +
  16 + belongs_to :user
  17 + attr_accessible :user, :notifications
  18 +
  19 + def self.default_hash_flags
  20 + default_hash_flags = {}
  21 + NOTIFICATIONS.keys.each do |event|
  22 + default_hash_flags[event] = "0"
  23 + end
  24 + default_hash_flags
  25 + end
  26 +
  27 + def hash_flags
  28 + flags = {}
  29 + NOTIFICATIONS.keys.each do |notification|
  30 + flags[notification] = active? notification
  31 + end
  32 + flags
  33 + end
  34 +
  35 + def active_notifications
  36 + NOTIFICATIONS.keys.select{|notification| active?(notification)}
  37 + end
  38 +
  39 + def inactive_notifications
  40 + NOTIFICATIONS.keys.select{|notification| !active?(notification)}
  41 + end
  42 +
  43 + def active? notification
  44 + ((self.notifications & NOTIFICATIONS[notification])!=0)
  45 + end
  46 +
  47 + def activate_notification notification
  48 + self.notifications |= NOTIFICATIONS[notification]
  49 + end
  50 +
  51 + def set_notifications notifications
  52 + NOTIFICATIONS.keys.each do |event|
  53 + set_notification_state event, notifications[event]
  54 + end
  55 + end
  56 +
  57 + def deactivate_notification notification
  58 + self.notifications &= ~NOTIFICATIONS[notification]
  59 + end
  60 +
  61 + def set_notification_state notification, state
  62 + if state.blank? || (state == 0) || (state == "0") || state == false
  63 + deactivate_notification notification
  64 + else
  65 + activate_notification notification
  66 + end
  67 + end
  68 +
  69 +end
... ...
plugins/push_notification/lib/notification_subscription.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class PushNotificationPlugin::NotificationSubscription < ActiveRecord::Base
  2 + belongs_to :environment
  3 + attr_accessible :subscribers, :notification, :environment
  4 +
  5 + validates :notification, :uniqueness => true
  6 + serialize :subscribers
  7 +
  8 +end
  9 +
... ...
plugins/push_notification/lib/push_notification_helper.rb 0 → 100644
... ... @@ -0,0 +1,46 @@
  1 +module PushNotificationHelper
  2 +
  3 + def gcm_instance
  4 + api_key = settings[:server_api_key]
  5 + gcm = GCM.new(api_key)
  6 + gcm
  7 + end
  8 +
  9 + def settings
  10 + return Noosfero::Plugin::Settings.new(environment, PushNotificationPlugin.class)
  11 + end
  12 +
  13 + #data should be a hash, like {some_info: 123123}
  14 + def send_to_users(flag, users, data)
  15 + return false unless users.present?
  16 + users |= subscribers_additional_users(flag, users.first.environment)
  17 + users = filter_users_for_flag(flag, users)
  18 + return false unless users.present?
  19 + tokens = tokens_for_users(users)
  20 + gcm = gcm_instance
  21 + response = gcm.send(tokens, data)
  22 + response[:response]
  23 + end
  24 +
  25 + def filter_users_for_flag(flag, users)
  26 + users.select{|u| u.notification_settings.active?(flag)}
  27 + end
  28 +
  29 + def tokens_for_users(users)
  30 + tokens=[]
  31 + users.each{|c| tokens+= c.device_token_list}
  32 + return tokens
  33 + end
  34 +
  35 + def subscribers_additional_users notification, environment
  36 + subs = PushNotificationPlugin::subscribers(environment, notification)
  37 + users=[]
  38 + if subs.present?
  39 + subs.each do |s|
  40 + users+=s.send("push_notification_#{notification}_additional_users".to_sym)
  41 + end
  42 + end
  43 + users
  44 + end
  45 +
  46 +end
... ...
plugins/push_notification/lib/push_notification_plugin.rb 0 → 100644
... ... @@ -0,0 +1,62 @@
  1 +class PushNotificationPlugin < Noosfero::Plugin
  2 +
  3 + include Noosfero::Plugin::HotSpot
  4 + include PushNotificationPlugin::Observers
  5 +
  6 + def self.plugin_name
  7 + I18n.t("push_notification_plugin.lib.plugin.name")
  8 + end
  9 +
  10 + def self.subscribe environment, notification, klass
  11 + return nil unless PushNotificationPlugin::NotificationSettings::NOTIFICATIONS.keys.include?(notification)
  12 + return nil unless klass.name.constantize.respond_to?("push_notification_#{notification}_additional_users".to_sym)
  13 +
  14 + notification_subscription = PushNotificationPlugin::NotificationSubscription.where(:notification => notification).first
  15 + notification_subscription ||= PushNotificationPlugin::NotificationSubscription.new({:notification => notification,
  16 + :environment => environment, :subscribers => [klass.name]})
  17 +
  18 + notification_subscription.subscribers |= [klass.name]
  19 + notification_subscription.save
  20 + end
  21 +
  22 + def self.unsubscribe environment, notification, klass
  23 + return nil unless PushNotificationPlugin::NotificationSettings::NOTIFICATIONS.keys.include?(notification)
  24 + notification_subscription = PushNotificationPlugin::NotificationSubscription.where(:notification => notification, :environment => environment).first
  25 + unless notification_subscription.blank?
  26 + if notification_subscription.subscribers.include?(klass.name)
  27 + notification_subscription.subscribers -= [klass.name]
  28 + return notification_subscription.save
  29 + end
  30 + end
  31 + return false
  32 + end
  33 +
  34 + def self.subscribers environment, notification
  35 + return nil unless PushNotificationPlugin::NotificationSettings::NOTIFICATIONS.keys.include?(notification)
  36 + notification_subscription = PushNotificationPlugin::NotificationSubscription.where(:notification => notification, :environment => environment)
  37 + return [] if notification_subscription.blank?
  38 + notification_subscription.first.subscribers.map{|s| s.constantize}
  39 + end
  40 +
  41 + def self.api_mount_points
  42 + [PushNotificationPlugin::API]
  43 + end
  44 +
  45 + def self.plugin_description
  46 + I18n.t("push_notification_plugin.lib.plugin.description")
  47 + end
  48 +
  49 + def stylesheet?
  50 + true
  51 + end
  52 +
  53 + def control_panel_buttons
  54 + if context.profile.person?
  55 + {
  56 + title: I18n.t("push_notification_plugin.lib.plugin.panel_button"),
  57 + icon: 'push-notifications',
  58 + url: {controller: 'push_notification_plugin_myprofile', action: 'index'}
  59 + }
  60 + end
  61 + end
  62 +end
... ...
plugins/push_notification/lib/push_notification_plugin/api.rb 0 → 100644
... ... @@ -0,0 +1,96 @@
  1 +require File.dirname(__FILE__) + '/../../../../../lib/noosfero/api/helpers'
  2 +require_relative 'api_entities'
  3 +
  4 +class PushNotificationPlugin::API < Grape::API
  5 +
  6 + include Noosfero::API::APIHelpers
  7 +
  8 + resource :push_notification_plugin do
  9 +
  10 + helpers do
  11 + def target
  12 + if params.has_key?(:target_id)
  13 + target_user = environment.users.detect{|u|u.id == params[:target_id].to_i}
  14 + else
  15 + target_user = current_user
  16 + end
  17 +
  18 + if !current_person.is_admin? && (target_user.nil? || target_user!=current_user)
  19 + render_api_error!(_('Unauthorized'), 401)
  20 + end
  21 +
  22 + return target_user
  23 + end
  24 + end
  25 +
  26 + get 'device_tokens' do
  27 + authenticate!
  28 + target_user = target
  29 + tokens = target_user.device_token_list || []
  30 + present tokens
  31 + end
  32 +
  33 + post 'device_tokens' do
  34 + authenticate!
  35 + target_user = target
  36 +
  37 + bad_request!("device_name") unless params[:device_name]
  38 + token = PushNotificationPlugin::DeviceToken.new({:device_name => params[:device_name], :token => params[:token], :user => target_user}) if !target_user.device_token_list.include?(params[:token])
  39 +
  40 + target_user.device_tokens.push(token)
  41 +
  42 +
  43 + unless target_user.save
  44 + render_api_errors!(target_user.errors.full_messages)
  45 + end
  46 + present target_user, :with => PushNotificationPlugin::Entities::DeviceUser
  47 + end
  48 +
  49 + delete 'device_tokens' do
  50 + authenticate!
  51 + target_user = target
  52 +
  53 + PushNotificationPlugin::DeviceToken.delete_all(["token = ? AND user_id = (?)", params[:token],target_user.id])
  54 +
  55 + present target_user, :with => PushNotificationPlugin::Entities::DeviceUser
  56 + end
  57 +
  58 + get 'notification_settings' do
  59 + authenticate!
  60 + target_user = target
  61 +
  62 + present target_user, with: PushNotificationPlugin::Entities::DeviceUser
  63 + end
  64 +
  65 + get 'possible_notifications' do
  66 + result = {:possible_notifications => PushNotificationPlugin::NotificationSettings::NOTIFICATIONS.keys}
  67 + present result, with: Grape::Presenters::Presenter
  68 + end
  69 +
  70 + post 'notification_settings' do
  71 + authenticate!
  72 + target_user = target
  73 +
  74 + PushNotificationPlugin::NotificationSettings::NOTIFICATIONS.keys.each do |notification|
  75 + next unless params.keys.include?(notification)
  76 + state = params[notification]
  77 + target_user.notification_settings.set_notification_state notification, state
  78 + end
  79 +
  80 + target_user.save!
  81 + present target_user, with: PushNotificationPlugin::Entities::DeviceUser
  82 + end
  83 +
  84 + get 'active_notifications' do
  85 + authenticate!
  86 + target_user = target
  87 + present target_user.notification_settings.active_notifications, with: Grape::Presenters::Presenter
  88 + end
  89 +
  90 + get 'inactive_notifications' do
  91 + authenticate!
  92 + target_user = target
  93 + present target_user.notification_settings.inactive_notifications, with: Grape::Presenters::Presenter
  94 + end
  95 + end
  96 +end
... ...
plugins/push_notification/lib/push_notification_plugin/api_entities.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +module PushNotificationPlugin::Entities
  2 + class DeviceUser < Noosfero::API::Entities::User
  3 + expose :device_token_list, :as => :device_tokens
  4 + expose :notification_settings do |user, options|
  5 + user.notification_settings.hash_flags
  6 + end
  7 + end
  8 +
  9 +end
... ...
plugins/push_notification/lib/push_notification_plugin/observers.rb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +require_relative '../device_token'
  2 +Dir[File.dirname(__FILE__) + "/observers/*"].each {|file| require file}
  3 +module PushNotificationPlugin::Observers
  4 + include PushNotificationHelper
  5 + constants.collect{|const_name| const_get(const_name)}.select {|const| const.class == Module}.each{|submodule| include submodule}
  6 +end
... ...
plugins/push_notification/lib/push_notification_plugin/observers/add_friend_observer.rb 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +module PushNotificationPlugin::Observers
  2 + module AddFriendObserver
  3 + def add_friend_after_create_callback(add_friend)
  4 + requestor = add_friend.requestor
  5 + target = add_friend.target
  6 +
  7 + send_to_users("add_friend",
  8 + [target.user],
  9 + {:event => "Add Friend",
  10 + :requestor_id => requestor.id,
  11 + :requestor_name => requestor.name,
  12 + :task_id => add_friend.id}
  13 + )
  14 + end
  15 +
  16 + #check when task is finished
  17 + def add_friend_after_save_callback(add_friend)
  18 + requestor = add_friend.requestor
  19 + target = add_friend.target
  20 +
  21 + return false unless [Task::Status::FINISHED, Task::Status::CANCELLED].include?(add_friend.status)
  22 +
  23 + added = add_friend.status==Task::Status::FINISHED
  24 + event= added ? "Friendship accepted" : "Friendship refused"
  25 +
  26 + send_to_users("add_friend_result",
  27 + [requestor.user],
  28 + {:event => event,
  29 + :target_id => target.id,
  30 + :target_name => target.name,
  31 + :task_id => add_friend.id}
  32 + )
  33 + end
  34 + end
  35 +end
... ...
plugins/push_notification/lib/push_notification_plugin/observers/add_member_observer.rb 0 → 100644
... ... @@ -0,0 +1,36 @@
  1 +module PushNotificationPlugin::Observers
  2 + module AddMemberObserver
  3 + def add_member_after_create_callback(add_member)
  4 + requestor = add_member.requestor
  5 + target = add_member.target
  6 +
  7 + users = target.admins.map{|person| person.user}
  8 +
  9 + send_to_users("add_member",
  10 + users,
  11 + {:event => "Add Member to Organization",
  12 + :requestor_id => requestor.id,
  13 + :requestor_name => requestor.name,
  14 + :task_id => add_member.id}
  15 + )
  16 + end
  17 +
  18 + def add_member_after_save_callback(add_member)
  19 + requestor = add_member.requestor
  20 + target = add_member.target
  21 +
  22 + return false unless [Task::Status::FINISHED, Task::Status::CANCELLED].include?(add_member.status)
  23 +
  24 + accepted = add_member.status==Task::Status::FINISHED
  25 + event= accepted ? "Membership accepted" : "Membership rejected"
  26 +
  27 + send_to_users("add_member_result",
  28 + [requestor],
  29 + {:event => event,
  30 + :target_id => target.id,
  31 + :target_name => target.name,
  32 + :task_id => add_member.id}
  33 + )
  34 + end
  35 + end
  36 +end
... ...
plugins/push_notification/lib/push_notification_plugin/observers/approve_article_observer.rb 0 → 100644
... ... @@ -0,0 +1,42 @@
  1 +module PushNotificationPlugin::Observers
  2 + module ApproveArticleObserver
  3 + def approve_article_after_create_callback(approve_article)
  4 + requestor = approve_article.requestor
  5 + target = approve_article.target
  6 +
  7 + if target.person?
  8 + users = [target.user]
  9 + elsif target.organization?
  10 + users = target.admins.map{|person| person.user}
  11 + end
  12 +
  13 + send_to_users("approve_article",
  14 + users,
  15 + {:event => "Approve Article",
  16 + :requestor_id => requestor.id,
  17 + :requestor_name => requestor.name,
  18 + :article => approve_article.article,
  19 + :task_id => approve_article.id}
  20 + )
  21 + end
  22 +
  23 + def approve_article_after_save_callback(approve_article)
  24 + requestor = approve_article.requestor
  25 + target = approve_article.target
  26 +
  27 + return false unless [Task::Status::FINISHED, Task::Status::CANCELLED].include?(approve_article.status)
  28 +
  29 + accepted = approve_article.status==Task::Status::FINISHED
  30 + event= accepted ? "Article approved" : "Article rejected"
  31 +
  32 + send_to_users("approve_article_result",
  33 + [requestor],
  34 + {:event => event,
  35 + :target_id => target.id,
  36 + :target_name => target.name,
  37 + :article => approve_article.article,
  38 + :task_id => approve_article.id}
  39 + )
  40 + end
  41 + end
  42 +end
... ...
plugins/push_notification/lib/push_notification_plugin/observers/article_observer.rb 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +module PushNotificationPlugin::Observers
  2 + module ArticleObserver
  3 + def article_after_create_callback(article)
  4 + users=[]
  5 +
  6 + if article.profile.organization?
  7 + article.profile.members.each do |person|
  8 + users |= [person.user] if person.user.present?
  9 + end
  10 + elsif article.profile.person?
  11 + users |= [article.profile.user] if article.profile.user.present?
  12 + end
  13 +
  14 + send_to_users("new_article",
  15 + users,
  16 + {:event => "New article",
  17 + :article => article.id,
  18 + :article_body => article.body,
  19 + :article_title => article.title,
  20 + :article_name => article.name,
  21 + :author => article.author_name})
  22 + end
  23 + end
  24 +end
... ...
plugins/push_notification/lib/push_notification_plugin/observers/comment_observer.rb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +module PushNotificationPlugin::Observers
  2 + module CommentObserver
  3 + def comment_after_create_callback(comment)
  4 + users = comment.source.person_followers.map{|p| p.user}
  5 +
  6 + send_to_users("new_comment",
  7 + users,
  8 + {:event => "New Comment",
  9 + :article => comment.source.id,
  10 + :comment_body => comment.body,
  11 + :comment_title => comment.title,
  12 + :comment_name => comment.name,
  13 + :author => comment.author.name})
  14 + end
  15 + end
  16 +end
... ...
plugins/push_notification/lib/push_notification_plugin/observers/suggest_article_observer.rb 0 → 100644
... ... @@ -0,0 +1,42 @@
  1 +module PushNotificationPlugin::Observers
  2 + module SuggestArticleObserver
  3 + def suggest_article_after_create_callback(suggest_article)
  4 + requestor = suggest_article.requestor
  5 + target = suggest_article.target
  6 +
  7 + if target.person?
  8 + users = [target.user]
  9 + elsif target.organization?
  10 + users = target.admins.map{|person| person.user}
  11 + end
  12 +
  13 + send_to_users("suggest_article",
  14 + users,
  15 + {:event => "Add Member",
  16 + :requestor_id => requestor.id,
  17 + :requestor_name => requestor.name,
  18 + :article => suggest_article.article,
  19 + :task_id => suggest_article.id}
  20 + )
  21 + end
  22 +
  23 + def suggest_article_after_save_callback(suggest_article)
  24 + requestor = suggest_article.requestor
  25 + target = suggest_article.target
  26 +
  27 + return false unless [Task::Status::FINISHED, Task::Status::CANCELLED].include?(suggest_article.status)
  28 +
  29 + accepted = suggest_article.status==Task::Status::FINISHED
  30 + event= accepted ? "Article approved" : "Article rejected"
  31 +
  32 + send_to_users("suggest_article_result",
  33 + [requestor],
  34 + {:event => event,
  35 + :target_id => target.id,
  36 + :target_name => target.name,
  37 + :article => suggest_article.article,
  38 + :task_id => suggest_article.id}
  39 + )
  40 + end
  41 + end
  42 +end
... ...
plugins/push_notification/locales/en-US.yml 0 → 100644
... ... @@ -0,0 +1,41 @@
  1 +en-US: &en-US
  2 +
  3 + push_notification_plugin:
  4 + events:
  5 + add_friend: "Add Friend"
  6 + new_comment: "New Comment"
  7 + add_member: "New Member"
  8 + suggest_article: "Suggest Article"
  9 + new_article: "New Article"
  10 + approve_article: "Approve Article"
  11 + add_friend_result: "Friendship Request Result"
  12 + add_member_result: "Membership Request Result"
  13 + approve_article_result: "Article approval Result"
  14 + suggest_article_result: "Article suggestion Result"
  15 +
  16 + lib:
  17 + plugin:
  18 + name: "Push Notifications"
  19 + description: "Send push notifications to mobile devices using Google Cloud Message"
  20 + panel_button: "Notifications"
  21 + settings: "Push Notification Settings"
  22 + notifications: "Notifications"
  23 + enabled_notifications: "Enabled Notifications"
  24 + device: "Device"
  25 + actions: "Actions"
  26 +
  27 + controllers:
  28 + myprofile:
  29 + my_devices: "My Devices"
  30 + no_devices: "No device connected to your account"
  31 + delete_device: "Delete Device"
  32 + event: "Event"
  33 + enabled: "Enabled?"
  34 +
  35 + admin:
  36 + server_api_key: "Server API key"
  37 +
  38 +en_US:
  39 + <<: *en-US
  40 +en:
  41 + <<: *en-US
... ...
plugins/push_notification/locales/pt-BR.yml 0 → 100644
... ... @@ -0,0 +1,41 @@
  1 +pt-BR: &pt-BR
  2 +
  3 + push_notification_plugin:
  4 + events:
  5 + add_friend: "Nova Amizade"
  6 + new_comment: "Novo Comentário"
  7 + add_member: "Novo Membro"
  8 + suggest_article: "Artigo Sugerido"
  9 + new_article: "Novo Artigo"
  10 + approve_article: "Aprovar Artigo"
  11 + add_friend_result: "Resultado de Solicitação de Amizade"
  12 + add_member_result: "Resultado de Solicitação de participação de comunidade"
  13 + approve_article_result: "Resultado de Aprovação de Artigo"
  14 + suggest_article_result: "Resultado de Sugestão de Artigo"
  15 +
  16 + lib:
  17 + plugin:
  18 + name: "Notificações Push"
  19 + description: "Envie notificações para dispositivos móveis usando o Google Cloud Message"
  20 + panel_button: "Notificações"
  21 + settings: "Configuração das Notificações Push"
  22 + notifications: "Notificações"
  23 + enabled_notifications: "Notificações Habilitadas"
  24 + device: "Dispositivo"
  25 + actions: "Ações"
  26 +
  27 + controllers:
  28 + myprofile:
  29 + my_devices: "Meus Dispositivos"
  30 + no_devices: "Nenhum dispositivo conectado à sua conta."
  31 + delete_device: "Apagar Dispositivo"
  32 + event: "Evento"
  33 + enabled: "Ativo?"
  34 +
  35 + admin:
  36 + server_api_key: "Chave da API do Servidor"
  37 +
  38 +pt_BR:
  39 + <<: *pt-BR
  40 +pt:
  41 + <<: *pt-BR
... ...
plugins/push_notification/public/images/control-panel/push-notification-settings.jpg 0 → 100644

2.82 KB

plugins/push_notification/public/images/control-panel/push-notification-settings.png 0 → 100644

1.44 KB

plugins/push_notification/public/style.css 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +.controller-profile_editor a.control-panel-push-notifications {
  2 + background-image: url("/plugins/push_notification/images/control-panel/push-notification-settings.png");
  3 +}
  4 +
  5 +.controller-profile_editor .msie6 a.control-panel-push-notifications {
  6 + background-image: url("/plugins/push_notification/images/control-panel/push-notification-settings.jpg");
  7 +}
  8 +
... ...
plugins/push_notification/test/helpers/observers_test_helper.rb 0 → 100644
... ... @@ -0,0 +1,33 @@
  1 +module ObserversTestHelper
  2 +
  3 + def create_add_friend_task
  4 + user1 = fast_create(User)
  5 + person = fast_create(Person, :user_id => user1.id)
  6 + user2 = fast_create(User)
  7 + friend = fast_create(Person, :user_id => user2.id)
  8 + return AddFriend.create!(:requestor => person, :target => friend)
  9 + end
  10 +
  11 + def create_add_member_task
  12 + person = fast_create(Person)
  13 + community = fast_create(Community)
  14 + return AddMember.create!(:requestor => person, :target => community)
  15 + end
  16 +
  17 + def create_suggest_article_task
  18 + person = fast_create(Person)
  19 + community = fast_create(Community)
  20 + return SuggestArticle.create!(:target => community, :article => {:name => 'Munchkin', :body => 'Kill monsters!! Get treasures!! Stab your friends!!'}, :requestor => person)
  21 + end
  22 +
  23 + def create_approve_article_task
  24 + user1 = fast_create(User)
  25 + person = fast_create(Person, :user_id => user1.id)
  26 + article = fast_create(Article, :profile_id => person.id)
  27 + community = fast_create(Community)
  28 + community.add_member(person)
  29 + community.save!
  30 +
  31 + return ApproveArticle.create!(:article => article, :target => community, :requestor => person)
  32 + end
  33 +end
... ...
plugins/push_notification/test/unit/api_test.rb 0 → 100644
... ... @@ -0,0 +1,178 @@
  1 +require_relative '../../../../test/unit/api/test_helper'
  2 +
  3 +class PushNotificationApiTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + login_api
  7 + environment = Environment.default
  8 + environment.enable_plugin(PushNotificationPlugin)
  9 + end
  10 +
  11 + should 'list all my device tokens' do
  12 + logged_user = @user
  13 + token1 = PushNotificationPlugin::DeviceToken.create!(:token => "firsttoken", device_name: "my device", :user => logged_user)
  14 + token2 = PushNotificationPlugin::DeviceToken.create!(:token => "secondtoken", device_name: "my device", :user => logged_user)
  15 +
  16 + get "/api/v1/push_notification_plugin/device_tokens?#{params.to_query}"
  17 + json = JSON.parse(last_response.body)
  18 + assert_equivalent [token1.token, token2.token], json
  19 + end
  20 +
  21 + should 'not list other people device tokens' do
  22 + user = User.create!(:login => 'outro', :email => 'outro@example.com', :password => 'outro', :password_confirmation => 'outro', :environment => Environment.default)
  23 + user.activate
  24 + PushNotificationPlugin::DeviceToken.create!(:token => "firsttoken", device_name: "my device", :user => user)
  25 + get "/api/v1/push_notification_plugin/device_tokens?#{params.merge(:target_id => user.id).to_query}"
  26 + assert_equal 401, last_response.status
  27 + end
  28 +
  29 + should 'admin see other user\'s device tokens' do
  30 + logged_user = @user
  31 + Environment.default.add_admin(logged_user.person)
  32 + logged_user.reload
  33 +
  34 + user = User.create!(:login => 'outro', :email => 'outro@example.com', :password => 'outro', :password_confirmation => 'outro', :environment => Environment.default)
  35 + user.activate
  36 +
  37 + token1 = PushNotificationPlugin::DeviceToken.create!(:token => "firsttoken", device_name: "my device", :user => user)
  38 +
  39 + get "/api/v1/push_notification_plugin/device_tokens?#{params.merge(:target_id => user.id).to_query}"
  40 + json = JSON.parse(last_response.body)
  41 + assert_equivalent [token1.token], json
  42 + end
  43 +
  44 +#------------------------------------------------------------------------------------------------------
  45 +
  46 + should 'add my device token' do
  47 + params.merge!(:device_name => "my_device", :token => "token1")
  48 + post "/api/v1/push_notification_plugin/device_tokens?#{params.to_query}"
  49 + json = JSON.parse(last_response.body)
  50 + assert_equivalent ["token1"], json["user"]["device_tokens"]
  51 + end
  52 +
  53 + should 'not add device tokens for other people' do
  54 + user = User.create!(:login => 'outro', :email => 'outro@example.com', :password => 'outro', :password_confirmation => 'outro', :environment => Environment.default)
  55 + user.activate
  56 + params.merge!(:device_name => "my_device", :token => "tokenX", :target_id => user.id)
  57 + post "/api/v1/push_notification_plugin/device_tokens?#{params.to_query}"
  58 + assert_equal 401, last_response.status
  59 + end
  60 +
  61 + should 'admin add device tokens for other users' do
  62 + logged_user = @user
  63 + Environment.default.add_admin(logged_user.person)
  64 + logged_user.reload
  65 +
  66 + user = User.create!(:login => 'outro', :email => 'outro@example.com', :password => 'outro', :password_confirmation => 'outro', :environment => Environment.default)
  67 + user.activate
  68 +
  69 + params.merge!(:device_name => "my_device", :token=> "tokenY", :target_id => user.id)
  70 + post "/api/v1/push_notification_plugin/device_tokens?#{params.to_query}"
  71 +
  72 + json = JSON.parse(last_response.body)
  73 + assert_equivalent ["tokenY"], json["user"]["device_tokens"]
  74 + end
  75 +
  76 +#------------------------------------------------------------------------------------------------------
  77 +
  78 + should 'delete my device tokens' do
  79 + logged_user = @user
  80 + PushNotificationPlugin::DeviceToken.create!(:token => "firsttoken", device_name: "my device", :user => logged_user)
  81 + PushNotificationPlugin::DeviceToken.create!(:token => "secondtoken", device_name: "my device", :user => logged_user)
  82 +
  83 + params.merge!(:token => "secondtoken")
  84 + delete "/api/v1/push_notification_plugin/device_tokens?#{params.to_query}"
  85 + json = JSON.parse(last_response.body)
  86 + assert_equivalent ["firsttoken"], json["user"]["device_tokens"]
  87 + end
  88 +
  89 + should 'not delete device tokens for other people' do
  90 + user = User.create!(:login => 'outro', :email => 'outro@example.com', :password => 'outro', :password_confirmation => 'outro', :environment => Environment.default)
  91 + user.activate
  92 +
  93 + PushNotificationPlugin::DeviceToken.create!(:token => "secondtoken", device_name: "my device", :user => user)
  94 + user.reload
  95 +
  96 + params.merge!(:token => "secondtoken", :target_id => user.id)
  97 + delete "/api/v1/push_notification_plugin/device_tokens?#{params.to_query}"
  98 + assert_equal 401, last_response.status
  99 + assert_equivalent user.device_token_list, ["secondtoken"]
  100 + end
  101 +
  102 + should 'admin delete device tokens for other users' do
  103 + logged_user = @user
  104 + Environment.default.add_admin(logged_user.person)
  105 + logged_user.reload
  106 +
  107 + user = User.create!(:login => 'outro', :email => 'outro@example.com', :password => 'outro', :password_confirmation => 'outro', :environment => Environment.default)
  108 + user.activate
  109 +
  110 + PushNotificationPlugin::DeviceToken.create!(:token => "firsttoken", device_name: "my device", :user => user)
  111 + PushNotificationPlugin::DeviceToken.create!(:token => "secondtoken", device_name: "my device", :user => user)
  112 + user.reload
  113 +
  114 + params.merge!(:token=> "secondtoken", :target_id => user.id)
  115 + delete "/api/v1/push_notification_plugin/device_tokens?#{params.to_query}"
  116 +
  117 + json = JSON.parse(last_response.body)
  118 + assert_equivalent ["firsttoken"], json["user"]["device_tokens"]
  119 + end
  120 +
  121 +#--------------------------------------------------------------------------------------------------------------------------------------------
  122 +
  123 + should 'list all notifications disabled by default for new users' do
  124 + get "/api/v1/push_notification_plugin/notification_settings?#{params.to_query}"
  125 + json = JSON.parse(last_response.body)
  126 + json["user"]["notification_settings"].each_pair do |notification, status|
  127 + refute status
  128 + end
  129 + end
  130 +
  131 + should 'list device tokens notification options' do
  132 + logged_user = @user
  133 + logged_user.notification_settings.activate_notification "new_comment"
  134 + logged_user.save!
  135 +
  136 + get "/api/v1/push_notification_plugin/notification_settings?#{params.to_query}"
  137 + json = JSON.parse(last_response.body)
  138 + assert_equal true, json["user"]["notification_settings"]["new_comment"]
  139 + assert_equal false, json["user"]["notification_settings"]["add_friend"]
  140 + end
  141 +
  142 + should 'get possible notifications' do
  143 + get "/api/v1/push_notification_plugin/possible_notifications?#{params.to_query}"
  144 + json = JSON.parse(last_response.body)
  145 + assert_equivalent PushNotificationPlugin::NotificationSettings::NOTIFICATIONS.keys, json["possible_notifications"]
  146 + end
  147 +
  148 + should 'change device tokens notification options' do
  149 + logged_user = @user
  150 + params.merge!("new_comment"=> "true")
  151 +
  152 + post "/api/v1/push_notification_plugin/notification_settings?#{params.to_query}"
  153 + logged_user.reload
  154 + json = JSON.parse(last_response.body)
  155 + assert_equal true, json["user"]["notification_settings"]["new_comment"]
  156 + assert_equal true, logged_user.notification_settings.hash_flags["new_comment"]
  157 + end
  158 +
  159 + should 'get active notifications list' do
  160 + logged_user = @user
  161 + logged_user.notification_settings.activate_notification "new_comment"
  162 + logged_user.save!
  163 +
  164 + get "/api/v1/push_notification_plugin/active_notifications?#{params.to_query}"
  165 + json = JSON.parse(last_response.body)
  166 + assert_equivalent ["new_comment"], json
  167 + end
  168 +
  169 + should 'get inactive notifications list' do
  170 + logged_user = @user
  171 + logged_user.notification_settings.activate_notification "new_comment"
  172 + logged_user.save
  173 +
  174 + get "/api/v1/push_notification_plugin/inactive_notifications?#{params.to_query}"
  175 + json = JSON.parse(last_response.body)
  176 + assert_equivalent (PushNotificationPlugin::NotificationSettings::NOTIFICATIONS.keys-["new_comment"]), json
  177 + end
  178 +end
... ...
plugins/push_notification/test/unit/notification_settings_test.rb 0 → 100644
... ... @@ -0,0 +1,102 @@
  1 +require_relative '../../lib/push_notification_helper.rb'
  2 +require 'test_helper'
  3 +
  4 +class NotificationSettingsTest < ActiveSupport::TestCase
  5 + include PushNotificationHelper
  6 +
  7 + should 'list notifications options in a hash' do
  8 + user = User.create!(:login => 'homer', :email => 'homer@example.com', :password => 'beer', :password_confirmation => 'beer', :environment => Environment.default)
  9 + user.activate
  10 + settings = PushNotificationPlugin::NotificationSettings.create!(:user => user)
  11 + user.reload
  12 + assert_equivalent PushNotificationPlugin::NotificationSettings::NOTIFICATIONS.keys, settings.hash_flags.keys
  13 +
  14 + settings.hash_flags.each_pair do |notification, status|
  15 + assert !!status == status
  16 + end
  17 + end
  18 +
  19 + should 'all notifications be disabled by default for new settingss' do
  20 + user = User.create!(:login => 'outro', :email => 'outro@example.com', :password => 'outro', :password_confirmation => 'outro', :environment => Environment.default)
  21 + user.activate
  22 + settings = PushNotificationPlugin::NotificationSettings.create!(:user => user)
  23 + settings.hash_flags.each_pair do |notification, status|
  24 + refute status
  25 + end
  26 + end
  27 +
  28 + should 'activate a notification for a settings' do
  29 + user = User.create!(:login => 'homer', :email => 'homer@example.com', :password => 'beer', :password_confirmation => 'beer', :environment => Environment.default)
  30 + user.activate
  31 + settings = PushNotificationPlugin::NotificationSettings.create!(:user => user)
  32 + settings.activate_notification "new_comment"
  33 + settings.save!
  34 +
  35 + assert_equal true, settings.hash_flags["new_comment"]
  36 + end
  37 +
  38 + should 'deactivate a notification for a settings' do
  39 + user = User.create!(:login => 'homer', :email => 'homer@example.com', :password => 'beer', :password_confirmation => 'beer', :environment => Environment.default)
  40 + user.activate
  41 + settings = PushNotificationPlugin::NotificationSettings.create!(:user => user)
  42 +
  43 + settings.activate_notification "new_comment"
  44 + settings.save!
  45 + assert_equal true, settings.hash_flags["new_comment"]
  46 +
  47 + settings.deactivate_notification "new_comment"
  48 + settings.save!
  49 + assert_equal false, settings.hash_flags["new_comment"]
  50 + end
  51 +
  52 + should 'set notification to specific state' do
  53 + user = User.create!(:login => 'homer', :email => 'homer@example.com', :password => 'beer', :password_confirmation => 'beer', :environment => Environment.default)
  54 + user.activate
  55 + settings = PushNotificationPlugin::NotificationSettings.new(:user => user)
  56 +
  57 + settings.set_notification_state "new_comment", 1
  58 + settings.save!
  59 + assert_equal true, settings.hash_flags["new_comment"]
  60 +
  61 + settings.set_notification_state "new_comment", 0
  62 + settings.save!
  63 + assert_equal false, settings.hash_flags["new_comment"]
  64 + end
  65 +
  66 + should 'check if notification is active' do
  67 + user = User.create!(:login => 'homer', :email => 'homer@example.com', :password => 'beer', :password_confirmation => 'beer', :environment => Environment.default)
  68 + user.activate
  69 + settings = PushNotificationPlugin::NotificationSettings.create!(:user => user)
  70 +
  71 + settings.activate_notification "new_comment"
  72 + settings.save!
  73 +
  74 + assert_equal true, settings.active?("new_comment")
  75 + end
  76 +
  77 + should 'list active notifications' do
  78 + user = User.create!(:login => 'homer', :email => 'homer@example.com', :password => 'beer', :password_confirmation => 'beer', :environment => Environment.default)
  79 + user.activate
  80 + settings = PushNotificationPlugin::NotificationSettings.create!(:user => user)
  81 +
  82 + settings.activate_notification "new_comment"
  83 + settings.save!
  84 + assert_equivalent ["new_comment"], settings.active_notifications
  85 +
  86 + settings.activate_notification "add_friend"
  87 + settings.save!
  88 + assert_equivalent ["new_comment","add_friend"], settings.active_notifications
  89 + end
  90 +
  91 + should 'list inactive notifications' do
  92 + user = User.create!(:login => 'homer', :email => 'homer@example.com', :password => 'beer', :password_confirmation => 'beer', :environment => Environment.default)
  93 + user.activate
  94 + settings = PushNotificationPlugin::NotificationSettings.create!(:user => user)
  95 +
  96 + assert_equivalent PushNotificationPlugin::NotificationSettings::NOTIFICATIONS.keys, settings.inactive_notifications
  97 +
  98 + settings.activate_notification "new_comment"
  99 + settings.save!
  100 + assert_equivalent (PushNotificationPlugin::NotificationSettings::NOTIFICATIONS.keys-["new_comment"]), settings.inactive_notifications
  101 + end
  102 +end
... ...
plugins/push_notification/test/unit/observers_test.rb 0 → 100644
... ... @@ -0,0 +1,86 @@
  1 +require 'test_helper'
  2 +require_relative '../helpers/observers_test_helper'
  3 +
  4 +class ObserversTest < ActiveSupport::TestCase
  5 + include ObserversTestHelper
  6 +
  7 + def setup
  8 + environment = Environment.default
  9 + environment.enable_plugin(PushNotificationPlugin)
  10 + end
  11 +
  12 + should 'send notification when creating a comment' do
  13 + PushNotificationPlugin.any_instance.expects(:send_to_users)
  14 + person = fast_create(Person)
  15 + article = fast_create(Article, :profile_id => person.id)
  16 + Comment.create!(:author => person, :title => 'test comment', :body => 'body!', :source => article)
  17 + end
  18 +
  19 + should 'send notification when adding a friend' do
  20 + PushNotificationPlugin.any_instance.expects(:send_to_users)
  21 + create_add_friend_task
  22 + end
  23 +
  24 + should 'send notification when friendship is accepted' do
  25 + PushNotificationPlugin.any_instance.expects(:send_to_users).twice
  26 + create_add_friend_task.finish
  27 + end
  28 +
  29 + should 'send notification when friendship is refused' do
  30 + PushNotificationPlugin.any_instance.expects(:send_to_users).twice
  31 + create_add_friend_task.cancel
  32 + end
  33 +
  34 + should 'send notification when adding a member to a community' do
  35 + PushNotificationPlugin.any_instance.expects(:send_to_users)
  36 + create_add_member_task
  37 + end
  38 +
  39 + should 'send notification when accepting a member in a community' do
  40 + PushNotificationPlugin.any_instance.expects(:send_to_users).twice
  41 + create_add_member_task.finish
  42 + end
  43 +
  44 + should 'send notification when rejecting a member in a community' do
  45 + PushNotificationPlugin.any_instance.expects(:send_to_users).twice
  46 + create_add_member_task.cancel
  47 + end
  48 +
  49 + should 'send notification when suggesting an article' do
  50 + PushNotificationPlugin.any_instance.expects(:send_to_users).twice
  51 + create_suggest_article_task
  52 + end
  53 +
  54 + should 'send notification when accepting suggested article' do
  55 + PushNotificationPlugin.any_instance.expects(:send_to_users).times(5)
  56 + create_suggest_article_task.finish
  57 + end
  58 +
  59 + should 'send notification when rejecting suggested article' do
  60 + PushNotificationPlugin.any_instance.expects(:send_to_users).times(4)
  61 + create_suggest_article_task.cancel
  62 + end
  63 +
  64 + should 'send notification when an article needs to be approved' do
  65 + PushNotificationPlugin.any_instance.expects(:send_to_users)
  66 + create_approve_article_task
  67 + end
  68 +
  69 + should 'send notification when an article is approved' do
  70 + PushNotificationPlugin.any_instance.expects(:send_to_users).times(3)
  71 + create_approve_article_task.finish
  72 + end
  73 +
  74 + should 'send notification when an article is not approved' do
  75 + PushNotificationPlugin.any_instance.expects(:send_to_users).times(2)
  76 + create_approve_article_task.cancel
  77 + end
  78 +
  79 + should 'send notification when an article is created' do
  80 + PushNotificationPlugin.any_instance.expects(:send_to_users).twice
  81 + community = fast_create(Community)
  82 + person = fast_create(Person)
  83 + Article.create!(:name => 'great article', :profile => community)
  84 + Article.create!(:name => 'great article', :profile => person)
  85 + end
  86 +end
... ...
plugins/push_notification/test/unit/push_notification_helper_test.rb 0 → 100644
... ... @@ -0,0 +1,38 @@
  1 +require_relative '../../lib/push_notification_helper.rb'
  2 +require 'test_helper'
  3 +
  4 +class PushNotificationHelperTest < ActiveSupport::TestCase
  5 + include PushNotificationHelper
  6 +
  7 + should 'get all tokens for a group of users' do
  8 + user = User.create!(:login => 'homer', :email => 'homer@example.com', :password => 'beer', :password_confirmation => 'beer', :environment => Environment.default)
  9 + user.activate
  10 + PushNotificationPlugin::DeviceToken.create!(:token => "tokenHomer1", device_name: "my device", :user => user)
  11 + PushNotificationPlugin::DeviceToken.create!(:token => "tokenHomer2", device_name: "my device", :user => user)
  12 +
  13 + user2 = User.create!(:login => 'bart', :email => 'bart@example.com', :password => 'fart', :password_confirmation => 'fart', :environment => Environment.default)
  14 + user2.activate
  15 + PushNotificationPlugin::DeviceToken.create!(:token => "tokenBart1", device_name: "my device", :user => user2)
  16 + PushNotificationPlugin::DeviceToken.create!(:token => "tokenBart2", device_name: "my device", :user => user2)
  17 + PushNotificationPlugin::DeviceToken.create!(:token => "tokenBart3", device_name: "my device", :user => user2)
  18 +
  19 + tokens = tokens_for_users([user,user2])
  20 +
  21 + assert_equivalent ["tokenHomer1","tokenHomer2","tokenBart1","tokenBart2","tokenBart3"], tokens
  22 + end
  23 +
  24 + should 'filter users registered for a notification' do
  25 + user = User.create!(:login => 'homer', :email => 'homer@example.com', :password => 'beer', :password_confirmation => 'beer', :environment => Environment.default)
  26 + user.activate
  27 + user2 = User.create!(:login => 'bart', :email => 'bart@example.com', :password => 'fart', :password_confirmation => 'fart', :environment => Environment.default)
  28 + user2.activate
  29 +
  30 + user.notification_settings.activate_notification "new_comment"
  31 + user.save!
  32 +
  33 + users = filter_users_for_flag("new_comment",[user,user2])
  34 +
  35 + assert_equivalent [user], users
  36 + end
  37 +
  38 +end
... ...
plugins/push_notification/test/unit/push_notification_plugin_test.rb 0 → 100644
... ... @@ -0,0 +1,68 @@
  1 +require_relative '../../lib/push_notification_helper.rb'
  2 +require 'test_helper'
  3 +
  4 +class PushNotificationPluginTest < ActiveSupport::TestCase
  5 + include PushNotificationHelper
  6 +
  7 + def setup
  8 + environment = Environment.default
  9 + environment.enable_plugin(PushNotificationPlugin)
  10 + end
  11 +
  12 + should 'subscribe and unsubscribe to notification' do
  13 + class AnyClass
  14 + def self.push_notification_new_comment_additional_users
  15 + ['YO']
  16 + end
  17 + end
  18 +
  19 + assert PushNotificationPlugin::subscribe(Environment.default, "new_comment", AnyClass)
  20 + assert_equivalent PushNotificationPlugin::subscribers(Environment.default, "new_comment"), [AnyClass.name.constantize]
  21 + assert PushNotificationPlugin::unsubscribe(Environment.default, "new_comment", AnyClass)
  22 + assert_empty PushNotificationPlugin::subscribers(Environment.default, "new_comment")
  23 + end
  24 +
  25 + should 'get additional users from subscribers' do
  26 + class AnyClass
  27 + def self.push_notification_new_comment_additional_users
  28 + ['YO']
  29 + end
  30 + end
  31 +
  32 + PushNotificationPlugin::subscribe(Environment.default, "new_comment", AnyClass)
  33 + AnyClass.expects(:push_notification_new_comment_additional_users).returns(['YO'])
  34 + subscribers_additional_users("new_comment", Environment.default)
  35 + end
  36 +
  37 + should 'return nill for unknown notification subscription methods' do
  38 + class AnyEventCallbackClass
  39 + def self.push_notification_any_event_additional_users
  40 + ['YO']
  41 + end
  42 + end
  43 +
  44 + assert_nil PushNotificationPlugin::subscribe(Environment.default, "any_event", AnyEventCallbackClass)
  45 + assert_nil PushNotificationPlugin::unsubscribe(Environment.default, "any_event", AnyEventCallbackClass)
  46 + assert_nil PushNotificationPlugin::subscribers(Environment.default, "any_event")
  47 + end
  48 +
  49 + should 'return empty list for known notification without subscribers' do
  50 + class CommentCallbackClass
  51 + def self.push_notification_new_comment_additional_users
  52 + ['YO']
  53 + end
  54 + end
  55 +
  56 + refute PushNotificationPlugin::unsubscribe(Environment.default, "new_comment", CommentCallbackClass)
  57 + assert_empty PushNotificationPlugin::subscribers(Environment.default, "new_comment")
  58 + end
  59 +
  60 + should 'not subscribe to notification if correspondent method callback is not implemented' do
  61 + class NoCallbackClass
  62 + end
  63 +
  64 + assert_nil PushNotificationPlugin::subscribe(Environment.default, "new_comment", NoCallbackClass)
  65 + assert_empty PushNotificationPlugin::subscribers(Environment.default, "new_comment")
  66 + end
  67 +
  68 +end
... ...
plugins/push_notification/views/_notification_events_form.html.erb 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +<table>
  2 + <tr>
  3 + <th><%= t("push_notification_plugin.controllers.myprofile.event") %> </th>
  4 + <th><%= t("push_notification_plugin.controllers.myprofile.enabled") %> </th>
  5 + </tr>
  6 +
  7 + <% settings.each do |event, checked|%>
  8 + <tr>
  9 + <td><%= t("push_notification_plugin.events.#{event}") %></td>
  10 + <td>
  11 + <%= hidden_field_tag "settings[#{event}]", "0" %>
  12 + <%= check_box_tag "settings[#{event}]", "1", (checked == "1" || checked == true) %>
  13 + </td>
  14 + </tr>
  15 + <% end %>
  16 +
  17 +</table>
... ...
plugins/push_notification/views/push_notification_plugin_admin/_notification_events_form.html.erb 0 → 120000
... ... @@ -0,0 +1 @@
  1 +../_notification_events_form.html.erb
0 2 \ No newline at end of file
... ...
plugins/push_notification/views/push_notification_plugin_admin/index.html.erb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +<h1><%= t("push_notification_plugin.lib.plugin.settings")%></h1>
  2 +
  3 +<%= form_for(:server_settings, :url => {:action => 'update'}) do |f| %>
  4 +
  5 + <%= labelled_form_field t('push_notification_plugin.controllers.admin.server_api_key'), f.text_field(:server_api_key, :size => "80") %>
  6 +
  7 + <h3><%= t('push_notification_plugin.lib.plugin.enabled_notifications')%></h3>
  8 + <%= render partial: "notification_events_form", locals:{f: f, settings: @settings} %>
  9 +
  10 + <% button_bar do %>
  11 + <%= submit_button(:save, c_('Save'), :cancel => {:controller => 'plugins'}) %>
  12 + <% end %>
  13 +
  14 +<% end %>
... ...
plugins/push_notification/views/push_notification_plugin_myprofile/_notification_events_form.html.erb 0 → 120000
... ... @@ -0,0 +1 @@
  1 +../_notification_events_form.html.erb
0 2 \ No newline at end of file
... ...
plugins/push_notification/views/push_notification_plugin_myprofile/index.html.erb 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +<h1><%= t("push_notification_plugin.controllers.myprofile.my_devices") %></h1>
  2 + <% if @devices.blank? %>
  3 + <div><%= t("push_notification_plugin.controllers.myprofile.no_devices")%></div>
  4 + <% else %>
  5 + <table>
  6 + <tr>
  7 + <th><%= t("push_notification_plugin.lib.plugin.device")%></th>
  8 + <th><%= t("push_notification_plugin.lib.plugin.actions") %></th>
  9 + </tr>
  10 + <% @devices.each do |d| %>
  11 + <tr>
  12 + <td><%= d.device_name %></td>
  13 + <td><%= button_without_text(:delete, t('push_notification_plugin.controllers.myprofile.delete_device'), {:controller => "push_notification_plugin_myprofile", :action => "delete_device", :params => {:device => d}}, method: :delete, id: "delete_device_#{d.id}") %></td>
  14 + </tr>
  15 + <% end %>
  16 + </table>
  17 + <% end %>
  18 +<%= form_for(:settings, :url => {:controller => "push_notification_plugin_myprofile", :action => "update_settings"}) do |f| %>
  19 + <h1><%= t('push_notification_plugin.lib.plugin.enabled_notifications') %></h1>
  20 + <%= render partial: "notification_events_form", locals: {f: f, settings: @settings} %>
  21 + <% button_bar do %>
  22 + <%= submit_button(:save, _('Save')) %>
  23 + <%= button(:back, _('Back to control panel'), :controller => (profile.nil? ? 'admin_panel': 'profile_editor')) %>
  24 + <% end %>
  25 +<% end %>
... ...