diff --git a/app/models/article.rb b/app/models/article.rb index bdb304d..284fbac 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -8,7 +8,8 @@ class Article < ActiveRecord::Base :accept_comments, :feed, :published, :source, :source_name, :highlighted, :notify_comments, :display_hits, :slug, :external_feed_builder, :display_versions, :external_link, - :image_builder, :show_to_followers + :image_builder, :show_to_followers, + :author acts_as_having_image diff --git a/app/models/uploaded_file.rb b/app/models/uploaded_file.rb index dcb0008..8f307ec 100644 --- a/app/models/uploaded_file.rb +++ b/app/models/uploaded_file.rb @@ -163,4 +163,8 @@ class UploadedFile < Article true end + def notifiable? + true + end + end diff --git a/lib/tasks/plugins_tests.rake b/lib/tasks/plugins_tests.rake index f5201c5..39e29c2 100644 --- a/lib/tasks/plugins_tests.rake +++ b/lib/tasks/plugins_tests.rake @@ -169,7 +169,7 @@ def test_sequence(plugins, tasks) failed[plugin] << task end end - disable_plugins(plugin) + disable_plugins end fail_flag = false failed.each do |plugin, tasks| diff --git a/plugins/open_graph/Gemfile b/plugins/open_graph/Gemfile new file mode 100644 index 0000000..c434a07 --- /dev/null +++ b/plugins/open_graph/Gemfile @@ -0,0 +1,3 @@ + +gem 'jsonify-rails' + diff --git a/plugins/open_graph/controllers/myprofile/open_graph_plugin/myprofile_controller.rb b/plugins/open_graph/controllers/myprofile/open_graph_plugin/myprofile_controller.rb new file mode 100644 index 0000000..5603be9 --- /dev/null +++ b/plugins/open_graph/controllers/myprofile/open_graph_plugin/myprofile_controller.rb @@ -0,0 +1,47 @@ +class OpenGraphPlugin::MyprofileController < MyProfileController + + protect 'edit_profile', :profile + before_filter :set_context + + def enterprise_search + scope = environment.enterprises.enabled.public + profile_search scope + end + def community_search + scope = environment.communities.public + profile_search scope + end + def friend_search + scope = profile.friends + profile_search scope + end + + def track_config + profile.update_attributes! params[:profile_data] + render partial: 'track_form', locals: {context: context, reload: true} + end + + protected + + def profile_search scope + @query = params[:query] + @profiles = scope.limit(10).order('name ASC'). + where(['name ILIKE ? OR name ILIKE ? OR identifier LIKE ?', "#{@query}%", "% #{@query}%", "#{@query}%"]) + render partial: 'profile_search', locals: {profiles: @profiles} + end + + def context + :open_graph + end + + def set_context + OpenGraphPlugin.context = self.context + end + + def default_url_options + # avoid rails' use_relative_controller! + {use_route: '/'} + end + +end + diff --git a/plugins/open_graph/db/migrate/20141031130250_create_open_graph_plugin_tracks.rb b/plugins/open_graph/db/migrate/20141031130250_create_open_graph_plugin_tracks.rb new file mode 100644 index 0000000..2b5d691 --- /dev/null +++ b/plugins/open_graph/db/migrate/20141031130250_create_open_graph_plugin_tracks.rb @@ -0,0 +1,36 @@ +class CreateOpenGraphPluginTracks < ActiveRecord::Migration + def up + create_table :open_graph_plugin_tracks do |t| + t.string :type + t.string :context + t.boolean :enabled, default: true + + t.integer :tracker_id + + t.integer :actor_id + + t.string :action + + t.string :object_type + t.text :object_data_url + t.integer :object_data_id + t.string :object_data_type + + t.timestamps + end + + add_index :open_graph_plugin_tracks, [:type] + add_index :open_graph_plugin_tracks, [:context] + add_index :open_graph_plugin_tracks, [:type, :context] + add_index :open_graph_plugin_tracks, [:actor_id] + add_index :open_graph_plugin_tracks, [:action] + add_index :open_graph_plugin_tracks, [:object_type] + add_index :open_graph_plugin_tracks, [:enabled] + add_index :open_graph_plugin_tracks, [:object_data_url] + add_index :open_graph_plugin_tracks, [:object_data_id, :object_data_type], name: 'index_open_graph_plugin_tracks_object_data_id_type' + end + + def down + drop_table :open_graph_plugin_tracks + end +end diff --git a/plugins/open_graph/install.rb b/plugins/open_graph/install.rb new file mode 100644 index 0000000..cfba09e --- /dev/null +++ b/plugins/open_graph/install.rb @@ -0,0 +1,2 @@ +system "script/noosfero-plugins -q enable metadata" + diff --git a/plugins/open_graph/lib/ext/article.rb b/plugins/open_graph/lib/ext/article.rb new file mode 100644 index 0000000..1fa76fa --- /dev/null +++ b/plugins/open_graph/lib/ext/article.rb @@ -0,0 +1,15 @@ +require_dependency 'article' + +class Article + + after_update :open_graph_scrape + + protected + + def open_graph_scrape + activity = OpenGraphPlugin::Activity.where(object_data_id: self.id, object_data_type: self.class.base_class.name).first + activity.scrape if activity + end + handle_asynchronously :open_graph_scrape + +end diff --git a/plugins/open_graph/lib/ext/profile.rb b/plugins/open_graph/lib/ext/profile.rb new file mode 100644 index 0000000..09e144f --- /dev/null +++ b/plugins/open_graph/lib/ext/profile.rb @@ -0,0 +1,59 @@ +require_dependency 'profile' +# hate to wrte this, but without Noosfero::Plugin::Settings is loaded instead +require 'open_graph_plugin/settings' + +# attr_accessible must be defined on subclasses +Profile.descendants.each do |subclass| + subclass.class_eval do + attr_accessible :open_graph_settings + + OpenGraphPlugin::TrackConfig::Types.each do |track, klass| + klass = "OpenGraphPlugin::#{klass}".constantize + attributes = "#{klass.association}_attributes" + profile_ids = "open_graph_#{track}_profiles_ids" + + attr_accessible attributes + attr_accessible profile_ids + end + end +end + +class Profile + + def open_graph_settings attrs = {} + @open_graph_settings ||= OpenGraphPlugin::Settings.new self, attrs + attrs.each{ |a, v| @open_graph_settings.send "#{a}=", v } + @open_graph_settings + end + alias_method :open_graph_settings=, :open_graph_settings + + has_many :open_graph_tracks, class_name: 'OpenGraphPlugin::Track', source: :tracker_id, foreign_key: :tracker_id + + has_many :open_graph_activities, class_name: 'OpenGraphPlugin::Activity', source: :tracker_id, foreign_key: :tracker_id + + has_many :open_graph_track_configs, class_name: 'OpenGraphPlugin::TrackConfig', source: :tracker_id, foreign_key: :tracker_id + OpenGraphPlugin::TrackConfig::Types.each do |track, klass| + klass = "OpenGraphPlugin::#{klass}".constantize + association = klass.association + profile_ids = "open_graph_#{track}_profiles_ids" + + has_many association, class_name: klass.name, foreign_key: :tracker_id + accepts_nested_attributes_for association, allow_destroy: true, reject_if: :open_graph_reject_empty_object_type + + define_method "#{profile_ids}=" do |ids| + cids = self.send(association).order('created_at ASC').map(&:object_data_id) + nids = if ids.is_a? Array then ids else ids.split ',' end + nids = nids.map(&:to_i) + Profile.where(id: nids-cids).each{ |profile| self.send(association).create! type: klass.name, object_data: profile } + self.send(association).each{ |c| c.destroy unless c.object_data_id.in? nids } + end + + end + + define_method :open_graph_reject_empty_object_type do |attributes| + exists = attributes[:id].present? + empty = attributes[:object_type].empty? + attributes.merge! _destroy: 1 if exists and empty + return (!exists and empty) + end +end diff --git a/plugins/open_graph/lib/ext/profile_activity.rb b/plugins/open_graph/lib/ext/profile_activity.rb new file mode 100644 index 0000000..498ebeb --- /dev/null +++ b/plugins/open_graph/lib/ext/profile_activity.rb @@ -0,0 +1,19 @@ +require_dependency 'profile_activity' + +class ProfileActivity + + # update happens with grouped ActionTracker + after_save :open_graph_publish + + def open_graph_publish + # Scrap not yet supported + if self.activity.is_a? ActionTracker::Record + verb = self.activity.verb.to_sym + return unless object = self.activity.target + return unless stories = OpenGraphPlugin::Stories::TrackerStories[verb] + OpenGraphPlugin::Stories.publish object, stories + end + end + +end + diff --git a/plugins/open_graph/lib/ext/uploaded_file.rb b/plugins/open_graph/lib/ext/uploaded_file.rb new file mode 100644 index 0000000..bc79a1a --- /dev/null +++ b/plugins/open_graph/lib/ext/uploaded_file.rb @@ -0,0 +1,8 @@ +require_dependency 'uploaded_file' + +class UploadedFile + + extend OpenGraphPlugin::AttachStories::ClassMethods + open_graph_attach_stories only: :add_an_image + +end diff --git a/plugins/open_graph/lib/open_graph_plugin.rb b/plugins/open_graph/lib/open_graph_plugin.rb new file mode 100644 index 0000000..7c239f1 --- /dev/null +++ b/plugins/open_graph/lib/open_graph_plugin.rb @@ -0,0 +1,21 @@ +module OpenGraphPlugin + + extend Noosfero::Plugin::ParentMethods + + def self.plugin_name + I18n.t 'open_graph_plugin.lib.plugin.name' + end + + def self.plugin_description + I18n.t 'open_graph_plugin.lib.plugin.description' + end + + def self.context + Thread.current[:open_graph_context] || :open_graph + end + def self.context= value + Thread.current[:open_graph_context] = value + end + +end + diff --git a/plugins/open_graph/lib/open_graph_plugin/attach_stories.rb b/plugins/open_graph/lib/open_graph_plugin/attach_stories.rb new file mode 100644 index 0000000..7a84ca5 --- /dev/null +++ b/plugins/open_graph/lib/open_graph_plugin/attach_stories.rb @@ -0,0 +1,44 @@ +require_dependency 'open_graph_plugin/stories' + +# This is used when ActionTracker is not compartible with the way +module OpenGraphPlugin::AttachStories + + module ClassMethods + + def open_graph_attach_stories options={} + if stories = Array(options[:only]) + callbacks = {} + stories.each do |story| + defs = OpenGraphPlugin::Stories::Definitions[story] + Array(defs[:on]).each do |on| + callbacks[on] ||= [] + callbacks[on] << story + end + end + else + klass = self.name + callbacks = OpenGraphPlugin::Stories::ModelStories[klass.to_sym] + return if callbacks.blank? + end + + callbacks.each do |on, stories| + # subclasses may override this, but the callback is called only once + method = "open_graph_publish_after_#{on}" + + self.send "after_#{on}", method + # buggy with rails 3.2 + #self.send "after_commit", method, on: on + + define_method method do + OpenGraphPlugin::Stories.publish self, stories + end + end + end + + end + + module InstanceMethods + + end + +end diff --git a/plugins/open_graph/lib/open_graph_plugin/base.rb b/plugins/open_graph/lib/open_graph_plugin/base.rb new file mode 100644 index 0000000..2ac7145 --- /dev/null +++ b/plugins/open_graph/lib/open_graph_plugin/base.rb @@ -0,0 +1,15 @@ + +class OpenGraphPlugin::Base < Noosfero::Plugin + + def js_files + [].map{ |j| "javascripts/#{j}" } + end + + def stylesheet? + true + end + +end + +ActiveSupport.run_load_hooks :open_graph_plugin, OpenGraphPlugin + diff --git a/plugins/open_graph/lib/open_graph_plugin/display_helper.rb b/plugins/open_graph/lib/open_graph_plugin/display_helper.rb new file mode 100644 index 0000000..c3b24f8 --- /dev/null +++ b/plugins/open_graph/lib/open_graph_plugin/display_helper.rb @@ -0,0 +1,7 @@ + +module OpenGraphPlugin::DisplayHelper + + def blah + puts 'here' + end +end diff --git a/plugins/open_graph/lib/open_graph_plugin/publisher.rb b/plugins/open_graph/lib/open_graph_plugin/publisher.rb new file mode 100644 index 0000000..ebf19f1 --- /dev/null +++ b/plugins/open_graph/lib/open_graph_plugin/publisher.rb @@ -0,0 +1,166 @@ + +class OpenGraphPlugin::Publisher + + attr_accessor :actions + attr_accessor :objects + + def self.default + @default ||= self.new + end + + def initialize attributes = {} + # defaults + self.actions = OpenGraphPlugin::Stories::DefaultActions + self.objects = OpenGraphPlugin::Stories::DefaultObjects + + attributes.each do |attr, value| + self.send "#{attr}=", value + end + end + + def publish actor, story_defs, object_data_url + raise 'abstract method called' + end + + def publish_stories object_data, actor, stories + stories.each do |story| + begin + self.publish_story object_data, actor, story + rescue => e + ExceptionNotifier.notify_exception e + end + end + end + + def update_delay + 1.day + end + + # only publish recent objects to avoid multiple publications + def recent_publish? actor, object_type, object_data_url + activity_params = {actor_id: actor.id, object_type: object_type, object_data_url: object_data_url} + activity = OpenGraphPlugin::Activity.where(activity_params).first + activity.present? and activity.created_at <= self.update_delay.from_now + end + + def publish_story object_data, actor, story + OpenGraphPlugin.context = self.context + defs = OpenGraphPlugin::Stories::Definitions[story] + passive = defs[:passive] + + print_debug "open_graph: publish_story #{story}" if debug? actor + match_criteria = if (ret = self.call defs[:criteria], object_data, actor).nil? then true else ret end + return unless match_criteria + print_debug "open_graph: #{story} match criteria" if debug? actor + match_condition = if (ret = self.call defs[:publish_if], object_data, actor).nil? then true else ret end + return unless match_condition + print_debug "open_graph: #{story} match publish_if" if debug? actor + + actors = self.story_trackers defs, actor, object_data + return if actors.blank? + print_debug "open_graph: #{story} has enabled trackers" if debug? actor + + if publish = defs[:publish] + begin + instance_exec actor, object_data, &publish + rescue => e + print_debug "open_graph: can't publish story: #{e.message}" if debug? actor + ExceptionNotifier.notify_exception e + end + else + # force profile identifier for custom domains and fixed host. see og_url_for + object_profile = self.call(story_defs[:object_profile], object_data) || object_data.profile rescue nil + extra_params = if object_profile then {profile: object_profile.identifier} else {} end + + custom_object_data_url = self.call defs[:object_data_url], object_data, actor + object_data_url = if passive then self.passive_url_for object_data, custom_object_data_url, defs, extra_params else self.url_for object_data, custom_object_data_url, extra_params end + + actors.each do |actor| + print_debug "open_graph: start publishing" if debug? actor + begin + self.publish actor, defs, object_data_url + rescue => e + print_debug "open_graph: can't publish story: #{e.message}" if debug? actor + ExceptionNotifier.notify_exception e + end + end + end + end + + def story_trackers story_defs, actor, object_data + passive = story_defs[:passive] + trackers = [] + + track_configs = Array(story_defs[:track_config]).compact.map(&:constantize) + return if track_configs.empty? + print_debug "open_graph: using configs: #{track_configs.map(&:name).inspect}" if debug? actor + + if passive + object_profile = self.call(story_defs[:object_profile], object_data) || object_data.profile rescue nil + return unless object_profile + + track_configs.each do |c| + trackers.concat c.trackers_to_profile(object_profile) + end.flatten + + trackers.select! do |t| + track_configs.any?{ |c| c.enabled? self.context, t } + end + else #active + object_actor = self.call(story_defs[:object_actor], object_data) || object_data.profile rescue nil + return unless object_actor and object_actor.person? + custom_actor = self.call(story_defs[:custom_actor], object_data) + actor = custom_actor if custom_actor + + match_track = track_configs.any? do |c| + c.enabled?(self.context, actor) and + actor.send("open_graph_#{c.track_name}_track_configs").where(object_type: story_defs[:object_type]).first + end + trackers << actor if match_track + end + + trackers + end + + protected + + include MetadataPlugin::UrlHelper + + def register_publish attributes + OpenGraphPlugin::Activity.create! attributes + end + + # Call don't ask: move to a og_url method inside object + def url_for object, custom_url=nil, extra_params={} + return custom_url if custom_url.is_a? String + url = custom_url || if object.is_a? Profile then og_profile_url object else object.url end + # for profile when custom domain is used + url.merge! profile: object.profile.identifier if object.respond_to? :profile + url.merge! extra_params + self.og_url_for url + end + + def passive_url_for object, custom_url, story_defs, extra_params={} + object_type = story_defs[:object_type] + extra_params.merge! og_type: MetadataPlugin.og_types[object_type] + self.url_for object, custom_url, extra_params + end + + def call p, *args + p and instance_exec *args, &p + end + + def context + :open_graph + end + + def print_debug msg + puts msg + Delayed::Worker.logger.debug msg + end + def debug? actor=nil + !Rails.env.production? + end + +end + diff --git a/plugins/open_graph/lib/open_graph_plugin/settings.rb b/plugins/open_graph/lib/open_graph_plugin/settings.rb new file mode 100644 index 0000000..9bf7970 --- /dev/null +++ b/plugins/open_graph/lib/open_graph_plugin/settings.rb @@ -0,0 +1,14 @@ +class OpenGraphPlugin::Settings < Noosfero::Plugin::Settings + + def self.new base, attrs = {} + super base, self.parents.first, attrs + end + + OpenGraphPlugin::TrackConfig::Types.each do |track, klass| + define_method "#{track}_track_enabled=" do |value| + super ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value) + end + end + +end + diff --git a/plugins/open_graph/lib/open_graph_plugin/stories.rb b/plugins/open_graph/lib/open_graph_plugin/stories.rb new file mode 100644 index 0000000..27633d3 --- /dev/null +++ b/plugins/open_graph/lib/open_graph_plugin/stories.rb @@ -0,0 +1,302 @@ + +class OpenGraphPlugin::Stories + + class_attribute :publishers + self.publishers = [] + + def self.register_publisher publisher + self.publishers << publisher + end + + def self.publish record, stories + actor = User.current.person rescue nil + return unless actor + + self.publishers.each do |publisher| + publisher = publisher.delay unless Rails.env.development? or Rails.env.test? + publisher.publish_stories record, actor, stories + end + end + + Definitions = { + # needed a patch on UploadedFile: def notifiable?; true; end + add_a_document: { + action_tracker_verb: :create_article, + track_config: 'OpenGraphPlugin::ActivityTrackConfig', + action: :add, + object_type: :uploaded_file, + models: :UploadedFile, + on: :create, + criteria: proc do |article, actor| + article.is_a? UploadedFile + end, + publish_if: proc do |uploaded_file, actor| + # done in add_an_image + next false if uploaded_file.image? + uploaded_file.published? + end, + object_data_url: proc do |uploaded_file, actor| + uploaded_file.url.merge view: true + end, + }, + add_an_image: { + # :upload_image verb can't be used as it uses the parent Gallery as target + # hooked via open_graph_attach_stories + action_tracker_verb: nil, + track_config: 'OpenGraphPlugin::ActivityTrackConfig', + action: :add, + object_type: :gallery_image, + models: :UploadedFile, + on: :create, + criteria: proc do |article, actor| + article.is_a? UploadedFile + end, + publish_if: proc do |uploaded_file, actor| + uploaded_file.image? and uploaded_file.parent.is_a? Gallery + end, + object_data_url: proc do |uploaded_file, actor| + uploaded_file.url.merge view: true + end, + }, + create_an_article: { + action_tracker_verb: :create_article, + track_config: 'OpenGraphPlugin::ActivityTrackConfig', + action: :create, + object_type: :blog_post, + models: :Article, + on: :create, + criteria: proc do |article, actor| + article.parent.is_a? Blog + end, + publish_if: proc do |article, actor| + article.published? + end, + }, + create_an_event: { + action_tracker_verb: :create_article, + track_config: 'OpenGraphPlugin::ActivityTrackConfig', + action: :create, + object_type: :event, + models: :Event, + on: :create, + criteria: proc do |article, actor| + article.is_a? Event + end, + publish_if: proc do |event, actor| + event.published? + end, + }, + start_a_discussion: { + action_tracker_verb: :create_article, + track_config: 'OpenGraphPlugin::ActivityTrackConfig', + action: :start, + object_type: :forum, + models: :Article, + on: :create, + criteria: proc do |article, actor| + article.parent.is_a? Forum + end, + publish_if: proc do |article, actor| + article.published? + end, + object_data_url: proc do |article, actor| + url = article.url + if og_type = MetadataPlugin::og_types[:forum] + url[:og_type] = og_type + end + url + end, + }, + + # these a published as passive to give focus to the enterprise +=begin + add_a_sse_product: { + action_tracker_verb: :create_product, + track_config: 'OpenGraphPlugin::ActivityTrackConfig', + action: :announce_new, + models: :Product, + on: :create, + object_type: :product, + publish_if: proc do |product, actor| + product.profile.public? + end, + }, + update_a_sse_product: { + action_tracker_verb: :update_product, + track_config: 'OpenGraphPlugin::ActivityTrackConfig', + action: :announce_update, + object_type: :product, + models: :Product, + on: :update, + publish_if: proc do |product, actor| + product.profile.public? + end, + }, +=end + + favorite_a_sse_initiative: { + action_tracker_verb: :favorite_enterprise, + track_config: 'OpenGraphPlugin::ActivityTrackConfig', + action: :favorite, + object_type: :favorite_enterprise, + models: :FavoriteEnterprisePerson, + on: :create, + object_actor: proc do |favorite_enterprise_person| + favorite_enterprise_person.person + end, + object_profile: proc do |favorite_enterprise_person| + favorite_enterprise_person.enterprise + end, + object_data_url: proc do |favorite_enterprise_person, actor| + self.og_profile_url favorite_enterprise_person.enterprise + end, + }, + +=begin + comment_a_discussion: { + action_tracker_verb: nil, + action: :comment, + object_type: :forum, + models: :Comment, + on: :create, + criteria: proc do |comment, actor| + source, parent = comment.source, comment.source.parent + source.is_a? Article and parent.is_a? Forum + end, + publish_if: proc do |comment, actor| + comment.source.parent.published? + end, + }, + comment_an_article: { + action_tracker_verb: nil, + action: :comment, + object_type: :blog_post, + models: :Comment, + on: :create, + criteria: proc do |comment, actor| + source, parent = comment.source, comment.source.parent + source.is_a? Article and parent.is_a? Blog + end, + publish_if: proc do |comment, actor| + comment.source.parent.published? + end, + }, +=end + + make_friendship_with: { + action_tracker_verb: :new_friendship, + track_config: 'OpenGraphPlugin::ActivityTrackConfig', + action: :make_friendship, + object_type: :friend, + models: :Friendship, + on: :create, + custom_actor: proc do |friendship| + friendship.person + end, + object_actor: proc do |friendship| + friendship.person + end, + object_profile: proc do |friendship| + friendship.friend + end, + object_data_url: proc do |friendship, actor| + self.og_profile_url friendship.friend + end, + }, + + # PASSIVE STORIES + announce_news_from_a_sse_initiative: { + action_tracker_verb: :create_article, + track_config: 'OpenGraphPlugin::EnterpriseTrackConfig', + action: :announce_news, + object_type: :enterprise, + passive: true, + models: :Article, + on: :create, + criteria: proc do |article, actor| + article.profile.enterprise? + end, + publish_if: proc do |article, actor| + article.published? + end, + }, + announce_a_new_sse_product: { + action_tracker_verb: :create_product, + track_config: 'OpenGraphPlugin::EnterpriseTrackConfig', + action: :announce_new, + object_type: :product, + passive: true, + models: :Product, + on: :create, + criteria: proc do |product, actor| + product.profile.enterprise? + end, + }, + announce_an_update_of_sse_product: { + action_tracker_verb: :update_product, + track_config: 'OpenGraphPlugin::EnterpriseTrackConfig', + action: :announce_update, + object_type: :product, + passive: true, + models: :Product, + on: :update, + criteria: proc do |product, actor| + product.profile.enterprise? + end, + }, + + announce_news_from_a_community: { + action_tracker_verb: :create_article, + track_config: 'OpenGraphPlugin::CommunityTrackConfig', + action: :announce_news, + object_type: :community, + passive: true, + models: :Article, + on: :create, + criteria: proc do |article, actor| + article.profile.community? + end, + publish_if: proc do |article, actor| + article.published? + end, + }, + + } + + ValidObjectList = Definitions.map{ |story, data| data[:object_type] }.uniq + ValidActionList = Definitions.map{ |story, data| data[:action] }.uniq + + # TODO make this verification work + #raise "Each active story must use a unique object_type for configuration to work" if ValidObjectList.size < Definitions.size + + DefaultActions = ValidActionList.inject({}){ |h, a| h[a] = a; h } + DefaultObjects = ValidObjectList.inject({}){ |h, o| h[o] = o; h } + + TrackerStories = {}; Definitions.each do |story, data| + Array(data[:action_tracker_verb]).each do |verb| + next unless verb + TrackerStories[verb] ||= [] + TrackerStories[verb] << story + end + end + + TrackConfigStories = {}; Definitions.each do |story, data| + Array(data[:track_config]).each do |track_config| + next unless track_config + TrackConfigStories[track_config] ||= [] + TrackConfigStories[track_config] << [story, data] + end + end + + ModelStories = {}; Definitions.each do |story, data| + Array(data[:models]).each do |model| + ModelStories[model] ||= {} + Array(data[:on]).each do |on| + ModelStories[model][on] ||= [] + ModelStories[model][on] << story + end + end + end + +end + diff --git a/plugins/open_graph/locales/en-US.yml b/plugins/open_graph/locales/en-US.yml new file mode 100644 index 0000000..03ffec3 --- /dev/null +++ b/plugins/open_graph/locales/en-US.yml @@ -0,0 +1,43 @@ + +"en-US": &en-US + + open_graph_plugin: + lib: + plugin: + name: 'OpenGraph' + description: 'OpenGraph' + views: + track: + config: + activity: + configure: 'Configure' + label: "My activities: new photos on my albuns, blogs's posts and other contents" + objects: + blog_post: "Blogs' posts" + event: 'Creation of events' + favorite_enterprise: 'Quando eu favoritar um empreendimento' + forum: "Forum's topic posted" + friend: 'New friendships' + gallery_image: 'New images on my albuns' + uploaded_file: 'Files sent' + enterprise: + memberships: "Enterprises that I'm a member of" + favorites: "My favorite enterprises" + see_all: 'See enterprises' + label: "News from enterprises that I am a member and my favorites" + search_placeholder: "type to find enterprises" + favorites_how_to: + title: "How to add favorite Solidarity Economy initiatives" + body: "To add favorite Solidarity Economy initiatives, you should visit its page in Cirandas and click on button %{favorite_button} which is located below its logo, normally on the left side." + community: + label: "News from selected communities" + search_placeholder: "type to find communities" + friend: + label: "News from friends" + search_placeholder: "type to find friends" + +'en_US': + <<: *en-US +'en': + <<: *en-US + diff --git a/plugins/open_graph/locales/pt-BR.yml b/plugins/open_graph/locales/pt-BR.yml new file mode 100644 index 0000000..7a9f99c --- /dev/null +++ b/plugins/open_graph/locales/pt-BR.yml @@ -0,0 +1,40 @@ + +"pt-BR": &pt-BR + + open_graph_plugin: + lib: + plugin: + name: 'OpenGraph' + description: 'OpenGraph' + views: + track: + config: + activity: + configure: 'Configurar' + label: 'Publicar minhas atividades do cirandas no meu mural do facebook' + objects: + blog_post: 'Quando eu criar um conteúdo ou artigo' + event: 'Quando eu adicionar eventos' + forum: 'Quando eu criar novos tópicos de fórum' + friend: 'Quando eu fizer amizades' + gallery_image: 'Quando eu adicionar imagens nos meus albuns' + uploaded_file: 'Quando eu enviar novos documentos' + favorite_enterprise: 'Quando eu favoritar um empreendimento' + enterprise: + memberships: "Empreendimentos dos quais faço parte" + favorites: "Meus empreendimentos favoritos" + see_all: 'Ver empreendimentos' + label: "Publicar as novidades dos meus empreendimentos favoritos e daqueles que faço parte" + search_placeholder: "busque o empreendimento" + favorites_how_to: + title: "Como adicionar empreendimentos favoritos" + body: "Para adicionar empreendimentos favoritos, basta você visitar a página do empreendimento desejado e clicar no botão %{favorite_button} que fica abaixo da logo do empreendimento, geralmente à esquerda." + community: + label: "Publicar novidades das seguintes comunidades" + search_placeholder: "escolha a comunidade" + +'pt_BR': + <<: *pt-BR +'pt': + <<: *pt-BR + diff --git a/plugins/open_graph/models/open_graph_plugin/activity.rb b/plugins/open_graph/models/open_graph_plugin/activity.rb new file mode 100644 index 0000000..c311c0f --- /dev/null +++ b/plugins/open_graph/models/open_graph_plugin/activity.rb @@ -0,0 +1,8 @@ +# This is a log of activities, unlike ActivityTrack that is a configuration +class OpenGraphPlugin::Activity < OpenGraphPlugin::Track + + # subclass this to define (e.g. FbAppPlugin::Activity) + def scrape + end + +end diff --git a/plugins/open_graph/models/open_graph_plugin/activity_track_config.rb b/plugins/open_graph/models/open_graph_plugin/activity_track_config.rb new file mode 100644 index 0000000..0330aea --- /dev/null +++ b/plugins/open_graph/models/open_graph_plugin/activity_track_config.rb @@ -0,0 +1,22 @@ +class OpenGraphPlugin::ActivityTrackConfig < OpenGraphPlugin::TrackConfig + + # workaround for STI bug + self.table_name = :open_graph_plugin_tracks + + self.track_name = :activity + + Objects = OpenGraphPlugin::Stories::TrackConfigStories[self.name].map do |story, data| + data[:object_type].to_s + end.uniq + + def self.objects + Objects + end + + validates_uniqueness_of :object_type, scope: [:tracker_id] + validates_inclusion_of :object_type, in: self.objects + + protected + +end + diff --git a/plugins/open_graph/models/open_graph_plugin/community_track_config.rb b/plugins/open_graph/models/open_graph_plugin/community_track_config.rb new file mode 100644 index 0000000..b0e532d --- /dev/null +++ b/plugins/open_graph/models/open_graph_plugin/community_track_config.rb @@ -0,0 +1,8 @@ +class OpenGraphPlugin::CommunityTrackConfig < OpenGraphPlugin::TrackConfig + + # workaround for STI bug + self.table_name = :open_graph_plugin_tracks + + self.track_name = :community + +end diff --git a/plugins/open_graph/models/open_graph_plugin/enterprise_track_config.rb b/plugins/open_graph/models/open_graph_plugin/enterprise_track_config.rb new file mode 100644 index 0000000..3138559 --- /dev/null +++ b/plugins/open_graph/models/open_graph_plugin/enterprise_track_config.rb @@ -0,0 +1,20 @@ +class OpenGraphPlugin::EnterpriseTrackConfig < OpenGraphPlugin::TrackConfig + + # workaround for STI bug + self.table_name = :open_graph_plugin_tracks + + self.track_name = :enterprise + + self.static_trackers = true + + def self.trackers_to_profile enterprise + trackers = enterprise.members.to_set + trackers.merge enterprise.fans if enterprise.respond_to? :fans + trackers.to_a + end + + def self.profile_track_objects profile + (profile.enterprises.public.enabled + profile.favorite_enterprises.public.enabled).uniq + end + +end diff --git a/plugins/open_graph/models/open_graph_plugin/friend_track_config.rb b/plugins/open_graph/models/open_graph_plugin/friend_track_config.rb new file mode 100644 index 0000000..5602a1c --- /dev/null +++ b/plugins/open_graph/models/open_graph_plugin/friend_track_config.rb @@ -0,0 +1,8 @@ +class OpenGraphPlugin::FriendTrackConfig < OpenGraphPlugin::TrackConfig + + # workaround for STI bug + self.table_name = :open_graph_plugin_tracks + + self.track_name = :friend + +end diff --git a/plugins/open_graph/models/open_graph_plugin/track.rb b/plugins/open_graph/models/open_graph_plugin/track.rb new file mode 100644 index 0000000..697e9b5 --- /dev/null +++ b/plugins/open_graph/models/open_graph_plugin/track.rb @@ -0,0 +1,28 @@ +class OpenGraphPlugin::Track < ActiveRecord::Base + + attr_accessible :type, :context, :tracker_id, :tracker, :actor_id, :action, + :object_type, :object_data, :object_data_id, :object_data_type, :object_data_url + + belongs_to :tracker, class_name: 'Profile' + belongs_to :actor, class_name: 'Profile' + belongs_to :object_data, polymorphic: true + + validates_presence_of :context + before_validation :set_context + + def self.objects + [] + end + + def self.association + @association ||= "open_graph_#{self.name.demodulize.pluralize.underscore}".to_sym + end + + protected + + def set_context + self.context = OpenGraphPlugin.context + end + +end + diff --git a/plugins/open_graph/models/open_graph_plugin/track_config.rb b/plugins/open_graph/models/open_graph_plugin/track_config.rb new file mode 100644 index 0000000..184810a --- /dev/null +++ b/plugins/open_graph/models/open_graph_plugin/track_config.rb @@ -0,0 +1,49 @@ +class OpenGraphPlugin::TrackConfig < OpenGraphPlugin::Track + + Types = { + activity: 'ActivityTrackConfig', + enterprise: 'EnterpriseTrackConfig', + community: 'CommunityTrackConfig', + # TODO: not yet implemented + #friend: 'FriendTrackConfig', + } + + # define on subclasses (required) + class_attribute :track_name + def self.track_enabled_field + "#{self.track_name}_track_enabled" + end + + # true if do not depend on records (e.g. EnterpriseTrackConfig depends on friends) + # redefine on subclasses + class_attribute :static_trackers + self.static_trackers = false + + def self.enabled? context, actor + settings = actor.send "#{context}_settings" + settings.send "#{self.track_name}_track_enabled" + end + + scope :tracks_to_profile, lambda { |profile, exclude_actor=nil| + scope = where object_data_id: profile.id, object_data_type: profile.class.base_class + scope = scope.where context: OpenGraphPlugin.context + scope = scope.includes :tracker + scope = scope.where ['tracker_id <> ?', exclude_actor.id] if exclude_actor + scope + } + + # redefine on subclasses + def self.trackers_to_profile profile + tracks = self.tracks_to_profile profile + tracks = tracks.where type: self + tracks.map(&:tracker) + end + + def self.profile_tracks profile + profile.send self.association + end + def self.profile_track_objects profile + self.profile_tracks(profile).map(&:object_data).compact + end + +end diff --git a/plugins/open_graph/plugin.yml b/plugins/open_graph/plugin.yml new file mode 100644 index 0000000..b63a516 --- /dev/null +++ b/plugins/open_graph/plugin.yml @@ -0,0 +1,3 @@ +name: open_graph +dependencies: + - metadata diff --git a/plugins/open_graph/public/javascripts/open_graph.js b/plugins/open_graph/public/javascripts/open_graph.js new file mode 100644 index 0000000..0d01d8c --- /dev/null +++ b/plugins/open_graph/public/javascripts/open_graph.js @@ -0,0 +1,181 @@ +open_graph = { + + track: { + + config: { + + view: { + form: null, + }, + + init: function(reload) { + this.view.form = $('#track-form form') + this.view.form.find('.panel-heading').each(function(i, context) { + open_graph.track.config.headingToggle(context) + }) + }, + + submit: function() { + loading_overlay.show($('#track-config')) + open_graph.track.config.view.form.ajaxSubmit({ + success: function(data) { + data = $(data) + // needs update to get ids from accepts_nested_attributes_for + $('#track-activity').html(data.find('#track-activity').html()) + loading_overlay.hide($('#track-config')) + }, + }) + return false; + }, + + // trigged on init state and on subcheckboxes change + headingToggle: function(context, open) { + var panel = $(context).parents('.panel') + var panelHeading = panel.find('.panel-heading') + var panelBody = panel.find('.panel-body') + var parentCheckbox = panel.find('.config-check') + var configButton = panel.find('.config-button') + var input = panel.find('.track-config-toggle') + var openWas = input.val() == 'true' + if (open === undefined) + open = input.val() == 'true' && (panelHeading.hasClass('enable-on-empty') || this.numberChecked(context) > 0) + // open is defined, that is an user action + else { + if (open) { + if (panelHeading.hasClass('open-on-enable')) + panelBody.collapse('show') + } else + panelBody.collapse('hide') + } + + configButton.toggle(open) + parentCheckbox.toggleClass('fa-toggle-on', open) + parentCheckbox.toggleClass('fa-toggle-off', !open) + input.prop('value', open) + if (openWas != open) + open_graph.track.config.submit() + }, + + // the event of change + toggleEvent: function(context, event) { + var panel = $(context).parents('.panel') + var panelBody = panel.find('.panel-body') + var checkboxes = panelBody.find('input[type=checkbox]') + var open = panel.find('.track-config-toggle').val() == 'true' + open = !open; + + checkboxes.prop('checked', open) + + this.headingToggle(context, open) + return false; + }, + + open: function(context) { + var panel = $(context).parents('.panel') + var panelBody = panel.find('.panel-body') + panelBody.collapse('show') + }, + + toggleObjectType: function(checkbox) { + checkbox = $(checkbox) + + this.headingToggle(checkbox) + + checkbox.siblings("input[name*='[_destroy]']").val(!checkbox.is(':checked')) + open_graph.track.config.submit() + }, + + numberChecked: function(context) { + var panel = $(context).parents('.panel') + var panelBody = panel.find('.panel-body') + var checkboxes = panel.find('.panel-body input[type=checkbox]') + var profilesInput = panel.find('.panel-body .select-profiles') + + var nObjects = checkboxes.filter(':checked').length + var nProfiles = profilesInput.length ? profilesInput.tokenfield('getTokens').length : 0; + var nChecked = nObjects + nProfiles; + var nTotal = checkboxes.length + nProfiles + + return nChecked + }, + + enterprise: { + see_all: function(context) { + var panel = $(context).parents('.panel') + var panelBody = panel.find('.panel-body') + noosfero.modal.html(panelBody.html()) + }, + }, + + initAutocomplete: function(track, url, items) { + var selector = '#select-'+track + var input = $(selector) + var tokenField = open_graph.autocomplete.init(url, selector, items) + + input.change(open_graph.track.config.submit) + tokenField + .on('tokenfield:createdtoken tokenfield:removedtoken', function() { + open_graph.track.config.headingToggle(this) + }).on('tokenfield:createtoken tokenfield:removetoken', function(event) { + input.val() + }).on('tokenfield:createtoken', function(event) { + var existingTokens = $(this).tokenfield('getTokens') + $.each(existingTokens, function(index, token) { + if (token.value === event.attrs.value) + event.preventDefault() + }) + }) + + return tokenField; + }, + + }, + }, + + autocomplete: { + bloodhoundOptions: { + datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), + queryTokenizer: Bloodhound.tokenizers.whitespace, + ajax: { + beforeSend: function() { + input.addClass('small-loading') + }, + complete: function() { + input.removeClass('small-loading') + }, + }, + }, + tokenfieldOptions: { + + }, + typeaheadOptions: { + minLength: 1, + highlight: true, + }, + + init: function(url, selector, data, options) { + options = options || {} + var bloodhoundOptions = $.extend({}, this.bloodhoundOptions, options.bloodhound || {}); + var typeaheadOptions = $.extend({}, this.typeaheadOptions, options.typeahead || {}); + var tokenfieldOptions = $.extend({}, this.tokenfieldOptions, options.tokenfield || {}); + + var input = $(selector) + bloodhoundOptions.remote = { + url: url, + replace: function(url, uriEncodedQuery) { + return $.param.querystring(url, {query:uriEncodedQuery}); + }, + } + var engine = new Bloodhound(bloodhoundOptions) + engine.initialize() + + tokenfieldOptions.typeahead = [typeaheadOptions, { displayKey: 'label', source: engine.ttAdapter() }] + + var tokenField = input.tokenfield(tokenfieldOptions) + input.tokenfield('setTokens', data) + + return input + }, + }, +} + diff --git a/plugins/open_graph/public/style.scss b/plugins/open_graph/public/style.scss new file mode 120000 index 0000000..49c35bf --- /dev/null +++ b/plugins/open_graph/public/style.scss @@ -0,0 +1 @@ +stylesheets/style.scss \ No newline at end of file diff --git a/plugins/open_graph/public/stylesheets/style.scss b/plugins/open_graph/public/stylesheets/style.scss new file mode 100644 index 0000000..37a1c51 --- /dev/null +++ b/plugins/open_graph/public/stylesheets/style.scss @@ -0,0 +1,66 @@ +#track-form { + + .panel-heading { + a, a:visited { + color: #fff; + display: block; + text-decoration: none; + } + a:hover { + text-decoration: underline; + } + a.btn { + display: inline-block; + text-decoration: none; + } + } + + // always use one line to fit placeholder + .tokenfield { + + .twitter-typeahead { + width: 100%; + display: block; + + .tt-input { + width: 100% !important; + } + } + } + + #track-config { + .panel-heading { + padding-left: 36px; + } + span.config-check { + font-weight: bold; + margin-left: -26px; + margin-right: 4px; + } + span.config-check.fa-toggle-off { + color: #99f; + } + .activity-config, .sse-config, .community-config { + float:right; + } + .activity-label, .sse-label, .community-label { + margin-right: 90px; + } + } +} + +// shown inside a popin +.open-graph-enterprises-modal { + overflow: hidden; + + h1 { + font-size: 22px; + } + #open-graph-favorite-enterprises-how-to { + clear:both; + padding-top: 1px; + } + p { + text-align:justify; + } +} diff --git a/plugins/open_graph/test/functional/open_graph_graph/my_profile_controller_test.rb b/plugins/open_graph/test/functional/open_graph_graph/my_profile_controller_test.rb new file mode 100644 index 0000000..cb06a6b --- /dev/null +++ b/plugins/open_graph/test/functional/open_graph_graph/my_profile_controller_test.rb @@ -0,0 +1,54 @@ +require 'test_helper' +require 'open_graph_plugin/myprofile_controller' + +# Re-raise errors caught by the controller. +class OpenGraphPlugin::MyprofileController; def rescue_action(e) raise e end; end + +class OpenGraphPlugin::MyprofileControllerTest < ActionController::TestCase + + def setup + @controller = OpenGraphPlugin::MyprofileController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + @actor = create_user.person + end + + should "save selected activities" do + login_as @actor.identifier + @myenterprise = @actor.environment.enterprises.create! name: 'mycoop', identifier: 'mycoop' + @myenterprise.add_member @actor + @enterprise = @actor.environment.enterprises.create! name: 'coop', identifier: 'coop' + @enterprise.fans << @actor + + post :track_config, profile: @actor.identifier, profile_data: { + open_graph_settings: { + activity_track_enabled: "true", + enterprise_track_enabled: "true", + community_track_enabled: "false", + }, + open_graph_activity_track_configs_attributes: { + 0 => { + tracker_id: @actor.id, + object_type: 'blog_post', + }, + }, + + # ignored, enterprise uses static tracking + open_graph_enterprise_profiles_ids: [@enterprise.id], + } + @actor.reload + + assert_equal true, @actor.open_graph_settings.activity_track_enabled + assert_equal true, @actor.open_graph_settings.enterprise_track_enabled + assert_equal false, @actor.open_graph_settings.community_track_enabled + + assert_equal 1, @actor.open_graph_activity_track_configs.count + assert_equal 'blog_post', @actor.open_graph_activity_track_configs.first.object_type + assert_equal @actor.id, @actor.open_graph_activity_track_configs.first.tracker_id + + assert_equal [@actor], OpenGraphPlugin::EnterpriseTrackConfig.trackers_to_profile(@enterprise) + assert_equal [@actor], OpenGraphPlugin::EnterpriseTrackConfig.trackers_to_profile(@myenterprise) + + end + +end diff --git a/plugins/open_graph/test/unit/open_graph_graph/publisher_test.rb b/plugins/open_graph/test/unit/open_graph_graph/publisher_test.rb new file mode 100644 index 0000000..8c7748f --- /dev/null +++ b/plugins/open_graph/test/unit/open_graph_graph/publisher_test.rb @@ -0,0 +1,111 @@ +require "test_helper" + +class OpenGraphPlugin::PublisherTest < ActiveSupport::TestCase + + def setup + @actor = create_user.person + User.current = @actor.user + @stories = OpenGraphPlugin::Stories::Definitions + @publisher = OpenGraphPlugin::Publisher.new + OpenGraphPlugin::Stories.stubs(:publishers).returns([@publisher]) + @publisher.stubs(:context).returns(:open_graph) + @publisher.stubs(:og_domain).returns('noosfero.net') + end + + should "publish only tracked stuff" do + @other_actor = create_user.person + + @myenterprise = @actor.environment.enterprises.create! name: 'mycoop', identifier: 'mycoop' + @myenterprise.add_member @actor + @enterprise = @actor.environment.enterprises.create! name: 'coop', identifier: 'coop' + # the original domain from open_graph should be used + @enterprise.domains.create! name: 'customdomain.com' + + @community = @actor.environment.communities.create! name: 'comm', identifier: 'comm', closed: false + + @actor.update_attributes!({ + open_graph_settings: { + activity_track_enabled: "true", + enterprise_track_enabled: "true", + community_track_enabled: "true", + }, + open_graph_activity_track_configs_attributes: { + 0 => { tracker_id: @actor.id, object_type: 'blog_post', }, + 1 => { tracker_id: @actor.id, object_type: 'gallery_image', }, + 2 => { tracker_id: @actor.id, object_type: 'uploaded_file', }, + 3 => { tracker_id: @actor.id, object_type: 'event', }, + 4 => { tracker_id: @actor.id, object_type: 'forum', }, + 5 => { tracker_id: @actor.id, object_type: 'friend', }, + 6 => { tracker_id: @actor.id, object_type: 'favorite_enterprise', }, + }, + open_graph_enterprise_profiles_ids: "#{@enterprise.id}", + open_graph_community_profiles_ids: "#{@community.id}", + }) + @other_actor.update_attributes! open_graph_settings: { activity_track_enabled: "true", }, + open_graph_activity_track_configs_attributes: { 0 => { tracker_id: @other_actor.id, object_type: 'friend', }, } + + # active + User.current = @actor.user + + blog = Blog.create! profile: @actor, name: 'blog' + blog_post = TinyMceArticle.new profile: User.current.person, parent: blog, name: 'blah', author: User.current.person + @publisher.expects(:publish).with(User.current.person, @stories[:create_an_article], @publisher.send(:url_for, blog_post)) + blog_post.save! + + gallery = Gallery.create! name: 'gallery', profile: User.current.person + image = UploadedFile.new uploaded_data: fixture_file_upload('/files/rails.png', 'image/png'), parent: gallery, profile: User.current.person + @publisher.expects(:publish).with(User.current.person, @stories[:add_an_image], @publisher.send(:url_for, image, image.url.merge(view: true))) + image.save! + + document = UploadedFile.new uploaded_data: fixture_file_upload('/files/doctest.en.xhtml', 'text/html'), profile: User.current.person + @publisher.expects(:publish).with(User.current.person, @stories[:add_a_document], @publisher.send(:url_for, document, document.url.merge(view: true))) + document.save! + + event = Event.new name: 'event', profile: User.current.person + @publisher.expects(:publish).with(User.current.person, @stories[:create_an_event], @publisher.send(:url_for, event)) + event.save! + + forum = Forum.create! name: 'forum', profile: User.current.person + topic = TinyMceArticle.new profile: User.current.person, parent: forum, name: 'blah2', author: User.current.person + @publisher.expects(:publish).with(User.current.person, @stories[:start_a_discussion], @publisher.send(:url_for, topic, topic.url.merge(og_type: MetadataPlugin.og_types[:forum]))) + topic.save! + + @publisher.expects(:publish).with(@actor, @stories[:make_friendship_with], @publisher.send(:url_for, @other_actor)).twice + @publisher.expects(:publish).with(@other_actor, @stories[:make_friendship_with], @publisher.send(:url_for, @actor)).twice + AddFriend.create!(person: @actor, friend: @other_actor).finish + Friendship.remove_friendship @actor, @other_actor + # friend verb is groupable + AddFriend.create!(person: @actor, friend: @other_actor).finish + + @publisher.expects(:publish).with(User.current.person, @stories[:favorite_a_sse_initiative], @publisher.send(:url_for, @enterprise)) + @enterprise.fans << User.current.person + + # active but published as passive + User.current = @actor.user + + blog_post = TinyMceArticle.new profile: @enterprise, parent: @enterprise.blog, name: 'blah', author: User.current.person + story = @stories[:announce_news_from_a_sse_initiative] + @publisher.expects(:publish).with(User.current.person, story, @publisher.send(:passive_url_for, blog_post, nil, story)) + blog_post.save! + + # passive + User.current = @other_actor.user + + # fan + blog_post = TinyMceArticle.new profile: @enterprise, parent: @enterprise.blog, name: 'blah2', author: User.current.person + story = @stories[:announce_news_from_a_sse_initiative] + @publisher.expects(:publish).with(@actor, story, 'http://noosfero.net/coop/blog/blah2') + blog_post.save! + # member + blog_post = TinyMceArticle.new profile: @myenterprise, parent: @myenterprise.blog, name: 'blah2', author: User.current.person + story = @stories[:announce_news_from_a_sse_initiative] + @publisher.expects(:publish).with(@actor, story, 'http://noosfero.net/mycoop/blog/blah2') + blog_post.save! + + blog_post = TinyMceArticle.new profile: @community, parent: @community.blog, name: 'blah', author: User.current.person + story = @stories[:announce_news_from_a_community] + @publisher.expects(:publish).with(@actor, story, 'http://noosfero.net/comm/blog/blah') + blog_post.save! + end + +end diff --git a/plugins/open_graph/views/open_graph_plugin/myprofile/_ac_profile.html.erb b/plugins/open_graph/views/open_graph_plugin/myprofile/_ac_profile.html.erb new file mode 100644 index 0000000..abb11c9 --- /dev/null +++ b/plugins/open_graph/views/open_graph_plugin/myprofile/_ac_profile.html.erb @@ -0,0 +1,2 @@ +<%= profile_image profile, :icon %> +<%= profile.short_name nil %> diff --git a/plugins/open_graph/views/open_graph_plugin/myprofile/_heading.html.erb b/plugins/open_graph/views/open_graph_plugin/myprofile/_heading.html.erb new file mode 100644 index 0000000..a423ee4 --- /dev/null +++ b/plugins/open_graph/views/open_graph_plugin/myprofile/_heading.html.erb @@ -0,0 +1,5 @@ + +<%= f.fields_for "#{context}_settings" do |ff| %> + <%= ff.hidden_field klass.track_enabled_field, value: profile.send("#{context}_settings").send(klass.track_enabled_field), class: 'track-config-toggle' %> +<% end %> +<%= t("open_graph_plugin.views.track.config.#{track}.label") %> diff --git a/plugins/open_graph/views/open_graph_plugin/myprofile/_profile_search.jsonify b/plugins/open_graph/views/open_graph_plugin/myprofile/_profile_search.jsonify new file mode 100644 index 0000000..7d9ca50 --- /dev/null +++ b/plugins/open_graph/views/open_graph_plugin/myprofile/_profile_search.jsonify @@ -0,0 +1,7 @@ +self.formats = [:html] +profiles.each do |p| + json << { + value: p.id, label: render('open_graph_plugin/myprofile/ac_profile', profile: p), + } +end + diff --git a/plugins/open_graph/views/open_graph_plugin/myprofile/_track_activity.html.erb b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_activity.html.erb new file mode 100644 index 0000000..1593e35 --- /dev/null +++ b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_activity.html.erb @@ -0,0 +1,12 @@ +
+ <%= button_to_function 'menu-ctrl-panel', t('open_graph_plugin.views.track.config.activity.configure'), "", + class: 'activity-config config-button', option: 'success', size: 'xs', 'data-target' => "#track-#{track}", 'data-toggle' => 'collapse', 'aria-controls' => 'collapseTwo' %> + + <%= render 'heading', f: f, track: track, context: context, klass: klass %> + +
+ +
+ <%= render 'track_objects', f: f, track: track, objects: klass.objects, klass: klass, context: context %> +
+ diff --git a/plugins/open_graph/views/open_graph_plugin/myprofile/_track_community.html.erb b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_community.html.erb new file mode 100644 index 0000000..ea5ebe4 --- /dev/null +++ b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_community.html.erb @@ -0,0 +1,12 @@ +
+ <%= button_to_function 'menu-ctrl-panel', t('open_graph_plugin.views.track.config.activity.configure'), "", + class: 'community-config config-button', option: 'success', size: 'xs', 'data-target' => "#track-#{track}", 'data-toggle' => 'collapse', 'aria-controls' => 'collapseTwo' %> + + <%= render 'heading', f: f, track: track, context: context, klass: klass %> + +
+ +
+ <%= render 'track_objects', f: f, track: track, objects: klass.objects, klass: klass, context: context %> + <%= render 'track_profiles', f: f, track: track, context: context, klass: klass %> +
diff --git a/plugins/open_graph/views/open_graph_plugin/myprofile/_track_enterprise.html.erb b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_enterprise.html.erb new file mode 100644 index 0000000..481ffb5 --- /dev/null +++ b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_enterprise.html.erb @@ -0,0 +1,44 @@ +
+ <%= button_to_function 'menu-ctrl-panel', t('open_graph_plugin.views.track.config.enterprise.see_all'), 'open_graph.track.config.enterprise.see_all(this)', + class: 'sse-config config-button', option: 'success', size: 'xs' %> + + <%= render 'heading', f: f, track: track, context: context, klass: klass %> + +
+ + + diff --git a/plugins/open_graph/views/open_graph_plugin/myprofile/_track_form.html.erb b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_form.html.erb new file mode 100644 index 0000000..c17b71c --- /dev/null +++ b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_form.html.erb @@ -0,0 +1,22 @@ +<% + reload ||= false +%> +<%= javascript_tag do %> + open_graph.track.config.reload = <%= reload.to_json %> +<% end %> + +<%= form_for profile, as: :profile_data, remote: true, url: {action: :track_config}, + html: {id: 'track-config', onsubmit: 'return open_graph.track.config.submit()'} do |f| %> + +
+ <% OpenGraphPlugin::TrackConfig::Types.each do |track, klass| %> +
+ <%= render "track_#{track}", f: f, track: track, klass: "OpenGraphPlugin::#{klass}".constantize, context: context %> +
+ <% end %> +
+<% end %> + +<%= javascript_tag do %> + open_graph.track.config.init() +<% end %> diff --git a/plugins/open_graph/views/open_graph_plugin/myprofile/_track_friend.html.erb b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_friend.html.erb new file mode 100644 index 0000000..0609055 --- /dev/null +++ b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_friend.html.erb @@ -0,0 +1,10 @@ +
+ + <%= render 'heading', f: f, track: track, context: context, klass: klass %> + +
+ +
+ <%= render 'track_objects', f: f, track: track, objects: klass.objects, klass: klass, context: context %> + <%= render 'track_profiles', f: f, track: track, context: context, klass: klass %> +
diff --git a/plugins/open_graph/views/open_graph_plugin/myprofile/_track_objects.html.erb b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_objects.html.erb new file mode 100644 index 0000000..4dfd97c --- /dev/null +++ b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_objects.html.erb @@ -0,0 +1,17 @@ +<% + tracks = profile.send klass.association +%> + +<% objects.each do |object| %> +
+ <% track_record = tracks.find{ |t| t.object_type == object } || profile.send(klass.association).build %> + <%= f.fields_for klass.association, track_record do |ff| %> + <%= ff.hidden_field :id %> + <%= ff.hidden_field :tracker_id %> + <%= ff.check_box :object_type, {onchange: 'open_graph.track.config.toggleObjectType(this)'}, object, '' %> + <%= ff.label :object_type, t("open_graph_plugin.views.track.config.#{track}.objects.#{object}") %> + <%= ff.hidden_field :_destroy %> + <% end %> +
+<% end %> + diff --git a/plugins/open_graph/views/open_graph_plugin/myprofile/_track_profiles.html.erb b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_profiles.html.erb new file mode 100644 index 0000000..231cff3 --- /dev/null +++ b/plugins/open_graph/views/open_graph_plugin/myprofile/_track_profiles.html.erb @@ -0,0 +1,19 @@ +<% + static = klass.static_trackers + profiles = klass.profile_track_objects profile +%> +<%= text_field_tag "#{f.object_name}[open_graph_#{track}_profiles_ids]", '', id: "select-#{track}", class: 'select-profiles', + placeholder: (t("open_graph_plugin.views.track.config.#{track}.search_placeholder") unless static), + disabled: ("disabled" if static) %> + +<%= javascript_tag do %> + $(document).ready(function () { + var input = open_graph.track.config.initAutocomplete(<%=track.to_json%>, + <%= url_for(action: "#{track}_search").to_json %>, + <%= profiles.map{ |p| {value: p.id, label: render('ac_profile', profile: p), } }.to_json %> + ) + <% if static %> + input.tokenfield('readonly') + <% end %> + }) +<% end %> -- libgit2 0.21.2