Commit 909929d8ac120e363c50d16ce78c13e008f60b25

Authored by root
2 parents 2aa6b487 8a8598d6

Merge branch 'rails3_community_dashboard' into rails3_stable

Showing 73 changed files with 2955 additions and 123 deletions   Show diff stats
app/controllers/my_profile/profile_editor_controller.rb
... ... @@ -15,20 +15,14 @@ class ProfileEditorController < MyProfileController
15 15 @possible_domains = profile.possible_domains
16 16 if request.post?
17 17 params[:profile_data][:fields_privacy] ||= {} if profile.person? && params[:profile_data].is_a?(Hash)
18   - begin
19   - Profile.transaction do
20   - Image.transaction do
21   - if profile.update_attributes!(params[:profile_data])
22   - redirect_to :action => 'index', :profile => profile.identifier
23   - end
  18 + Profile.transaction do
  19 + Image.transaction do
  20 + if @profile_data.update_attributes(params[:profile_data])
  21 + redirect_to :action => 'index', :profile => profile.identifier
  22 + else
  23 + profile.identifier = params[:profile] if profile.identifier.blank?
24 24 end
25   - end
26   - rescue Exception => ex
27   - if profile.identifier.blank?
28   - profile.identifier = params[:profile]
29   - end
30   - session[:notice] = _('Cannot update profile')
31   - logger.error ex.to_s
  25 + end
32 26 end
33 27 end
34 28 end
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -127,9 +127,9 @@ class ContentViewerController < ApplicationController
127 127  
128 128 if params[:slideshow]
129 129 render :action => 'slideshow', :layout => 'slideshow'
130   - return
  130 + else
  131 + render :file => @page.view_page, :layout => true
131 132 end
132   - render :view_page, :formats => [:html]
133 133 end
134 134  
135 135 def versions_diff
... ...
app/models/article.rb
... ... @@ -760,6 +760,10 @@ class Article < ActiveRecord::Base
760 760 true
761 761 end
762 762  
  763 + def view_page
  764 + "content_viewer/view_page.rhtml"
  765 + end
  766 +
763 767 private
764 768  
765 769 def sanitize_tag_list
... ...
app/models/comment.rb
... ... @@ -43,6 +43,8 @@ class Comment < ActiveRecord::Base
43 43 rec.errors.add(:name, _('{fn} can only be informed for unauthenticated authors').fix_i18n)
44 44 end
45 45 end
  46 +
  47 + acts_as_having_settings :field => :setting
46 48  
47 49 xss_terminate :only => [ :body, :title, :name ], :on => 'validation'
48 50  
... ...
app/models/person.rb
... ... @@ -157,11 +157,6 @@ class Person < Profile
157 157  
158 158 validates_multiparameter_assignments
159 159  
160   - validates_each :birth_date do |record,attr,value|
161   - date_str = record.birth_date_before_type_cast
162   - record.errors.add(attr) if value.blank? && !date_str.blank?
163   - end
164   -
165 160 def self.fields
166 161 FIELDS
167 162 end
... ...
app/models/profile.rb
... ... @@ -3,7 +3,7 @@
3 3 # which by default is the one returned by Environment:default.
4 4 class Profile < ActiveRecord::Base
5 5  
6   - attr_accessible :name, :identifier, :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :environment, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n
  6 + attr_accessible :name, :identifier, :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :environment, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time
7 7  
8 8 # use for internationalizable human type names in search facets
9 9 # reimplement on subclasses
... ...
app/views/layouts/_javascript.html.erb
... ... @@ -3,7 +3,7 @@
3 3 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox',
4 4 'jquery-ui-1.10.4/js/jquery-ui-1.10.4.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate',
5 5 'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput',
6   -'add-and-join', 'report-abuse', 'catalog', 'manage-products',
  6 +'add-and-join', 'report-abuse', 'catalog', 'manage-products', 'autogrow',
7 7 'jquery-timepicker-addon/dist/jquery-ui-timepicker-addon', 'application.js', 'rails.js', :cache => 'cache/application' %>
8 8  
9 9 <% language = FastGettext.locale %>
... ...
app/views/profile/_profile_wall.html.erb
... ... @@ -2,7 +2,7 @@
2 2 <div id='leave_scrap'>
3 3 <%= flash[:error] %>
4 4 <%= form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_activities', :success => "$('leave_scrap_content').value=''", :complete => "jQuery('#leave_scrap_form').removeClass('loading').find('*').attr('disabled', false)", :loading => "jQuery('#leave_scrap_form').addClass('loading').find('*').attr('disabled', true)", :html => {:id => 'leave_scrap_form' } do %>
5   - <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2 %>
  5 + <%= limited_text_area :scrap, :content, 420, 'leave_scrap_content', :cols => 50, :rows => 2, :class => 'autogrow' %>
6 6 <%= submit_button :new, _('Share') %>
7 7 <% end %>
8 8 </div>
... ...
app/views/profile_editor/_person_form.html.erb
... ... @@ -16,7 +16,7 @@
16 16 <%= optional_field(@person, 'jabber_id', f.text_field(:jabber_id, :rel => _('Jabber'))) %>
17 17 <%= optional_field(@person, 'personal_website', f.text_field(:personal_website, :rel => _('Personal website'))) %>
18 18 <%= optional_field(@person, 'sex', f.radio_group(:profile_data, :sex, [ ['male',_('Male')], ['female',_('Female')] ])) %>
19   -<%= optional_field(@person, 'birth_date', labelled_form_field(_('Birth date'), '<div class="select-birth-date">' + pick_date(:profile_data, :birth_date, {:start_year => (Date.today.year - 100), :end_year => (Date.today.year - 5)}) + '</div>')) %>
  19 +<%= optional_field(@person, 'birth_date', labelled_form_field(_('Birth date'), date_field('profile_data[birth_date]', @profile_data. birth_date, '%Y-%m-%d', {:change_month => true, :change_year => true, :year_range => '-100:-5', :date_format => 'yy-mm-dd'}, {:id => 'profile_data_birth_date'}))) %>
20 20 <%= optional_field(@person, 'nationality', f.text_field(:nationality, :rel => _('Nationality'))) %>
21 21 <%= optional_field(@person, 'country', select_country(_('Country'), 'profile_data', 'country', {:class => 'type-select'})) %>
22 22 <%= optional_field(@person, 'state', f.text_field(:state, :id => 'state_field', :rel => _('State'))) %>
... ...
config/environments/development.rb
... ... @@ -33,4 +33,6 @@ Noosfero::Application.configure do
33 33  
34 34 # Expands the lines which load the assets
35 35 config.assets.debug = true
  36 +
  37 + config.consider_all_requests_local = true
36 38 end
... ...
db/migrate/20140505190748_add_setting_to_comments.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class AddSettingToComments < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :comments, :setting, :text
  4 + end
  5 +
  6 + def self.down
  7 + remove_column :comments, :setting
  8 + end
  9 +end
... ...
etc/init.d/noosfero
... ... @@ -54,10 +54,10 @@ fi
54 54 ######################
55 55  
56 56 main_script() {
57   - cd $NOOSFERO_DIR
58 57 if [ "$NOOSFERO_USER" != "$USER" ]; then
59   - su $NOOSFERO_USER -l -c "./script/production $1"
  58 + su $NOOSFERO_USER -l -c "cd $NOOSFERO_DIR && ./script/production $1"
60 59 else
  60 + cd $NOOSFERO_DIR
61 61 ./script/production $1
62 62 fi
63 63 }
... ...
etc/noosfero/varnish-accept-language.vcl
... ... @@ -186,7 +186,7 @@ sub vcl_recv {
186 186  
187 187 sub vcl_fetch {
188 188 if (beresp.http.Vary) {
189   - set beresp.http.Vary = beresp.http.Vary ", X-Varnish-Accept-Language";
  189 + set beresp.http.Vary = beresp.http.Vary + ", X-Varnish-Accept-Language";
190 190 } else {
191 191 set beresp.http.Vary = "X-Varnish-Accept-Language";
192 192 }
... ...
etc/noosfero/varnish-noosfero.vcl
... ... @@ -45,7 +45,7 @@ sub vcl_error {
45 45 <div id='wrap'>
46 46 <div id='header'>
47 47 <div id='logo'>&nbsp;</div>
48   - <div id='details'><b>"} obj.status "</b> - " obj.response {"</div>
  48 + <div id='details'><b>"} + obj.status + "</b> - " + obj.response + {"</div>
49 49 </div>
50 50  
51 51 <div id='de' style='display: none' class='message'>
... ...
features/edit_profile.feature
... ... @@ -6,30 +6,6 @@ Feature: edit profile
6 6 | joao |
7 7 Given I am logged in as "joao"
8 8  
9   - Scenario: Warn about invalid birth date when active
10   - Given the following person fields are active fields
11   - | display_name |
12   - | birth_date |
13   - When I go to joao's control panel
14   - And I follow "Edit Profile"
15   - And I select "November" from "profile_data_birth_date_2i"
16   - And I select "15" from "profile_data_birth_date_3i"
17   - And I press "Save"
18   - Then I should see "Birth date is invalid"
19   - And I should not see "Birth date can't be blank"
20   -
21   - Scenario: Warn about invalid birth date when required
22   - Given the following person fields are required fields
23   - | display_name |
24   - | birth_date |
25   - When I go to joao's control panel
26   - And I follow "Edit Profile"
27   - And I select "November" from "profile_data_birth_date_2i"
28   - And I select "15" from "profile_data_birth_date_3i"
29   - And I press "Save"
30   - Then I should see "Birth date is invalid"
31   - And I should not see "Birth date can't be blank"
32   -
33 9 Scenario: Not warn if birth date is valid when active
34 10 Given the following person fields are active fields
35 11 | display_name |
... ...
lib/tasks/release.rake
... ... @@ -41,7 +41,7 @@ namespace :noosfero do
41 41 end
42 42  
43 43 def version
44   - require_dependency 'noosfero'
  44 + require 'noosfero'
45 45 Noosfero::VERSION
46 46 end
47 47  
... ... @@ -130,42 +130,6 @@ EOF
130 130 choice
131 131 end
132 132  
133   - desc 'sets the new version on apropriate files'
134   - task :set_version, :release_kind do |t, args|
135   - next if File.exist?("tmp/pending-release")
136   - release_kind = args[:release_kind] || 'stable'
137   -
138   - if release_kind == 'test'
139   - version_question = "Release candidate of which version"
140   - distribution = 'squeeze-test'
141   - else
142   - version_question = "Version that is being released"
143   - distribution = 'unstable'
144   - end
145   -
146   - version_name = new_version = ask(version_question)
147   -
148   - if release_kind == 'test'
149   - rc_version = ask('RC version', Time.now.strftime('%Y%m%d%H%M%S'))
150   - version_name += "~rc#{rc_version}"
151   - end
152   - release_message = ask("Release message")
153   -
154   - sh 'git checkout debian/changelog lib/noosfero.rb'
155   - sh "sed -i \"s/VERSION = '[^']*'/VERSION = '#{version_name}'/\" lib/noosfero.rb"
156   - sh "dch --newversion #{version_name} --distribution #{distribution} --force-distribution '#{release_message}'"
157   -
158   - sh 'git diff debian/changelog lib/noosfero.rb'
159   - if confirm("Commit version bump to #{version_name} on #{distribution} distribution")
160   - sh 'git add debian/changelog lib/noosfero.rb'
161   - sh "git commit -m 'Bumping version #{version_name}'"
162   - sh "touch tmp/pending-release"
163   - else
164   - sh 'git checkout debian/changelog lib/noosfero.rb'
165   - abort 'Version update not confirmed. Reverting changes and exiting...'
166   - end
167   - end
168   -
169 133 desc "uploads the packages to the repository"
170 134 task :upload_packages, :release_kind do |t, args|
171 135 release_kind = args[:release_kind] || 'stable'
... ... @@ -177,9 +141,13 @@ EOF
177 141 next if File.exist?("tmp/pending-release")
178 142 release_kind = args[:release_kind] || 'stable'
179 143  
180   - if release_kind == 'test'
  144 + if release_kind =~ /test/
181 145 version_question = "Release candidate of which version: "
182   - distribution = 'squeeze-test'
  146 + if release_kind == 'squeeze-test'
  147 + distribution = 'squeeze-test'
  148 + elsif release_kind == 'wheezy-test'
  149 + distribution = 'wheezy-test'
  150 + end
183 151 else
184 152 version_question = "Version that is being released: "
185 153 distribution = 'unstable'
... ... @@ -187,7 +155,7 @@ EOF
187 155  
188 156 version_name = new_version = ask(version_question)
189 157  
190   - if release_kind == 'test'
  158 + if release_kind =~ /test/
191 159 timestamp = Time.now.strftime('%Y%m%d%H%M%S')
192 160 version_name += "~rc#{timestamp}"
193 161 end
... ... @@ -208,12 +176,6 @@ EOF
208 176 end
209 177 end
210 178  
211   - desc "uploads the packages to the repository"
212   - task :upload_packages, :release_kind do |t, args|
213   - release_kind = args[:release_kind] || 'stable'
214   - sh "dput --unchecked #{release_kind} #{Dir['pkg/*.changes'].first}"
215   - end
216   -
217 179 desc 'prepares a release tarball'
218 180 task :release, :release_kind do |t, args|
219 181 release_kind = args[:release_kind] || 'stable'
... ... @@ -283,5 +245,5 @@ EOF
283 245 sh 'apt-ftparchive release . > Release'
284 246 end
285 247 end
286   -
  248 +
287 249 end
... ...
plugins/community_hub/controllers/myprofile/community_dashboard_plugin_myprofile_controller.rb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +class CommunityDashboardPluginMyprofileController < MyProfileController
  2 +
  3 + append_view_path File.join(File.dirname(__FILE__) + '/../../views')
  4 +
  5 + before_filter :allow_edit_dashboard, :only => :save_order
  6 +
  7 + def save_order
  8 + dashboard = profile.articles.find(params[:dashboard])
  9 + redirect_to dashboard.url
  10 + end
  11 +
  12 + protected
  13 +
  14 + def allow_edit_dashboard
  15 + render_access_denied unless profile.articles.find(params[:dashboard]).allow_edit?(user)
  16 + end
  17 +
  18 +end
... ...
plugins/community_hub/controllers/public/community_hub_plugin_public_controller.rb 0 → 100644
... ... @@ -0,0 +1,219 @@
  1 +class CommunityHubPluginPublicController < PublicController
  2 +
  3 + append_view_path File.join(File.dirname(__FILE__) + '/../../views')
  4 +
  5 + layout false
  6 +
  7 +
  8 + def new_message
  9 + if logged_in?
  10 +
  11 + begin
  12 + hub = Article.find(params[:article_id])
  13 + rescue
  14 + hub = nil
  15 + end
  16 +
  17 + if hub
  18 + message_data = {}
  19 + message_data.merge!(params[:message]) if params[:message]
  20 +
  21 + message = Comment.new(message_data)
  22 + message.author = user
  23 + message.title = message_timestamp
  24 + message.article = hub
  25 + message.ip_address = request.remote_ip
  26 + message.user_agent = request.user_agent
  27 + message.referrer = request.referrer
  28 +
  29 + if message && message.save
  30 + render :text => {'ok' => true}.to_json, :content_type => 'application/json'
  31 + return true
  32 + end
  33 + end
  34 +
  35 + end
  36 + render :text => {'ok' => false}.to_json, :content_type => 'application/json'
  37 + end
  38 +
  39 +
  40 + def new_mediation
  41 + if logged_in?
  42 +
  43 + begin
  44 + profile = Profile.find(params[:profile_id])
  45 + rescue
  46 + profile = nil
  47 + end
  48 +
  49 + if profile
  50 + mediation_data = {}
  51 + mediation_data.merge!(params[:article]) if params[:article]
  52 +
  53 + mediation = CommunityHubPlugin::Mediation.new(mediation_data)
  54 + mediation.name = CommunityHubPlugin::Mediation.timestamp
  55 + mediation.profile = profile
  56 + mediation.last_changed_by = user
  57 + mediation.created_by_id = user.id
  58 + mediation.source = 'local'
  59 +
  60 + if mediation && mediation.save
  61 + render :text => {'ok' => true}.to_json, :content_type => 'application/json'
  62 + return true
  63 + end
  64 + end
  65 +
  66 + end
  67 + render :text => {'ok' => false}.to_json, :content_type => 'application/json'
  68 + end
  69 +
  70 +
  71 + def newer_mediation_comment
  72 + latest_id = params[:latest_post]
  73 + mediation = params[:mediation]
  74 + comments = Comment.find(:all, :conditions => ["id > :id and source_id = :mediation", { :id => latest_id, :mediation => mediation }])
  75 + render :partial => "mediation_comment", :collection => comments
  76 + end
  77 +
  78 +
  79 + def newer_comments
  80 + latest_post = params[:latest_post]
  81 + hub = Article.find(params[:hub])
  82 + posts = Comment.find(:all, :order => "id desc", :conditions => ["id > :id and source_id = :hub", { :id => latest_post, :hub => hub }], :limit => 30)
  83 +
  84 + if !posts.empty?
  85 + oldest_post = posts.last.id
  86 + latest_post = posts.first.id
  87 + size = posts.size
  88 + else
  89 + oldest_post = 0
  90 + latest_post = 0
  91 + end
  92 +
  93 + render :partial => "post", :collection => posts, :locals => { :latest_id => latest_post, :oldest_id => oldest_post, :hub => hub }
  94 + end
  95 +
  96 +
  97 + def older_comments
  98 + oldest_id = params[:oldest_id]
  99 + hub = Article.find(params[:hub])
  100 + posts = Comment.find(:all, :order => "id desc", :conditions => ["id < :id and source_id = :hub", { :id => oldest_id, :hub => hub }], :limit => 30)
  101 +
  102 + if !posts.empty?
  103 + oldest_id = posts.last.id
  104 + latest_id = posts.first.id
  105 + end
  106 +
  107 + render :partial => "post", :collection => posts, :locals => { :latest_id => latest_id, :oldest_id => oldest_id, :hub => hub }
  108 + end
  109 +
  110 +
  111 + def newer_articles
  112 + latest_post = params[:latest_post]
  113 + hub = Article.find(params[:hub])
  114 + posts = CommunityHubPlugin::Mediation.find(:all, :order => "id desc", :conditions => ["id > :id and parent_id = :hub", { :id => latest_post, :hub => hub.id }])
  115 +
  116 + if !posts.empty?
  117 + oldest_post = posts.last.id
  118 + latest_post = posts.first.id
  119 + else
  120 + oldest_post = 0
  121 + latest_post = 0
  122 + end
  123 +
  124 + render :partial => "mediation", :collection => posts, :locals => { :latest_post => latest_post, :oldest_post => oldest_post, :hub => hub }
  125 + end
  126 +
  127 +
  128 + def promote_user
  129 + if logged_in?
  130 + if (!params[:hub].blank? && !params[:user].blank?)
  131 + begin
  132 + hub = Article.find(params[:hub])
  133 + rescue
  134 + hub = nil
  135 + end
  136 + if hub && hub.mediator?(user)
  137 + begin
  138 + user_to_promote = Profile.find(params[:user])
  139 + rescue
  140 + user_to_promote = nil
  141 + end
  142 + if user_to_promote
  143 + hub.mediators += [user_to_promote.id] unless hub.mediators.include?(user_to_promote.id)
  144 + if hub.save
  145 + render :text => {'ok' => true}.to_json, :content_type => 'application/json'
  146 + return true
  147 + end
  148 + end
  149 + end
  150 + end
  151 + end
  152 + render :text => {'ok' => false}.to_json, :content_type => 'application/json'
  153 + end
  154 +
  155 +
  156 + def pin_message
  157 + if logged_in?
  158 + if (!params[:hub].blank? && !params[:message].blank?)
  159 +
  160 + begin
  161 + hub = Article.find(params[:hub])
  162 + rescue
  163 + hub = nil
  164 + end
  165 +
  166 + if hub && hub.mediator?(user)
  167 + begin
  168 + message = Comment.find(params[:message])
  169 + rescue
  170 + message = nil
  171 + end
  172 +
  173 + if message
  174 + author = message.author.blank? ? user : message.author
  175 +
  176 + mediation = CommunityHubPlugin::Mediation.new
  177 + mediation.name = CommunityHubPlugin::Mediation.timestamp
  178 +
  179 + mediation.profile = hub.profile
  180 + mediation.last_changed_by = author
  181 + mediation.created_by_id = author.id
  182 + mediation.body = message.body
  183 + mediation.parent_id = hub.id
  184 + mediation.source = 'local'
  185 +
  186 + if mediation && mediation.save
  187 +
  188 + if ( message.title == 'hub-message-twitter' )
  189 + mediation.source = 'twitter'
  190 + mediation.author_name = message.name
  191 + mediation.profile_picture = message.profile_picture
  192 + mediation.save
  193 + end
  194 +
  195 + hub.pinned_messages += [message.id] unless hub.pinned_messages.include?(message.id)
  196 + hub.pinned_mediations += [mediation.id] unless hub.pinned_mediations.include?(mediation.id)
  197 +
  198 + if hub && hub.save
  199 + render :text => {'ok' => true}.to_json, :content_type => 'application/json'
  200 + return true
  201 + end
  202 +
  203 + end
  204 +
  205 + end
  206 + end
  207 + end
  208 + end
  209 + render :text => {'ok' => false}.to_json, :content_type => 'application/json'
  210 + end
  211 +
  212 +
  213 + protected
  214 +
  215 + def message_timestamp
  216 + "hub-message-#{(Time.now.to_f * 1000).to_i}"
  217 + end
  218 +
  219 +end
... ...
plugins/community_hub/facebook_stream/codfish_facebook_api.rb 0 → 100644
... ... @@ -0,0 +1,86 @@
  1 +require 'rubygems'
  2 +require 'open-uri'
  3 +require 'json'
  4 +
  5 +token = 'CAAD8cd4tMVkBAO3sh2DrzwZCDfeQq9ZAvTz7Jz24ZC26KtMfBoljqaXhD2vBV1zpP0bjrpxXUBzJvKKcFzOm6rMG9Sok7iNVUaxt5iwr7dfMqCvHpMboKpqrqgeLrfCH5ITVTAdezA6ZBSr9iOJrqyCSOYfui0zTmbXJ3FqtshwNRrRy4NPH'
  6 +hashtag = "love"
  7 +pooling_time = 10
  8 +
  9 +
  10 +#Aviso 12/04/2014
  11 +#token que só deverá expirar em 59 dias
  12 +#@graph = Koala::Facebook::API.new('CAAD8cd4tMVkBAO3sh2DrzwZCDfeQq9ZAvTz7Jz24ZC26KtMfBoljqaXhD2vBV1zpP0bjrpxXUBzJvKKcFzOm6rMG9Sok7iNVUaxt5iwr7dfMqCvHpMboKpqrqgeLrfCH5ITVTAdezA6ZBSr9iOJrqyCSOYfui0zTmbXJ3FqtshwNRrRy4NPH')
  13 +# https://graph.facebook.com/v1.0/search?q=%23nba&type=post&access_token=CAAD8cd4tMVkBAO3sh2DrzwZCDfeQq9ZAvTz7Jz24ZC26KtMfBoljqaXhD2vBV1zpP0bjrpxXUBzJvKKcFzOm6rMG9Sok7iNVUaxt5iwr7dfMqCvHpMboKpqrqgeLrfCH5ITVTAdezA6ZBSr9iOJrqyCSOYfui0zTmbXJ3FqtshwNRrRy4NPH
  14 +
  15 +def not_blank(v)
  16 + if v == nil || v == ""
  17 + false
  18 + else
  19 + true
  20 + end
  21 +end
  22 +
  23 +#if hashtag[0]='#'
  24 +# hashtag = hashtag[1,hashtag.length-1]
  25 +#end
  26 +
  27 +extractedComments = []
  28 +initialComments = []
  29 +firstTime = true
  30 +read = 1
  31 +
  32 +while true
  33 + file = open("https://graph.facebook.com/v1.0/search?q=%23#{hashtag}&type=post&access_token=#{token}")
  34 + itens = JSON.parse(file.read)['data']
  35 + mostRecent = ""
  36 + itens.each{|i|
  37 + from = ""
  38 + message = ""
  39 + if not_blank(i['from']['name'])
  40 + from = i['from']['name']
  41 + if not_blank(i['message'])
  42 + message += i['message']
  43 + end
  44 + if not_blank(message)
  45 + if mostRecent == "" or mostRecent < i["created_time"]
  46 + mostRecent = i["created_time"]
  47 + end
  48 +
  49 + extractedComments.push("#{from} said: #{message}")
  50 + # puts "#{from} said: #{message}"
  51 + end
  52 + end
  53 + }
  54 +
  55 + extractedComments = extractedComments.uniq
  56 + if firstTime
  57 + initialComments = extractedComments.clone
  58 + firstTime = false
  59 + end
  60 +
  61 +# extractedComments.each{|comment|
  62 +# puts comment
  63 +# }
  64 +
  65 +# extractedComments.each{|comment|
  66 +# puts comment
  67 +# }
  68 +
  69 +
  70 + newComments = extractedComments - initialComments
  71 + newComments = newComments.uniq
  72 + initialComments += newComments
  73 + initialComments = initialComments.uniq
  74 + newComments.each{|comment|
  75 + puts comment
  76 + }
  77 + puts "****************************************************************************************************************"
  78 + puts "most recent post at #{mostRecent}"
  79 + puts "Read: #{read} newComments: #{newComments.length} initialComments: #{initialComments.length} extractedComments: #{extractedComments.length} *******"
  80 +
  81 + read+=1
  82 + sleep(pooling_time)
  83 +end
  84 +
  85 +
  86 +
... ...
plugins/community_hub/facebook_stream/facebook_stream.rb 0 → 100755
... ... @@ -0,0 +1,56 @@
  1 +require 'rubygems'
  2 +require 'koala'
  3 +require 'json'
  4 +
  5 +#Aviso 12/04/2014
  6 +#token que só deverá expirar em 59 dias
  7 +@graph = Koala::Facebook::API.new('CAAD8cd4tMVkBAO3sh2DrzwZCDfeQq9ZAvTz7Jz24ZC26KtMfBoljqaXhD2vBV1zpP0bjrpxXUBzJvKKcFzOm6rMG9Sok7iNVUaxt5iwr7dfMqCvHpMboKpqrqgeLrfCH5ITVTAdezA6ZBSr9iOJrqyCSOYfui0zTmbXJ3FqtshwNRrRy4NPH')
  8 +# https://graph.facebook.com/v1.0/search?q=%23dilma&type=post&access_token=CAAD8cd4tMVkBAO3sh2DrzwZCDfeQq9ZAvTz7Jz24ZC26KtMfBoljqaXhD2vBV1zpP0bjrpxXUBzJvKKcFzOm6rMG9Sok7iNVUaxt5iwr7dfMqCvHpMboKpqrqgeLrfCH5ITVTAdezA6ZBSr9iOJrqyCSOYfui0zTmbXJ3FqtshwNRrRy4NPH
  9 +
  10 +#feed = @graph.get_connections("participabr", "posts")
  11 +
  12 +comentariosIniciais = []
  13 +comentariosExtraidos = []
  14 +comentariosNovos = []
  15 +
  16 +primeiraVez = true
  17 +
  18 +while true
  19 +
  20 + feed = @graph.get_connections("mundoreagindo", "posts")
  21 +
  22 + array = []
  23 + comentariosExtraidos = []
  24 + feed.each {|f|
  25 + if f['comments'] != nil && f['comments']['data'] != nil
  26 + array.push(f['comments']['data'])
  27 + end
  28 + }
  29 +
  30 + array.each{ |comentarios|
  31 + comentarios.each{|comentario|
  32 + comentariosExtraidos.push("#{comentario['from']['name']} disse: #{comentario['message']}")
  33 + }
  34 + }
  35 +
  36 + comentariosExtraidos = comentariosExtraidos.uniq
  37 +
  38 + if primeiraVez
  39 + comentariosIniciais=comentariosExtraidos.clone
  40 + primeiraVez = false
  41 + end
  42 +
  43 +# comentariosExtraidos.each{|comentario|
  44 +# puts comentario
  45 +# }
  46 +
  47 + comentariosNovos = comentariosExtraidos - comentariosIniciais
  48 + comentariosNovos = comentariosNovos.uniq
  49 + comentariosIniciais += comentariosNovos
  50 + comentariosIniciais = comentariosIniciais.uniq
  51 + comentariosNovos.each{|comentario|
  52 + puts comentario
  53 + }
  54 +
  55 + sleep(5)
  56 +end
... ...
plugins/community_hub/facebook_stream/lib_facebook_koala_stream.rb 0 → 100644
... ... @@ -0,0 +1,48 @@
  1 +require 'rubygems'
  2 +require 'koala'
  3 +require 'json'
  4 +
  5 +#Warning!!! Warning!!! Warning!!! Warning!!! Warning!!! Warning!!! Warning!!! Warning!!!
  6 +#token will expire at 12/04/2014 (Brazilian date format) + 59 days
  7 +#'CAAD8cd4tMVkBAO3sh2DrzwZCDfeQq9ZAvTz7Jz24ZC26KtMfBoljqaXhD2vBV1zpP0bjrpxXUBzJvKKcFzOm6rMG9Sok7iNVUaxt5iwr7dfMqCvHpMboKpqrqgeLrfCH5ITVTAdezA6ZBSr9iOJrqyCSOYfui0zTmbXJ3FqtshwNRrRy4NPH'
  8 +# BACKUP TOKEN 'CAAEhsewl0ZAcBAHhipXszZCURSwWLmgvceDbs9mB5baJdLriFxYMEzywmF2fvZBuThuA2Mm7QF8wPd3E6R5pVqVEnC2VhcBb4VrfAnkZC73ZC5g1NRUnKZCB2e6CaRiUBDatR2nf505PeKp7Aj5XxvTdfSqdZCsXxQFYZApPNSUUgkUWm6HwL4rp21MRJXb612sZD'
  9 +
  10 +def facebook_comments(hub, author_id, page_id, pooling_time, token, proxy_url)
  11 + pooling_time ||= 5
  12 + Koala.http_service.http_options = { :proxy => proxy_url } unless proxy_url.blank?
  13 +
  14 + @graph = Koala::Facebook::API.new(token)
  15 + initialComments = []
  16 + firstTime = true
  17 + while true
  18 + feed = @graph.get_connections(page_id, "posts")
  19 + array = []
  20 + extractedComments = []
  21 + feed.each {|f|
  22 + if f['comments'] != nil && f['comments']['data'] != nil
  23 + array.push(f['comments']['data'])
  24 + end
  25 + }
  26 + extractedComments = array.flatten.uniq
  27 + if firstTime
  28 + initialComments = extractedComments.clone
  29 + firstTime = false
  30 + end
  31 + newComments = extractedComments - initialComments
  32 + newComments = newComments.uniq
  33 + initialComments += newComments
  34 + initialComments = initialComments.uniq
  35 + newComments.each{|comment|
  36 + puts "#{comment['from']['name']} " + _("said") + ": #{comment['message']}"
  37 + noosferoComment = Comment.new
  38 + noosferoComment.title = 'hub-message-facebook'
  39 + noosferoComment.source = hub
  40 + noosferoComment.body = comment['message']
  41 + noosferoComment.author_id = author_id
  42 + noosferoComment.name = comment['from']['name']
  43 + noosferoComment.email = 'admin@localhost.local'
  44 + noosferoComment.save!
  45 + }
  46 + sleep(pooling_time)
  47 + end
  48 +end
... ...
plugins/community_hub/facebook_stream/lib_facebook_stream.rb 0 → 100644
... ... @@ -0,0 +1,117 @@
  1 +require 'rubygems'
  2 +require 'open-uri'
  3 +require 'json'
  4 +
  5 +def not_blank?(v)
  6 + if v == nil || v == ""
  7 + false
  8 + else
  9 + true
  10 + end
  11 +end
  12 +
  13 +def save_comment(hub, comment, author_id)
  14 + noosferoComment = Comment.new
  15 + noosferoComment.title = 'hub-message-facebook'
  16 + noosferoComment.source = hub
  17 + noosferoComment.body = comment[:message]
  18 + noosferoComment.author_id = author_id
  19 + noosferoComment.name = comment[:from]
  20 + noosferoComment.email = 'admin@localhost.local'
  21 + noosferoComment.save!
  22 +end
  23 +
  24 +
  25 +def facebook_comments(hub, author_id, hashtag, pooling_time, token)
  26 +
  27 + pooling_time ||= 10
  28 + token ||= 'CAAD8cd4tMVkBAO3sh2DrzwZCDfeQq9ZAvTz7Jz24ZC26KtMfBoljqaXhD2vBV1zpP0bjrpxXUBzJvKKcFzOm6rMG9Sok7iNVUaxt5iwr7dfMqCvHpMboKpqrqgeLrfCH5ITVTAdezA6ZBSr9iOJrqyCSOYfui0zTmbXJ3FqtshwNRrRy4NPH'
  29 + hashtag ||= "#nba"
  30 + #Aviso 12/04/2014
  31 + #token que só deverá expirar em 59 dias
  32 + #https://graph.facebook.com/v1.0/search?q=%23nba&type=post&access_token=CAAD8cd4tMVkBAO3sh2DrzwZCDfeQq9ZAvTz7Jz24ZC26KtMfBoljqaXhD2vBV1zpP0bjrpxXUBzJvKKcFzOm6rMG9Sok7iNVUaxt5iwr7dfMqCvHpMboKpqrqgeLrfCH5ITVTAdezA6ZBSr9iOJrqyCSOYfui0zTmbXJ3FqtshwNRrRy4NPH
  33 + #
  34 +
  35 + #removes extra '#'
  36 + if hashtag[0,1]=='#'
  37 + hashtag = hashtag[1,hashtag.length-1]
  38 + end
  39 +
  40 + initialComments = []
  41 + firstTime = true
  42 + read = 1
  43 + url = "https://graph.facebook.com/v1.0/search?q=%23#{hashtag}&type=post&access_token=#{token}"
  44 + mostRecent = ""
  45 +
  46 + while true
  47 + connected = false
  48 + tries = 0
  49 + while !connected
  50 + begin
  51 + tries += 1
  52 + file = open(url)
  53 + connected = true
  54 + tries = 0
  55 + rescue => e
  56 + puts "Error connecting to facebook: #{e.inspect} "
  57 + puts file
  58 + sleep (10 + 2 ** tries)
  59 + end
  60 + end
  61 +
  62 + extractedComments = []
  63 + itens = JSON.parse(file.read)['data']
  64 + itens.each{|i|
  65 + from = ""
  66 + message = ""
  67 + if not_blank?(i['from']['name'])
  68 + from = i['from']['name']
  69 + if not_blank?(i['message'])
  70 + message += i['message']
  71 + else
  72 + if not_blank?(i['description'])
  73 + message += i['description']
  74 + else
  75 + if not_blank?(i['caption'])
  76 + message += i['caption']
  77 + end
  78 + end
  79 + end
  80 + if not_blank?(message)
  81 + if mostRecent == "" or mostRecent < i["created_time"]
  82 + mostRecent = i["created_time"]
  83 + end
  84 + extractedComments.push({:from=>from, :message=>message})
  85 + end
  86 + end
  87 + }
  88 +
  89 + extractedComments = extractedComments.uniq
  90 + if firstTime
  91 + initialComments = extractedComments.clone
  92 + firstTime = false
  93 + extractedComments.each{|comment|
  94 + puts "#{comment[:from]} " + _("said") + ": #{comment[:message]}"
  95 + save_comment(hub, comment, author_id)
  96 + }
  97 + end
  98 +
  99 +# if read == 2
  100 +# extractedComments.push({:from=>"Evandro", :message=>"teste"})
  101 +# end
  102 +
  103 + newComments = extractedComments - initialComments
  104 + newComments = newComments.uniq
  105 + initialComments += newComments
  106 + initialComments = initialComments.uniq
  107 + #y newComments
  108 + newComments.each{|comment|
  109 + puts "#{comment[:from]} " + _("said") + ": #{comment[:message]}"
  110 + save_comment(hub, comment, author_id)
  111 + }
  112 +# puts url
  113 +# puts "Read: #{read} last post #{mostRecent} newComments: #{newComments.length} initialComments: #{initialComments.length} extractedComments: #{extractedComments.length}"
  114 + read+=1
  115 + sleep(pooling_time)
  116 + end
  117 +end
0 118 \ No newline at end of file
... ...
plugins/community_hub/facebook_stream/sample.hash 0 → 100644
... ... @@ -0,0 +1,37 @@
  1 +{"shares"=>
  2 + {"count"=>1}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/671466872912148"},
  3 + {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/671466872912148"}],
  4 + "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"},
  5 + "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif",
  6 + "id"=>"584831918242311_671466872912148",
  7 + "created_time"=>"2014-04-12T13:00:01+0000",
  8 + "type"=>"photo",
  9 + "updated_time"=>"2014-04-12T13:00:01+0000",
  10 + "privacy"=>{"value"=>""},
  11 + "link"=>"https://www.facebook.com/photo.php?fbid=671348086257360&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "object_id"=>"671348086257360", "status_type"=>"added_photos",
  12 + "message"=>"BOM DIA pra voc\303\252 que est\303\241 contando os dias para a #ArenaNETmundial!\n\nFALTA POUCO! Fique por dentro de tudo o que rolar\303\241 no maior evento da cultura digit@l! Quer saber mais sobre a programa\303\247\303\243o, os di\303\241logos e atra\303\247\303\265es? Ent\303\243o n\303\243o perca tempo! \n\nACOMPANHE e PARTICIPE! http://www.participa.br/arena\n\n#ParticipaBR", "likes"=>{"data"=>[{"name"=>"Nanda Barreto", "id"=>"100000525584317"}, {"name"=>"Natascha Castro", "id"=>"1792307671"}, {"name"=>"Vanessa Ferreira", "id"=>"100008128234636"}, {"name"=>"Katia Figueira", "id"=>"1171034080"}, {"name"=>"Carla Joner", "id"=>"1659400126"}, {"name"=>"Fagner Cunha", "id"=>"100007101672860"}, {"name"=>"Fernando Silva Soares Silva", "id"=>"1578450483"}], "paging"=>{"cursors"=>{"after"=>"MTU3ODQ1MDQ4Mw==", "before"=>"MTAwMDAwNTI1NTg0MzE3"}}}, "picture"=>"https://fbcdn-photos-d-a.akamaihd.net/hphotos-ak-prn1/v/t1.0-0/10171617_671348086257360_5332957054169466779_s.jpg?oh=57626662a0fccb0325162a7c72f86097&oe=53C4ECE6&__gda__=1405116765_706c0ad0982e3858c862eab9d71eed39"}
  13 +
  14 +{"shares"=>{"count"=>5}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/671235546268614"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/671235546268614"}], "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", "to"=>{"data"=>[{"name"=>"Centro Cultural S\303\243o Paulo", "category"=>"Public places", "id"=>"320942385642", "category_list"=>[{"name"=>"Art Gallery", "id"=>"197384240287028"}, {"name"=>"Theatre", "id"=>"173883042668223"}, {"name"=>"Library", "id"=>"169896676390222"}]}]}, "id"=>"584831918242311_671235546268614", "created_time"=>"2014-04-11T22:00:01+0000", "type"=>"photo", "updated_time"=>"2014-04-11T22:00:01+0000", "privacy"=>{"value"=>""}, "link"=>"https://www.facebook.com/photo.php?fbid=671207849604717&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "message_tags"=>{"187"=>[{"name"=>"Centro Cultural S\303\243o Paulo", "id"=>"320942385642", "type"=>"page", "length"=>25, "offset"=>187}]}, "object_id"=>"671207849604717", "status_type"=>"added_photos", "message"=>"Se voc\303\252 gosta de navegar LIVREMENTE pela INTERNET, a #ArenaNETmundial \303\251 o local ideal para nos encontramos! \n\nVamos AGITAR a cidade de S\303\243o Paulo, durante os dias 22, 23 e 24 de abril, no Centro Cultural S\303\243o Paulo, e contamos com a sua PARTICIPA\303\207\303\203O para definirmos, juntos, o FUTURO da REDE!\n\nConfirme sua presen\303\247a e venha para o maior encontro da cultura digital! http://bit.ly/ArenaNETmundial \n\n#ParticipaBR", "likes"=>{"data"=>[{"name"=>"MP LN", "id"=>"100000053156433"}, {"name"=>"Murutinga Abaetetuba Murutinga", "id"=>"100008004282653"}, {"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"Luiz Claudio Souza", "id"=>"100000088952450"}, {"name"=>"Raquel Saraiva", "id"=>"100001990410008"}, {"name"=>"Participabr", "id"=>"584831918242311"}, {"name"=>"Jo\303\243o Paulo Mehl", "id"=>"1231157546"}, {"name"=>"Alexandra Peixoto", "id"=>"749059638"}], "paging"=>{"cursors"=>{"after"=>"NzQ5MDU5NjM4", "before"=>"MTAwMDAwMDUzMTU2NDMz"}}}, "picture"=>"https://fbcdn-photos-g-a.akamaihd.net/hphotos-ak-prn2/t1.0-0/10169317_671207849604717_5406099128447544778_s.jpg"}
  15 +{"shares"=>{"count"=>1}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/671197902939045"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/671197902939045"}], "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", "to"=>{"data"=>[{"name"=>"Centro Cultural S\303\243o Paulo", "category"=>"Public places", "id"=>"320942385642", "category_list"=>[{"name"=>"Art Gallery", "id"=>"197384240287028"}, {"name"=>"Theatre", "id"=>"173883042668223"}, {"name"=>"Library", "id"=>"169896676390222"}]}]}, "id"=>"584831918242311_671197902939045", "created_time"=>"2014-04-11T19:47:48+0000", "type"=>"photo", "updated_time"=>"2014-04-11T19:47:48+0000", "privacy"=>{"value"=>""}, "link"=>"https://www.facebook.com/photo.php?fbid=671197652939070&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "message_tags"=>{"174"=>[{"name"=>"Centro Cultural S\303\243o Paulo", "id"=>"320942385642", "type"=>"page", "length"=>25, "offset"=>174}]}, "object_id"=>"671197652939070", "status_type"=>"added_photos", "message"=>"A #ArenaNETmundial est\303\241 sendo criada por muitas mentes e cora\303\247\303\265es que acreditam na #InternetLivre como uma condi\303\247\303\243o para a democracia!\n\nNeste EXATO momento est\303\241 rolando, no Centro Cultural S\303\243o Paulo, o \303\272ltimo ENCONTR\303\203O com a SOCIEDADE CIVIL para debater a AGENDA do evento! Na pauta, a defini\303\247\303\243o de temas, sugest\303\243o de convidad@s e e \303\272ltimos retoques na programa\303\247\303\243o. \n\nNosso MUITO obrigad@ a tod@s os parceir@s que est\303\243o conosco na constru\303\247\303\243o desta hist\303\263ria! \n\nAcompanhe nossa cobertura pelo Twitter: https://twitter.com/participabr", "likes"=>{"data"=>[{"name"=>"Carla Joner", "id"=>"1659400126"}, {"name"=>"Cl\303\241udia Sousa Leit\303\243o", "id"=>"100004451373687"}, {"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"At\303\255lio Alencar", "id"=>"100001932291363"}, {"name"=>"Participabr", "id"=>"584831918242311"}, {"name"=>"Yuri Almeida", "id"=>"551947517"}, {"name"=>"Janaina Spode", "id"=>"579251019"}, {"name"=>"Nanda Barreto", "id"=>"100000525584317"}], "paging"=>{"cursors"=>{"after"=>"MTAwMDAwNTI1NTg0MzE3", "before"=>"MTY1OTQwMDEyNg=="}}}, "picture"=>"https://fbcdn-photos-b-a.akamaihd.net/hphotos-ak-prn2/t1.0-0/1477604_671197652939070_5437456354222840972_s.jpg"}
  16 +{"shares"=>{"count"=>3}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/671156409609861"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/671156409609861"}], "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", "id"=>"584831918242311_671156409609861", "created_time"=>"2014-04-11T17:35:47+0000", "type"=>"photo", "updated_time"=>"2014-04-11T17:35:47+0000", "privacy"=>{"value"=>""}, "link"=>"https://www.facebook.com/photo.php?fbid=671156066276562&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "object_id"=>"671156066276562", "status_type"=>"added_photos", "message"=>"A #ArenaNETmundial ser\303\241 o nosso ponto de encontro para discutirmos o FUTURO da INTERNET! \n\nVenha conferir os di\303\241logos sobre a DEMOCRACIA na REDE, DIREITOS HUMANOS, PARTICIPA\303\207\303\203O SOCIAL e muito mais! http://ow.ly/vCgb7\n\nSe voc\303\252 ainda n\303\243o confirmou presen\303\247a no evento, corre que ainda d\303\241 tempo! Vamos AGITAR a REDE!\n\n#ParticipaBR", "likes"=>{"data"=>[{"name"=>"Natalia Menhem", "id"=>"501520025"}, {"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"Luiz Claudio Souza", "id"=>"100000088952450"}, {"name"=>"Adriana Martorano", "id"=>"100005369532256"}, {"name"=>"Leo Farias", "id"=>"1475415098"}, {"name"=>"Myrian Conor", "id"=>"100000262033394"}, {"name"=>"Alexandra Peixoto", "id"=>"749059638"}], "paging"=>{"cursors"=>{"after"=>"NzQ5MDU5NjM4", "before"=>"NTAxNTIwMDI1"}}}, "picture"=>"https://fbcdn-photos-f-a.akamaihd.net/hphotos-ak-frc3/t1.0-0/10151967_671156066276562_1040945857784462133_s.png"}
  17 +{"shares"=>{"count"=>3}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/671092979616204"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/671092979616204"}], "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", "id"=>"584831918242311_671092979616204", "created_time"=>"2014-04-11T14:15:00+0000", "type"=>"photo", "updated_time"=>"2014-04-11T14:15:00+0000", "privacy"=>{"value"=>""}, "link"=>"https://www.facebook.com/photo.php?fbid=670251459700356&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "object_id"=>"670251459700356", "status_type"=>"added_photos", "message"=>"Agora a LUTA \303\251 no Senado!\n\n#MarcoCivil: Senado far\303\241 tr\303\252s audi\303\252ncias p\303\272blicas!\n\nSer\303\243o convidados para o debate representantes da Anatel, do CGI, do Sindtelebrasil e o Idec. As audi\303\252ncias est\303\243o previstas para os dias 10, 15 e 22 de abril. Saiba mais: http://ow.ly/vClKH\n\n#NaM\303\255dia #ParticipaBR", "likes"=>{"data"=>[{"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"Alexandre Arns Gonzales", "id"=>"100001822508049"}, {"name"=>"Vanessa Ferreira", "id"=>"100008128234636"}, {"name"=>"MP LN", "id"=>"100000053156433"}, {"name"=>"Carlos Lobo", "id"=>"763893285"}, {"name"=>"Leo Farias", "id"=>"1475415098"}, {"name"=>"Janaina Spode", "id"=>"579251019"}, {"name"=>"Adriana Martorano", "id"=>"100005369532256"}, {"name"=>"Myrian Conor", "id"=>"100000262033394"}, {"name"=>"Alexandra Peixoto", "id"=>"749059638"}], "paging"=>{"cursors"=>{"after"=>"NzQ5MDU5NjM4", "before"=>"MTAwMDAwNjY0OTYyNTQ0"}}}, "picture"=>"https://fbcdn-photos-d-a.akamaihd.net/hphotos-ak-prn2/t1.0-0/10258237_670251459700356_7951669488914497992_s.jpg"}
  18 +{"shares"=>{"count"=>11}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/671067426285426"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/671067426285426"}], "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", "to"=>{"data"=>[{"name"=>"Elizabeth Gilbert", "category"=>"Writer", "id"=>"227291194019670"}, {"name"=>"Be\303\241 Tibiri\303\247\303\241", "id"=>"100001756483405"}, {"name"=>"Javier Toret Medina", "id"=>"1221646905"}, {"name"=>"Sergio Amadeu da Silveira", "id"=>"752385273"}]}, "id"=>"584831918242311_671067426285426", "created_time"=>"2014-04-11T13:00:01+0000", "type"=>"photo", "updated_time"=>"2014-04-11T13:00:01+0000", "privacy"=>{"value"=>""}, "link"=>"https://www.facebook.com/photo.php?fbid=670811722977663&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "message_tags"=>{"229"=>[{"name"=>"Sergio Amadeu", "id"=>"752385273", "type"=>"user", "length"=>13, "offset"=>229}], "164"=>[{"name"=>"Elizabeth Gilbert", "id"=>"227291194019670", "type"=>"page", "length"=>17, "offset"=>164}], "200"=>[{"name"=>"Be\303\241 Tibiri\303\247\303\241", "id"=>"100001756483405", "type"=>"user", "length"=>12, "offset"=>200}], "214"=>[{"name"=>"Javier Toret", "id"=>"1221646905", "type"=>"user", "length"=>12, "offset"=>214}]}, "object_id"=>"670811722977663", "status_type"=>"added_photos", "message"=>"Veja quem s\303\243o os debatedores que j\303\241 CONFIRMARAM presen\303\247a na #ArenaNETmundial, o evento que vai sacudir a REDE, de 22 a 24 de abril, no Centro Cultural S\303\243o Paulo! \n\nElizabeth Gilbert, Tim Berners-Lee, Be\303\241 Tibiri\303\247\303\241, Javier Toret e Sergio Amadeu s\303\243o alguns dos GRANDES nomes confirmados! Quer saber mais sobre os debatedores? \303\211 s\303\263 CLICAR aqui -> http://ow.ly/vEPfV\n\nCONFIRME sua presen\303\247a no evento e venha discutir a GOVERNAN\303\207A da REDE! http://bit.ly/ArenaNETmundial!\n\n#ParticipaBR", "likes"=>{"data"=>[{"name"=>"Murutinga Abaetetuba Murutinga", "id"=>"100008004282653"}, {"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"Tatiana Sim\303\265es Sottili", "id"=>"1642810466"}, {"name"=>"Javier Toret Medina", "id"=>"1221646905"}, {"name"=>"Lucio Uberdan", "id"=>"685024343"}, {"name"=>"Allix Lis Rodrigues", "id"=>"624623048"}, {"name"=>"Nanda Barreto", "id"=>"100000525584317"}, {"name"=>"Alexandra Peixoto", "id"=>"749059638"}, {"name"=>"Janaina Spode", "id"=>"579251019"}, {"name"=>"Adriana Martorano", "id"=>"100005369532256"}, {"name"=>"Myrian Conor", "id"=>"100000262033394"}], "paging"=>{"cursors"=>{"after"=>"MTAwMDAwMjYyMDMzMzk0", "before"=>"MTAwMDA4MDA0MjgyNjUz"}}}, "picture"=>"https://fbcdn-photos-a-a.akamaihd.net/hphotos-ak-frc3/t1.0-0/1466021_670811722977663_2539462382863559512_s.png"}
  19 +{"shares"=>{"count"=>6}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/671044212954414"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/671044212954414"}], "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", "id"=>"584831918242311_671044212954414", "created_time"=>"2014-04-11T11:30:00+0000", "type"=>"photo", "updated_time"=>"2014-04-11T11:30:00+0000", "privacy"=>{"value"=>""}, "link"=>"https://www.facebook.com/photo.php?fbid=670808672977968&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "object_id"=>"670808672977968", "status_type"=>"added_photos", "message"=>"BOM DIA pra voc\303\252 que quer revolucionar a INTERNET! \n\nE que tal come\303\247ar esta revolu\303\247\303\243o dando ideias e enviando propostas para mudarmos o FUTURO da REDE? Acesse a nossa consulta p\303\272blica e deixe a sua sugest\303\243o! http://www.participa.br/\n\nA #ArenaNETmundial quer ouvir a sua opini\303\243o: QUE INTERNET VOC\303\212 QUER? \n\n#ParticipaBR", "likes"=>{"data"=>[{"name"=>"Renato Cortez", "id"=>"100000144194858"}, {"name"=>"Izabela Matos Chaves", "id"=>"100004256850261"}, {"name"=>"Raquel Le\303\243es", "id"=>"100000163559078"}, {"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"Vanessa Ferreira", "id"=>"100008128234636"}, {"name"=>"Myrian Conor", "id"=>"100000262033394"}, {"name"=>"Alexandra Peixoto", "id"=>"749059638"}, {"name"=>"Nathalia Sautchuk Patricio", "id"=>"662335299"}, {"name"=>"Carola Oliveira", "id"=>"1160276837"}, {"name"=>"Luiz Augusto Lisb\303\264a", "id"=>"100002956996510"}, {"name"=>"Janaina Spode", "id"=>"579251019"}, {"name"=>"Elly Marlley", "id"=>"100005663351771"}, {"name"=>"Participabr", "id"=>"584831918242311"}, {"name"=>"Yuri Almeida", "id"=>"551947517"}, {"name"=>"MP LN", "id"=>"100000053156433"}], "paging"=>{"cursors"=>{"after"=>"MTAwMDAwMDUzMTU2NDMz", "before"=>"MTAwMDAwMTQ0MTk0ODU4"}}}, "picture"=>"https://fbcdn-photos-g-a.akamaihd.net/hphotos-ak-prn2/t1.0-0/10007468_670808672977968_7995649865195786401_s.jpg"}
  20 +{"shares"=>{"count"=>12}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/670853929640109"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/670853929640109"}], "comments"=>{"data"=>[{"like_count"=>1, "user_likes"=>false, "can_remove"=>false, "from"=>{"name"=>"Evelyn Araripe", "id"=>"100003010308675"}, "id"=>"670805622978273_2156946", "created_time"=>"2014-04-11T17:57:19+0000", "message"=>"T\303\241 ficando muito boa essa programa\303\247\303\243o!!!"}], "paging"=>{"cursors"=>{"after"=>"MQ==", "before"=>"MQ=="}}}, "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", "to"=>{"data"=>[{"name"=>"Centro Cultural S\303\243o Paulo", "category"=>"Public places", "id"=>"320942385642", "category_list"=>[{"name"=>"Art Gallery", "id"=>"197384240287028"}, {"name"=>"Theatre", "id"=>"173883042668223"}, {"name"=>"Library", "id"=>"169896676390222"}]}, {"name"=>"Tom Z\303\251", "category"=>"Musician/band", "id"=>"26030117065"}, {"name"=>"Fernando Anitelli", "category"=>"Musician/band", "id"=>"146916915375515"}, {"name"=>"Emicida", "category"=>"Musician/band", "id"=>"145578725499808"}, {"name"=>"Jorge Mautner", "category"=>"Musician/band", "id"=>"24050959127"}]}, "id"=>"584831918242311_670853929640109", "created_time"=>"2014-04-11T00:00:01+0000", "type"=>"photo", "updated_time"=>"2014-04-11T17:57:19+0000", "privacy"=>{"value"=>""}, "link"=>"https://www.facebook.com/photo.php?fbid=670805622978273&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "message_tags"=>{"206"=>[{"name"=>"Tom Z\303\251", "id"=>"26030117065", "type"=>"page", "length"=>6, "offset"=>206}], "243"=>[{"name"=>"Jorge Mautner", "id"=>"24050959127", "type"=>"page", "length"=>13, "offset"=>243}], "233"=>[{"name"=>"Emicida", "id"=>"145578725499808", "type"=>"page", "length"=>7, "offset"=>233}], "157"=>[{"name"=>"Centro Cultural S\303\243o Paulo", "id"=>"320942385642", "type"=>"page", "length"=>25, "offset"=>157}], "214"=>[{"name"=>"Fernando Anitelli", "id"=>"146916915375515", "type"=>"page", "length"=>17, "offset"=>214}]}, "object_id"=>"670805622978273", "status_type"=>"added_photos", "message"=>"A CULTURA DIGITAL TEM ESPA\303\207O NOS SHOWS DA #ArenaNETmundial!\n\nPara instigar a criatividade coletiva, a #ArenaNETmundial abre espa\303\247o para atra\303\247\303\265es musicais no Centro Cultural S\303\243o Paulo, de 22 a 24 de abril!\n\nTom Z\303\251, Fernando Anitelli, Emicida e Jorge Mautner v\303\243o fechar a rotina di\303\241ria de di\303\241logos e oficinas do evento! Se voc\303\252 est\303\241 curios@ para saber a programa\303\247\303\243o, n\303\243o perca tempo, acesse: http://ow.ly/vFaAu\n\n#ParticipaBR", "likes"=>{"data"=>[{"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"Pepe Martini", "id"=>"1254030646"}, {"name"=>"Evelyn Araripe", "id"=>"100003010308675"}, {"name"=>"Fernando Caixeta", "id"=>"100000103555498"}, {"name"=>"Alan Collyer", "id"=>"100003584527506"}, {"name"=>"Laila Bellix", "id"=>"679318319"}, {"name"=>"Participabr", "id"=>"584831918242311"}, {"name"=>"Rodolfo Tokimatsu", "id"=>"100003197560595"}, {"name"=>"B Tany B. Tany", "id"=>"100000308845954"}, {"name"=>"Reinaldo Castro", "id"=>"1265463106"}, {"name"=>"Nanda Barreto", "id"=>"100000525584317"}, {"name"=>"Adriana Martorano", "id"=>"100005369532256"}, {"name"=>"Talita Nogueira", "id"=>"100001714757945"}, {"name"=>"Renilson Dur\303\243es", "id"=>"1665215750"}, {"name"=>"Janaina Spode", "id"=>"579251019"}, {"name"=>"Mariana Heinz", "id"=>"100001699022773"}, {"name"=>"Wellington Par\303\241", "id"=>"100000408605032"}, {"name"=>"Yuri Almeida", "id"=>"551947517"}, {"name"=>"Christiano Cavalcanti", "id"=>"100001984914972"}, {"name"=>"Alexandra Peixoto", "id"=>"749059638"}, {"name"=>"Fernando Silva Soares Silva", "id"=>"1578450483"}, {"name"=>"Henrique Parra Parra Filho", "id"=>"100000747904691"}, {"name"=>"D\303\251bora Lessa", "id"=>"680508753"}], "paging"=>{"cursors"=>{"after"=>"NjgwNTA4NzUz", "before"=>"MTAwMDAwNjY0OTYyNTQ0"}}}, "picture"=>"https://fbcdn-photos-e-a.akamaihd.net/hphotos-ak-ash3/t1.0-0/10155378_670805622978273_7293058947948255933_s.jpg"}
  21 +{"actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/670799439645558"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/670799439645558"}], "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", "id"=>"584831918242311_670799439645558", "created_time"=>"2014-04-10T22:00:00+0000", "type"=>"photo", "updated_time"=>"2014-04-10T22:00:00+0000", "privacy"=>{"value"=>""}, "link"=>"https://www.facebook.com/photo.php?fbid=670783762980459&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "object_id"=>"670783762980459", "status_type"=>"added_photos", "message"=>"Da #ArenaNETmundial para o MUNDO!\n\nColuna do Estad\303\243o fala sobre a participa\303\247\303\243o do Tim Berners-Lee no evento que vai AGITAR a REDE neste m\303\252s! http://ow.ly/vEPUy\n\n#NaM\303\255dia #ParticipaBR", "likes"=>{"data"=>[{"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"Johnny Pequeno", "id"=>"100001773781004"}, {"name"=>"Alexandra Peixoto", "id"=>"749059638"}, {"name"=>"Rafael Pops Barbosa Moraes", "id"=>"1533873688"}, {"name"=>"Fernando Silva Soares Silva", "id"=>"1578450483"}, {"name"=>"Carlos Lobo", "id"=>"763893285"}], "paging"=>{"cursors"=>{"after"=>"NzYzODkzMjg1", "before"=>"MTAwMDAwNjY0OTYyNTQ0"}}}, "picture"=>"https://fbcdn-photos-b-a.akamaihd.net/hphotos-ak-prn2/t1.0-0/10171045_670783762980459_9192964314133703634_s.jpg"}
  22 +{"shares"=>{"count"=>17}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/670756449649857"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/670756449649857"}], "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", "to"=>{"data"=>[{"name"=>"Centro Cultural S\303\243o Paulo", "category"=>"Public places", "id"=>"320942385642", "category_list"=>[{"name"=>"Art Gallery", "id"=>"197384240287028"}, {"name"=>"Theatre", "id"=>"173883042668223"}, {"name"=>"Library", "id"=>"169896676390222"}]}]}, "id"=>"584831918242311_670756449649857", "created_time"=>"2014-04-10T19:22:39+0000", "type"=>"photo", "updated_time"=>"2014-04-10T19:22:39+0000", "privacy"=>{"value"=>""}, "link"=>"https://www.facebook.com/photo.php?fbid=670756402983195&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "message_tags"=>{"86"=>[{"name"=>"Centro Cultural S\303\243o Paulo", "id"=>"320942385642", "type"=>"page", "length"=>25, "offset"=>86}]}, "object_id"=>"670756402983195", "status_type"=>"added_photos", "message"=>"\303\232LTIMA CHAMADA PARA O ENCONTRO ABERTO DA #ArenaNETmundial!\n\nAmanh\303\243, das 15 \303\240s 17h, no Centro Cultural S\303\243o Paulo, acontece o \303\272ltimo encontro aberto para a constru\303\247\303\243o da AGENDA do evento!\n\nBuscamos um esfor\303\247o COLETIVO para preservar o processo ABERTO, PARTICIPATIVO e DEMOCR\303\201TICO! N\303\243o fique de fora! PARTICIPE! SAIBA mais -> http://ow.ly/vEMvs\n\n#MarcaNaAgenda #ParticipaBR", "likes"=>{"data"=>[{"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"Pepe Martini", "id"=>"1254030646"}, {"name"=>"Lia Paul", "id"=>"100005140505167"}, {"name"=>"Thiago Sk\303\241rnio", "id"=>"100000805004346"}, {"name"=>"Rosely Andreassa", "id"=>"100003614211549"}, {"name"=>"Alexandra Peixoto", "id"=>"749059638"}, {"name"=>"Adriana Martorano", "id"=>"100005369532256"}, {"name"=>"Jo\303\243o Baptista Pimentel Neto", "id"=>"1551520480"}, {"name"=>"Kely Rego", "id"=>"100000530478123"}, {"name"=>"Nanda Barreto", "id"=>"100000525584317"}, {"name"=>"Marcelo Branco", "id"=>"1260145727"}, {"name"=>"Everton Rodrigues", "id"=>"832259597"}, {"name"=>"Douglas Freitas", "id"=>"100001187262990"}, {"name"=>"Carlos Lobo", "id"=>"763893285"}, {"name"=>"Myrian Conor", "id"=>"100000262033394"}], "paging"=>{"cursors"=>{"after"=>"MTAwMDAwMjYyMDMzMzk0", "before"=>"MTAwMDAwNjY0OTYyNTQ0"}}}, "picture"=>"https://fbcdn-photos-c-a.akamaihd.net/hphotos-ak-frc1/t1.0-0/10151311_670756402983195_7092075331556696204_s.png"}
  23 +{"shares"=>{"count"=>25}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/670664639659038"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/670664639659038"}], "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", "id"=>"584831918242311_670664639659038", "created_time"=>"2014-04-10T15:49:18+0000", "type"=>"photo", "updated_time"=>"2014-04-10T15:49:18+0000", "privacy"=>{"value"=>""}, "link"=>"https://www.facebook.com/photo.php?fbid=670663609659141&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "object_id"=>"670663609659141", "status_type"=>"added_photos", "message"=>"HOJE, \303\240s 14 tem TUITA\303\207O da #ArenaNETmundial! \n\nUse a hashtag #ArenaNETmundial e vamos invadir o Twitter! Acompanhe, participe e fique por dentro de tudo o que rolar\303\241 no evento! \n\n#ParticipaBR", "likes"=>{"data"=>[{"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"Fernando Silva Soares Silva", "id"=>"1578450483"}, {"name"=>"Rosely Andreassa", "id"=>"100003614211549"}, {"name"=>"Adriana Martorano", "id"=>"100005369532256"}, {"name"=>"Usuarios de Internet del Ecuador", "id"=>"96481988151"}, {"name"=>"Manoel Lages Filho", "id"=>"100000020201492"}, {"name"=>"Everton Rodrigues", "id"=>"832259597"}, {"name"=>"Marcelo Branco", "id"=>"1260145727"}, {"name"=>"Nanda Barreto", "id"=>"100000525584317"}, {"name"=>"Myrian Conor", "id"=>"100000262033394"}, {"name"=>"Carlos Lobo", "id"=>"763893285"}, {"name"=>"Leoncio Nascimento", "id"=>"100000680460377"}, {"name"=>"Janaina Spode", "id"=>"579251019"}, {"name"=>"Thiago Sk\303\241rnio", "id"=>"100000805004346"}], "paging"=>{"cursors"=>{"after"=>"MTAwMDAwODA1MDA0MzQ2", "before"=>"MTAwMDAwNjY0OTYyNTQ0"}}}, "picture"=>"https://fbcdn-photos-h-a.akamaihd.net/hphotos-ak-frc3/t1.0-0/1558385_670663609659141_6538204679475681502_s.jpg"}
  24 +{"shares"=>{"count"=>76}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/670625762996259"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/670625762996259"}], "comments"=>{"data"=>[{"like_count"=>5, "user_likes"=>false, "can_remove"=>false, "from"=>{"name"=>"Henrique Parra Parra Filho", "id"=>"100000747904691"}, "id"=>"670619366330232_2155057", "created_time"=>"2014-04-10T13:52:01+0000", "message_tags"=>[{"name"=>"Frederico Bortolato", "id"=>"1289479599", "type"=>"user", "length"=>19, "offset"=>242}, {"name"=>"Ricardo Poppi", "id"=>"100000099352333", "type"=>"user", "length"=>13, "offset"=>264}], "message"=>"Tim Berners-Lee, Manuel Castells e Gilberto Gil \303\251 programa\303\247\303\243o das mais interessantes. Tom Z\303\251, Fernando Anitelli, Emicida e Jorge Mautner \303\251 balada das melhores. N\303\243o sei se chamo de festa ou de aprendizado, mas sei que n\303\243o d\303\241 para perder heim Frederico Bortolato e Ricardo Poppi! ;)"}, {"like_count"=>3, "user_likes"=>false, "can_remove"=>false, "from"=>{"name"=>"Frederico Bortolato", "id"=>"1289479599"}, "id"=>"670619366330232_2155222", "created_time"=>"2014-04-10T15:17:19+0000", "message_tags"=>[{"name"=>"Henrique", "id"=>"100000747904691", "type"=>"user", "length"=>8, "offset"=>22}], "message"=>"Sem chance de perder, Henrique!"}, {"like_count"=>2, "user_likes"=>false, "can_remove"=>false, "from"=>{"name"=>"Lorac Mustaine", "id"=>"1083326546"}, "id"=>"670619366330232_2155143", "created_time"=>"2014-04-10T14:29:48+0000", "message"=>"Agora come\303\247ou ficar interessante... =)"}, {"like_count"=>1, "user_likes"=>false, "can_remove"=>false, "from"=>{"name"=>"Nestor Ribas Leite Filho", "id"=>"100003572810075"}, "id"=>"670619366330232_2155778", "created_time"=>"2014-04-10T21:59:28+0000", "message"=>"ISTO \303\211 JOIA RARA"}, {"like_count"=>1, "user_likes"=>false, "can_remove"=>false, "from"=>{"name"=>"Fa Conti", "id"=>"600679756"}, "id"=>"670619366330232_2155755", "created_time"=>"2014-04-10T21:27:47+0000", "message"=>"oba!"}, {"like_count"=>0, "user_likes"=>false, "can_remove"=>false, "from"=>{"name"=>"Ana Mirtes Fouro", "id"=>"100002265340422"}, "id"=>"670619366330232_2156470", "created_time"=>"2014-04-11T10:33:57+0000", "message"=>"(Y)"}, {"like_count"=>0, "user_likes"=>false, "can_remove"=>false, "from"=>{"name"=>"Ri At Uw", "id"=>"1071851267"}, "id"=>"670619366330232_2156197", "created_time"=>"2014-04-11T03:38:06+0000", "message_tags"=>[{"name"=>"Javier", "id"=>"528937139", "type"=>"user", "length"=>6, "offset"=>11}, {"name"=>"Otto", "id"=>"100002294062950", "type"=>"user", "length"=>4, "offset"=>18}, {"name"=>"Daniel", "id"=>"100000218054052", "type"=>"user", "length"=>6, "offset"=>23}, {"name"=>"Vitor", "id"=>"100002236271320", "type"=>"user", "length"=>5, "offset"=>30}], "message"=>"Vamos n\303\251?! Javier Otto Daniel Vitor"}, {"like_count"=>1, "user_likes"=>false, "can_remove"=>false, "from"=>{"name"=>"Lincoln Aoki", "id"=>"100000049366725"}, "id"=>"670619366330232_2155726", "created_time"=>"2014-04-10T20:59:38+0000", "message"=>"OK OK .. Valeu mestre Sergio Amadeu. A Internet n\303\243o pode ter dono, a Internet \303\251 do povo, precisamos ter mais democracia na era Digital. O Brasil j\303\241 supera mais linhas de celular habilitada que o n\303\272mero de habitantes do pa\303\255s. Estamos na era digital, por isso, o povo brasileiro precisa ter pelo menos acesso a Internet com pre\303\247os mais acess\303\255veis."}], "paging"=>{"cursors"=>{"after"=>"MQ==", "before"=>"OA=="}}}, "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", "to"=>{"data"=>[{"name"=>"Tim Berners-Lee", "category"=>"Public figure", "id"=>"108177502537793"}, {"name"=>"Manuel Castells", "category"=>"Author", "id"=>"112735342073078"}, {"name"=>"Gilberto Gil", "category"=>"Musician/band", "id"=>"104152332963030"}, {"name"=>"Tom Z\303\251", "category"=>"Musician/band", "id"=>"26030117065"}, {"name"=>"Fernando Anitelli", "category"=>"Musician/band", "id"=>"132110136857273"}, {"name"=>"Emicida", "category"=>"Musician/band", "id"=>"145578725499808"}, {"name"=>"Jorge Mautner", "category"=>"Musician/band", "id"=>"24050959127"}]}, "id"=>"584831918242311_670625762996259", "created_time"=>"2014-04-10T13:49:53+0000", "type"=>"photo", "updated_time"=>"2014-04-11T10:33:57+0000", "privacy"=>{"value"=>""}, "link"=>"https://www.facebook.com/photo.php?fbid=670619366330232&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "message_tags"=>{"534"=>[{"name"=>"Jorge Mautner", "id"=>"24050959127", "type"=>"page", "length"=>13, "offset"=>534}], "524"=>[{"name"=>"Emicida", "id"=>"145578725499808", "type"=>"page", "length"=>7, "offset"=>524}], "35"=>[{"name"=>"Gilberto Gil", "id"=>"104152332963030", "type"=>"page", "length"=>12, "offset"=>35}], "505"=>[{"name"=>"Fernando Anitelli", "id"=>"132110136857273", "type"=>"page", "length"=>17, "offset"=>505}], "0"=>[{"name"=>"Tim Berners-Lee", "id"=>"108177502537793", "type"=>"page", "length"=>15, "offset"=>0}], "17"=>[{"name"=>"Manuel Castells", "id"=>"112735342073078", "type"=>"page", "length"=>15, "offset"=>17}], "497"=>[{"name"=>"Tom Z\303\251", "id"=>"26030117065", "type"=>"page", "length"=>6, "offset"=>497}]}, "object_id"=>"670619366330232", "status_type"=>"added_photos", "message"=>"Tim Berners-Lee, Manuel Castells e Gilberto Gil juntos, para discutir os rumos da Internet no Brasil e no mundo, a democracia na era digital e a sociedade em rede?? \n\n#Sim e al\303\251m dessas personalidades, a #ArenaNETmundial #ParticipaBR trar\303\241 outros grandes pensadores, ativistas, gestores p\303\272blicos e comunicadores de diversos pa\303\255ses. Para unir a cultura popular com a cultura digital, grandes artistas que s\303\243o ativistas do conhecimento livre far\303\243o apresenta\303\247\303\265es musicais no evento. Entre eles est\303\243o Tom Z\303\251, Fernando Anitelli, Emicida e Jorge Mautner. T\303\241 bom pra caramba! ;) \n\nVenha discutir o futuro da internet na #ArenaNETmundial! Confirme sua presen\303\247a no evento! http://bit.ly/ArenaNETmundial", "likes"=>{"data"=>[{"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"Esmeralda Medeiros", "id"=>"100007760560534"}, {"name"=>"Nonatto Vieira", "id"=>"100003632488645"}, {"name"=>"Luiz S\303\251rgio Pires Guimar\303\243es", "id"=>"100004080441032"}, {"name"=>"Violeta Cross", "id"=>"100000784826227"}, {"name"=>"Tais Falc\303\243o", "id"=>"100001393874437"}, {"name"=>"Mary Oliveira", "id"=>"100006774454711"}, {"name"=>"Jane Celia Santos", "id"=>"100004278401016"}, {"name"=>"Eliana Santos", "id"=>"100004708982012"}, {"name"=>"Rodrigo Mantovani", "id"=>"100004036951692"}, {"name"=>"Aluizio Matias Santos", "id"=>"100000096283728"}, {"name"=>"Rosa Meire", "id"=>"1654472213"}, {"name"=>"Alzira Higino", "id"=>"100000838728858"}, {"name"=>"Carolina Rovai", "id"=>"1032733783"}, {"name"=>"Maria Barbosa Aparecida", "id"=>"100007490796202"}, {"name"=>"Pepe Martini", "id"=>"1254030646"}, {"name"=>"Everton Rodrigues", "id"=>"832259597"}, {"name"=>"S\303\251rgio Ant\303\264nio Mota Furtado", "id"=>"100003767543014"}, {"name"=>"Zeca Teodoro", "id"=>"1679641558"}, {"name"=>"Rafael Araujo", "id"=>"100004001539572"}, {"name"=>"Karoline Holanda", "id"=>"100000236542676"}, {"name"=>"Monick Passos", "id"=>"100007925590237"}, {"name"=>"Ana Maria Riva da Silva", "id"=>"100005727553477"}, {"name"=>"Luanacristina Simoes", "id"=>"100004173271257"}, {"name"=>"Iracema Ferreira", "id"=>"100005909612048"}], "paging"=>{"next"=>"https://graph.facebook.com/584831918242311_670625762996259/likes?access_token=CAAD8cd4tMVkBAO3sh2DrzwZCDfeQq9ZAvTz7Jz24ZC26KtMfBoljqaXhD2vBV1zpP0bjrpxXUBzJvKKcFzOm6rMG9Sok7iNVUaxt5iwr7dfMqCvHpMboKpqrqgeLrfCH5ITVTAdezA6ZBSr9iOJrqyCSOYfui0zTmbXJ3FqtshwNRrRy4NPH&limit=25&after=MTAwMDA1OTA5NjEyMDQ4", "cursors"=>{"after"=>"MTAwMDA1OTA5NjEyMDQ4", "before"=>"MTAwMDAwNjY0OTYyNTQ0"}}}, "picture"=>"https://fbcdn-photos-a-a.akamaihd.net/hphotos-ak-prn2/t1.0-0/1982044_670619366330232_6311457266477121189_s.jpg"}
  25 +{"story"=>"Participabr posted a link to F\303\263rum Internacional de Software Livre (FISL)'s timeline.", "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "story_tags"=>{"0"=>[{"name"=>"Participabr", "id"=>"584831918242311", "type"=>"page", "length"=>11, "offset"=>0}], "29"=>[{"name"=>"F\303\263rum Internacional de Software Livre (FISL)", "id"=>"53901836593", "type"=>"page", "length"=>44, "offset"=>29}]}, "id"=>"584831918242311_670616209663881", "created_time"=>"2014-04-10T13:12:38+0000", "type"=>"status", "updated_time"=>"2014-04-10T13:12:38+0000", "privacy"=>{"value"=>""}}
  26 +{"story"=>"Participabr posted a link to FNDC Democratiza\303\247\303\243o da Comunica\303\247\303\243o's timeline.", "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "story_tags"=>{"0"=>[{"name"=>"Participabr", "id"=>"584831918242311", "type"=>"page", "length"=>11, "offset"=>0}], "29"=>[{"name"=>"FNDC Democratiza\303\247\303\243o da Comunica\303\247\303\243o", "id"=>"645544058821361", "type"=>"page", "length"=>34, "offset"=>29}]}, "id"=>"584831918242311_670616169663885", "created_time"=>"2014-04-10T13:12:29+0000", "type"=>"status", "updated_time"=>"2014-04-10T13:12:29+0000", "privacy"=>{"value"=>""}}
  27 +{"story"=>"\"Hoje sai a programa\303\247\303\243o completa com os debatedores da #ArenaNETMundial! Logo mais compartilharemos em nossa p\303\241gina e aqui no face! Um time de peso e antenado com as principais quest\303\265es sobre governan\303\247a da internet, desafios no Brasil e as novas formas de participa\303\247\303\243o pol\303\255tica que emergem com a internet! Contamos com ajuda para divulgar! http://www.participa.br/arena/arena-net-mundial/dialogos\n\" on CISL's timeline.", "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "story_tags"=>{"402"=>[{"name"=>"CISL", "id"=>"432297343526878", "type"=>"page", "length"=>4, "offset"=>402}]}, "id"=>"584831918242311_670616156330553", "created_time"=>"2014-04-10T13:12:27+0000", "type"=>"status", "updated_time"=>"2014-04-10T13:12:27+0000", "privacy"=>{"value"=>""}, "status_type"=>"wall_post"}
  28 +{"story"=>"Participabr posted a link to CISL's timeline.", "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "story_tags"=>{"0"=>[{"name"=>"Participabr", "id"=>"584831918242311", "type"=>"page", "length"=>11, "offset"=>0}], "29"=>[{"name"=>"CISL", "id"=>"432297343526878", "type"=>"page", "length"=>4, "offset"=>29}]}, "id"=>"584831918242311_670616082997227", "created_time"=>"2014-04-10T13:12:05+0000", "type"=>"status", "updated_time"=>"2014-04-10T13:12:05+0000", "privacy"=>{"value"=>""}}
  29 +{"story"=>"Participabr posted a link to Intervozes's timeline.", "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "story_tags"=>{"0"=>[{"name"=>"Participabr", "id"=>"584831918242311", "type"=>"page", "length"=>11, "offset"=>0}], "29"=>[{"name"=>"Intervozes", "id"=>"266806120012638", "type"=>"page", "length"=>10, "offset"=>29}]}, "id"=>"584831918242311_670616066330562", "created_time"=>"2014-04-10T13:12:00+0000", "type"=>"status", "updated_time"=>"2014-04-10T13:12:00+0000", "privacy"=>{"value"=>""}}
  30 +{"story"=>"Participabr posted a link to Open Government Partnership's timeline.", "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "story_tags"=>{"0"=>[{"name"=>"Participabr", "id"=>"584831918242311", "type"=>"page", "length"=>11, "offset"=>0}], "29"=>[{"name"=>"Open Government Partnership", "id"=>"194822890570242", "type"=>"page", "length"=>27, "offset"=>29}]}, "id"=>"584831918242311_670616046330564", "created_time"=>"2014-04-10T13:11:58+0000", "type"=>"status", "updated_time"=>"2014-04-10T13:11:58+0000", "privacy"=>{"value"=>""}}
  31 +{"story"=>"\"Hoje sai a programa\303\247\303\243o completa com os debatedores da #ArenaNETMundial! Logo mais compartilharemos em nossa p\303\241gina e aqui no face! Um time de peso e antenado com as principais quest\303\265es sobre governan\303\247a da internet, desafios no Brasil e as novas formas de participa\303\247\303\243o pol\303\255tica que emergem com a internet! Contamos com ajuda para divulgar! http://www.participa.br/arena/arena-net-mundial/dialogos\" on Gabinete Digital's timeline.", "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "story_tags"=>{"401"=>[{"name"=>"Gabinete Digital", "id"=>"175059972609953", "type"=>"page", "length"=>16, "offset"=>401}]}, "id"=>"584831918242311_670615772997258", "created_time"=>"2014-04-10T13:11:00+0000", "type"=>"status", "updated_time"=>"2014-04-10T13:11:00+0000", "privacy"=>{"value"=>""}, "status_type"=>"wall_post"}
  32 +{"story"=>"\"Hoje sai a programa\303\247\303\243o completa com os debatedores da #ArenaNETMundial! Logo mais compartilharemos em nossa p\303\241gina e aqui no face! Um time de peso e antenado com as principais quest\303\265es sobre governan\303\247a da internet, desafios no Brasil e as novas formas de participa\303\247\303\243o pol\303\255tica que emergem com a internet! Contamos com ajuda para divulgar! http://www.participa.br/arena/arena-net-mundial/dialogos\" on Participat\303\263rio's timeline.", "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "story_tags"=>{"401"=>[{"name"=>"Participat\303\263rio", "id"=>"493847487360819", "type"=>"page", "length"=>14, "offset"=>401}]}, "id"=>"584831918242311_670615199663982", "created_time"=>"2014-04-10T13:10:06+0000", "type"=>"status", "updated_time"=>"2014-04-10T13:10:06+0000", "privacy"=>{"value"=>""}, "status_type"=>"wall_post"}
  33 +{"story"=>"\"Hoje sai a programa\303\247\303\243o completa com os debatedores da #ArenaNETMundial! Logo mais compartilharemos em nossa p\303\241gina e aqui no face! Um time de peso e antenado com as principais quest\303\265es sobre governan\303\247a da internet, desafios no Brasil e as novas formas de participa\303\247\303\243o pol\303\255tica que emergem com a internet! Contamos com ajuda para divulgar! http://www.participa.br/arena/arena-net-mundial/dialogos\" on Meu Rio's timeline.", "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "story_tags"=>{"401"=>[{"name"=>"Meu Rio", "id"=>"241897672509479", "type"=>"page", "length"=>7, "offset"=>401}]}, "id"=>"584831918242311_670615149663987", "created_time"=>"2014-04-10T13:09:55+0000", "type"=>"status", "updated_time"=>"2014-04-10T13:09:55+0000", "privacy"=>{"value"=>""}, "status_type"=>"wall_post"}
  34 +{"shares"=>{"count"=>18}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/670609036331265"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/670609036331265"}], "name"=>"Dilma anunciar\303\241 \342\200\230Marco Civil mundial\342\200\231 em evento na capital paulista", "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "application"=>{"name"=>"Links", "id"=>"2309869772"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yD/r/aS8ecmYRys0.gif", "id"=>"584831918242311_670609036331265", "created_time"=>"2014-04-10T12:47:11+0000", "type"=>"link", "caption"=>"blogs.estadao.com.br", "updated_time"=>"2014-04-10T12:47:11+0000", "privacy"=>{"value"=>""}, "description"=>"Presidente abrir\303\241 o NETmundial, evento internacional de governan\303\247a da internet, com o an\303\272ncio do texto que contar\303\241 com princ\303\255pios da internet", "link"=>"http://blogs.estadao.com.br/link/dilma-anunciara-marco-civil-mundial-em-evento-na-capital-paulista/", "status_type"=>"shared_story", "message"=>"Texto que ir\303\241 para a NETmundial, que acontece em S\303\243o Paulo neste m\303\252s, foi vazado pelo Wikileaks e d\303\241 princ\303\255pios para governan\303\247a e uso da internet\n\nJ\303\241 confirmou presen\303\247a no evento? Ent\303\243o n\303\243o perca tempo! http://bit.ly/ArenaNETmundial", "likes"=>{"data"=>[{"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"Luiz Augusto Lisb\303\264a", "id"=>"100002956996510"}, {"name"=>"Celia Maria Rodrigues Silva", "id"=>"100001451747299"}, {"name"=>"Vinicius Russo", "id"=>"1058306283"}, {"name"=>"Uir\303\241 Por\303\243 Maia Do Carmo", "id"=>"582803802"}, {"name"=>"Marcelo Branco", "id"=>"1260145727"}, {"name"=>"Laura De Castro", "id"=>"662017885"}, {"name"=>"Janaina Spode", "id"=>"579251019"}, {"name"=>"Lorac Mustaine", "id"=>"1083326546"}, {"name"=>"Adriana Martorano Comunica\303\247\303\243o", "id"=>"453793871359065"}, {"name"=>"Rosely Arantes", "id"=>"100002116761841"}, {"name"=>"Alex Garcia", "id"=>"100001852385999"}, {"name"=>"Daniel Astone", "id"=>"100000127763008"}, {"name"=>"Carlos Lobo", "id"=>"763893285"}, {"name"=>"Henrique Parra Parra Filho", "id"=>"100000747904691"}, {"name"=>"Myrian Conor", "id"=>"100000262033394"}, {"name"=>"Natascha Castro", "id"=>"1792307671"}], "paging"=>{"cursors"=>{"after"=>"MTc5MjMwNzY3MQ==", "before"=>"MTAwMDAwNjY0OTYyNTQ0"}}}, "picture"=>"https://fbexternal-a.akamaihd.net/safe_image.php?d=AQBeW57V3bbvkwYZ&w=154&h=154&url=http%3A%2F%2Fblogs.estadao.com.br%2Flink%2Ffiles%2F2014%2F02%2FDilma630.jpg"}
  35 +{"story"=>"\";) Agora \303\251 a hora de...\" on their own photo.", "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "application"=>{"name"=>"Photos", "id"=>"2305272732"}, "id"=>"584831918242311_670606639664838", "created_time"=>"2014-04-10T12:38:21+0000", "type"=>"status", "updated_time"=>"2014-04-10T12:38:21+0000", "privacy"=>{"value"=>""}}
  36 +{"story"=>"Participabr likes a photo.", "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "story_tags"=>{"0"=>[{"name"=>"Participabr", "id"=>"584831918242311", "type"=>"page", "length"=>11, "offset"=>0}]}, "id"=>"584831918242311_670606496331519", "created_time"=>"2014-04-10T12:37:45+0000", "type"=>"status", "updated_time"=>"2014-04-10T12:37:45+0000", "privacy"=>{"value"=>""}}
  37 +{"shares"=>{"count"=>4}, "actions"=>[{"name"=>"Comment", "link"=>"https://www.facebook.com/584831918242311/posts/670587879666714"}, {"name"=>"Like", "link"=>"https://www.facebook.com/584831918242311/posts/670587879666714"}], "comments"=>{"data"=>[{"like_count"=>1, "user_likes"=>false, "can_remove"=>false, "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "id"=>"670251893033646_2154947", "created_time"=>"2014-04-10T12:38:21+0000", "message"=>";) Agora \303\251 a hora de aproveitarmos os bons ventos e impulsionar uma #Arena transformadora!!"}], "paging"=>{"cursors"=>{"after"=>"MQ==", "before"=>"MQ=="}}}, "from"=>{"name"=>"Participabr", "category"=>"Community", "id"=>"584831918242311"}, "icon"=>"https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif", "id"=>"584831918242311_670587879666714", "created_time"=>"2014-04-10T11:30:00+0000", "type"=>"photo", "updated_time"=>"2014-04-10T12:38:21+0000", "privacy"=>{"value"=>""}, "link"=>"https://www.facebook.com/photo.php?fbid=670251893033646&set=a.611370448921791.1073741828.584831918242311&type=1&relevant_count=1", "object_id"=>"670251893033646", "status_type"=>"added_photos", "message"=>"BOM DIA pra voc\303\252 que nos acompanha! \n\nO futuro da INTERNET est\303\241 em nossas m\303\243os e \303\251 nossa responsabilidade lutar por uma rede LIVRE, COLABORATIVA e PLURAL! \n\nVenha debater a governan\303\247a da rede e vamos, juntos, construir a internet que queremos! \n\nConfirma presen\303\247a l\303\241! http://bit.ly/ArenaNETmundial\n\n#ArenaNETmundial #ParticipaBR", "likes"=>{"data"=>[{"name"=>"Murutinga Abaetetuba Murutinga", "id"=>"100008004282653"}, {"name"=>"Robson B. Sampaio", "id"=>"100000664962544"}, {"name"=>"Elly Marlley", "id"=>"100005663351771"}, {"name"=>"Nanda Barreto", "id"=>"100000525584317"}, {"name"=>"Maria Eug\303\252nia Mour\303\243o", "id"=>"1588972379"}, {"name"=>"Rosely Arantes", "id"=>"100002116761841"}, {"name"=>"Adriana Martorano Comunica\303\247\303\243o", "id"=>"453793871359065"}, {"name"=>"Janaina Spode", "id"=>"579251019"}, {"name"=>"Alexandra Peixoto", "id"=>"749059638"}, {"name"=>"Participabr", "id"=>"584831918242311"}, {"name"=>"Natascha Castro", "id"=>"1792307671"}, {"name"=>"Myrian Conor", "id"=>"100000262033394"}, {"name"=>"Henrique Parra Parra Filho", "id"=>"100000747904691"}, {"name"=>"Fernando Silva Soares Silva", "id"=>"1578450483"}], "paging"=>{"cursors"=>{"after"=>"MTU3ODQ1MDQ4Mw==", "before"=>"MTAwMDA4MDA0MjgyNjUz"}}}, "picture"=>"https://fbcdn-photos-h-a.akamaihd.net/hphotos-ak-prn1/t1.0-0/10175049_670251893033646_3173567516434002075_s.jpg"}
... ...
plugins/community_hub/lib/community_hub_plugin.rb 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +class CommunityHubPlugin < Noosfero::Plugin
  2 +
  3 + def self.plugin_name
  4 + 'Community Hub'
  5 + end
  6 +
  7 + def self.plugin_description
  8 + _("New kind of content for communities.")
  9 + end
  10 +
  11 + def stylesheet?
  12 + true
  13 + end
  14 +
  15 + def content_types
  16 + if context.respond_to?(:params) && context.params
  17 + types = []
  18 + parent_id = context.params[:parent_id]
  19 + types << CommunityHubPlugin::Hub if context.profile.community? && !parent_id
  20 + types
  21 + else
  22 + [CommunityHubPlugin::Hub]
  23 + end
  24 + end
  25 +
  26 + def content_remove_new(page)
  27 + page.kind_of?(CommunityHubPlugin::Hub)
  28 + end
  29 +
  30 +end
... ...
plugins/community_hub/lib/community_hub_plugin/hub.rb 0 → 100644
... ... @@ -0,0 +1,63 @@
  1 +require File.dirname(__FILE__) + '/../../twitter/stream.rb'
  2 +require File.dirname(__FILE__) + '/../../facebook_stream/lib_facebook_stream'
  3 +
  4 +class CommunityHubPlugin::Hub < Folder
  5 +
  6 + attr_accessible :last_changed_by_id, :integer
  7 +
  8 + attr_accessible :twitter_enabled, :type => :booelan, :default => false
  9 + attr_accessible :twitter_hashtags, :type => :string, :default => ""
  10 + attr_accessible :twitter_consumer_key, :type => :string, :default => ""
  11 + attr_accessible :twitter_consumer_secret, :type => :string, :default => ""
  12 + attr_accessible :twitter_access_token, :type => :string, :default => ""
  13 + attr_accessible :twitter_access_token_secret, :type => :string, :default => ""
  14 + attr_accessible :facebook_enabled, :type => :string, :default => ""
  15 + attr_accessible :facebook_hashtag, :type => :string, :default => ""
  16 + attr_accessible :facebook_access_token, :type => :string, :default => ""
  17 +
  18 + settings_items :twitter_enabled, :type => :boolean, :default => false
  19 + settings_items :twitter_hashtags, :type => :string, :default => ""
  20 + settings_items :twitter_consumer_key, :type => :string, :default => ""
  21 + settings_items :twitter_consumer_secret, :type => :string, :default => ""
  22 + settings_items :twitter_access_token, :type => :string, :default => ""
  23 + settings_items :twitter_access_token_secret, :type => :string, :default => ""
  24 + settings_items :facebook_enabled, :type => :boolean, :default => false
  25 + settings_items :facebook_hashtag, :type => :string, :default => ""
  26 + settings_items :facebook_pooling_time, :type => :integer, :default => 5 # Time in seconds
  27 + settings_items :facebook_access_token, :type => :string, :default => ''
  28 + settings_items :pinned_messages, :type => Array, :default => []
  29 + settings_items :pinned_mediations, :type => Array, :default => []
  30 + settings_items :mediators, :type => Array, :default => []
  31 +
  32 + before_create do |hub|
  33 + hub.mediators = [hub.author.id]
  34 + end
  35 +
  36 + def notify_comments
  37 + false
  38 + end
  39 +
  40 + def self.icon_name(article = nil)
  41 + 'community-hub'
  42 + end
  43 +
  44 + def self.short_description
  45 + _("Hub")
  46 + end
  47 +
  48 + def self.description
  49 + _('Defines a hub.')
  50 + end
  51 +
  52 + def accept_comments?
  53 + true
  54 + end
  55 +
  56 + def view_page
  57 + "content_viewer/hub.rhtml"
  58 + end
  59 +
  60 + def mediator?(user)
  61 + self.author.id == user.id || self.mediators.include?(user.id) ? true : false
  62 + end
  63 +end
... ...
plugins/community_hub/lib/community_hub_plugin/hub_helper.rb 0 → 100644
... ... @@ -0,0 +1,23 @@
  1 +module CommunityHubPlugin::HubHelper
  2 +
  3 + def mediator?(hub)
  4 + logged_in? && (hub.author.id == user.id || hub.mediators.include?(user.id)) ? true : false
  5 + end
  6 +
  7 + def promoted?(hub, user_id)
  8 + logged_in? && (hub.author.id == user_id || hub.mediators.include?(user_id)) ? true : false
  9 + end
  10 +
  11 + def pinned_message?(hub, message_id)
  12 + hub.pinned_messages.include?(message_id) ? true : false
  13 + end
  14 +
  15 + def pinned_mediation?(hub, mediation_id)
  16 + hub.pinned_mediations.include?(mediation_id) ? true : false
  17 + end
  18 +
  19 + def post_time(time)
  20 + _('%{hour}:%{minutes}') % { :hour => time.hour, :minutes => time.strftime("%M") } rescue ''
  21 + end
  22 +
  23 +end
... ...
plugins/community_hub/lib/community_hub_plugin/listener.rb 0 → 100644
... ... @@ -0,0 +1,50 @@
  1 +require 'ftools'
  2 +
  3 +class CommunityHubPlugin::Listener
  4 +
  5 + class << self
  6 +
  7 + def twitter_service(hub)
  8 + listen_twitter_stream(hub, nil)
  9 + end
  10 +
  11 + def facebook_service(hub)
  12 + facebook_comments(hub, nil, hub.facebook_hashtag, hub.facebook_pooling_time, hub.facebook_access_token)
  13 + end
  14 +
  15 + def run
  16 + hubs = {}
  17 + loop do
  18 + log "searching for hubs"
  19 + CommunityHubPlugin::Hub.all.each do |hub|
  20 + hub_conf = hubs[hub.id]
  21 + if hub_conf.nil? || hub_conf[:hub].updated_at < hub.updated_at
  22 + hub_conf[:threads].each {|t| t.terminate} if hub_conf
  23 + log "hub #{hub.id} found!!!!!!"
  24 + threads = []
  25 + threads << Thread.new { twitter_service(hub) } if hub.twitter_enabled
  26 + threads << Thread.new { facebook_service(hub) } if hub.facebook_enabled
  27 + hubs[hub.id] = {:threads => threads, :hub => hub}
  28 + end
  29 + end
  30 + sleep(10)
  31 + end
  32 + end
  33 +
  34 + def initialize_logger
  35 + logdir = File.join(Rails.root, 'log', CommunityHubPlugin::Listener.name.underscore)
  36 + FileUtils.makedirs(logdir) if !File.exist?(logdir)
  37 + logpath = File.join(logdir, "#{ENV['RAILS_ENV']}_#{Time.now.strftime('%F')}.log")
  38 + @logger = Logger.new(logpath)
  39 + end
  40 +
  41 + def log(message)
  42 + #puts message
  43 + initialize_logger unless @initiated
  44 + @initiated ||= true
  45 + @logger << "[#{Time.now.strftime('%F %T %z')}] #{message}\n"
  46 + end
  47 +
  48 + end
  49 +
  50 +end
... ...
plugins/community_hub/lib/community_hub_plugin/mediation.rb 0 → 100644
... ... @@ -0,0 +1,15 @@
  1 +class CommunityHubPlugin::Mediation < Article
  2 +
  3 + before_save do |mediation|
  4 + mediation.advertise = false
  5 + mediation.notify_comments = false
  6 + nil
  7 + end
  8 +
  9 + settings_items :profile_picture, :type => :string, :default => ""
  10 +
  11 + def self.timestamp
  12 + "hub-mediation-#{(Time.now.to_f * 1000).to_i}"
  13 + end
  14 +
  15 +end
... ...
plugins/community_hub/lib/ext/comment.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +require_dependency 'comment'
  2 +
  3 +class Comment
  4 + settings_items :profile_picture, :type => :string, :default => ""
  5 +end
... ...
plugins/community_hub/public/icons/community-hub.png 0 → 100644

442 Bytes

plugins/community_hub/public/icons/hub-arrow-right.png 0 → 100644

349 Bytes

plugins/community_hub/public/icons/hub-not-pinned-icon.png 0 → 100644

772 Bytes

plugins/community_hub/public/icons/hub-not-promote-icon.png 0 → 100644

674 Bytes

plugins/community_hub/public/icons/hub-pinned-icon.png 0 → 100644

772 Bytes

plugins/community_hub/public/icons/hub-promote-icon.png 0 → 100644

674 Bytes

plugins/community_hub/public/icons/hub-remove-icon.png 0 → 100644

567 Bytes

plugins/community_hub/public/icons/hub-samarelo-a.png 0 → 100644

424 Bytes

plugins/community_hub/public/icons/hub-samarelo-b.png 0 → 100644

426 Bytes

plugins/community_hub/public/icons/hub-samarelo.gif 0 → 100644

357 Bytes

plugins/community_hub/public/icons/hub-sverde-a.png 0 → 100644

438 Bytes

plugins/community_hub/public/icons/hub-sverde-b.png 0 → 100644

438 Bytes

plugins/community_hub/public/icons/hub-svermelho-a.png 0 → 100644

373 Bytes

plugins/community_hub/public/icons/hub-svermelho-b.png 0 → 100644

374 Bytes

plugins/community_hub/public/icons/hub-time-bg.gif 0 → 100644

807 Bytes

plugins/community_hub/public/icons/logo_facebook_50x50.png 0 → 100644

905 Bytes

plugins/community_hub/public/icons/logo_twitter_50x50.png 0 → 100644

1.73 KB

plugins/community_hub/public/icons/logo_twitter_bird_blue_50x50.png 0 → 100644

1.08 KB

plugins/community_hub/public/icons/logo_twitter_bird_white_50x50.png 0 → 100644

1.02 KB

plugins/community_hub/public/javascripts/community_hub.js 0 → 100644
... ... @@ -0,0 +1,366 @@
  1 +var latest_post_id = 0;
  2 +var oldest_post_id = 0;
  3 +live_scroll_position = 0;
  4 +var mediations = [];
  5 +var message_interval_id;
  6 +
  7 +function load_more(tab) {
  8 + switch (tab) {
  9 + case 'live':
  10 + load_more_messages();
  11 + break;
  12 + }
  13 +}
  14 +
  15 +
  16 +function load_more_messages() {
  17 + var hub_id = jQuery(".hub").attr('id');
  18 + var oldest_id = jQuery("#live-posts li.post").last().attr("id");
  19 +
  20 + jQuery.ajax({
  21 + url: '/plugin/community_hub/public/older_comments',
  22 + type: 'get',
  23 + data: { oldest_id: oldest_id, hub: hub_id },
  24 + success: function(data) {
  25 + if (data.trim().length > 0) {
  26 + jQuery("#live-posts").append(data);
  27 + }
  28 + },
  29 + error: function(ajax, stat, errorThrown) {
  30 + }
  31 + });
  32 +}
  33 +
  34 +
  35 +function validate_textarea(txt) {
  36 + return (txt.search(/[^\n\s]/)!=-1);
  37 +}
  38 +
  39 +
  40 +function toogle_mediation_comments(mediation) {
  41 + jQuery("#mediation-comment-list-" + mediation ).toggle();
  42 + jQuery("#mediation-comment-form-" + mediation ).toggle();
  43 +}
  44 +
  45 +
  46 +function new_mediation_comment(button, mediation) {
  47 +
  48 + if (!validate_textarea(jQuery("#mediation-comment-form-" + mediation + " textarea").val())) {
  49 + return false;
  50 + }
  51 +
  52 + for (var i = 0; i < mediations.length; i++) {
  53 + mediation_id = mediations[i][0];
  54 + if (mediation_id == mediation) {
  55 + interval_id = mediations[i][1];
  56 + clearInterval( interval_id );
  57 + break;
  58 + }
  59 +
  60 + }
  61 +
  62 + mediations.splice(i, 1);
  63 +
  64 + var form = jQuery(button).parents("form");
  65 +
  66 + jQuery("#mediation-comment-form-" + mediation + " .submit").attr("disabled", true);
  67 +
  68 + jQuery("body").addClass("hub-loading");
  69 +
  70 + jQuery.post(form.attr("action"), form.serialize(), function(data) {
  71 + jQuery("body").removeClass("hub-loading");
  72 + if (data.ok) {
  73 + jQuery("#mediation-comment-form-" + mediation + " textarea").val('');
  74 + jQuery("#mediation-comment-form-" + mediation + " .submit").attr("disabled", false);
  75 + update_mediation_comments(mediation, false);
  76 + mediations.push( [ mediation, setInterval(function() { update_mediation_comments(mediation, false)}, 5000) ] );
  77 + }
  78 + else {
  79 + jQuery("#mediation-comment-form-" + mediation + " .submit").attr("disabled", false);
  80 + mediations.push( [ mediation, setInterval(function() { update_mediation_comments(mediation, false)}, 5000) ] );
  81 + }
  82 + }, 'json');
  83 +
  84 +}
  85 +
  86 +
  87 +function new_message(button) {
  88 +
  89 + if (!validate_textarea(jQuery(".hub .form-message #message_body").val())) {
  90 + return false;
  91 + }
  92 +
  93 + clearInterval( message_interval_id );
  94 +
  95 + var form = jQuery(button).parents("form");
  96 +
  97 + jQuery(".hub .form-message .submit").attr("disabled", true);
  98 +
  99 + jQuery("body").addClass("hub-loading");
  100 +
  101 + jQuery.post(form.attr("action"), form.serialize(), function(data) {
  102 + jQuery("body").removeClass("hub-loading");
  103 + if (data.ok) {
  104 + jQuery(".hub .form-message #message_body").val('');
  105 + jQuery(".hub .form-message .submit").attr("disabled", false);
  106 + update_live_stream();
  107 + message_interval_id = setInterval(function() { update_live_stream()}, 5000);
  108 + }
  109 + else {
  110 + jQuery(".hub .form-message .submit").attr("disabled", false);
  111 + message_interval_id = setInterval(function() { update_live_stream()}, 5000);
  112 + }
  113 + }, 'json');
  114 +
  115 +}
  116 +
  117 +
  118 +function new_mediation(button) {
  119 +
  120 + if (!validate_textarea(tinymce.get('article_body').getContent(''))) {
  121 + return false;
  122 + }
  123 +
  124 + var form = jQuery(button).parents("form");
  125 +
  126 + jQuery(".hub .form-mediation .submit").attr("disabled", true);
  127 +
  128 + jQuery("body").addClass("hub-loading");
  129 +
  130 + tinymce.triggerSave();
  131 + jQuery.post(form.attr("action"), form.serialize(), function(data) {
  132 + jQuery("body").removeClass("hub-loading");
  133 + if (data.ok) {
  134 + jQuery(".hub .form-mediation .submit").attr("disabled", false);
  135 + tinymce.get('article_body').setContent('');
  136 + update_mediations();
  137 + }
  138 + else {
  139 + jQuery(".hub .form-mediation .submit").attr("disabled", false);
  140 + }
  141 + }, 'json');
  142 +
  143 +}
  144 +
  145 +
  146 +function promote_user(mediation, user_id) {
  147 +
  148 + if (confirm(DEFAULT_PROMOTE_QUESTION)) {
  149 +
  150 + var hub_id = jQuery(".hub").attr('id');
  151 +
  152 + jQuery.ajax({
  153 + url: '/plugin/community_hub/public/promote_user',
  154 + type: 'get',
  155 + dataType: 'json',
  156 + data: { user: user_id, hub: hub_id },
  157 + success: function(data) {
  158 + jQuery(".promote a").filter("#" + mediation).replaceWith( '<img class="promoted" src="/plugins/community_hub/icons/hub-not-promote-icon.png" title="User promoted">' );
  159 + },
  160 + error: function(ajax, stat, errorThrown) {
  161 + }
  162 + });
  163 +
  164 + }
  165 +
  166 +}
  167 +
  168 +
  169 +function pin_message(post_id) {
  170 +
  171 + if (confirm(DEFAULT_PIN_QUESTION)) {
  172 +
  173 + var hub_id = jQuery(".hub").attr('id');
  174 +
  175 + jQuery.ajax({
  176 + url: '/plugin/community_hub/public/pin_message',
  177 + type: 'get',
  178 + dataType: 'json',
  179 + data: { message: post_id, hub: hub_id },
  180 + success: function(data) {
  181 + jQuery(".pin a").filter("#" + post_id).replaceWith( '<img class="pinned" src="/plugins/community_hub/icons/hub-not-pinned-icon.png" title="Message pinned">' );
  182 + },
  183 + error: function(ajax, stat, errorThrown) {
  184 + }
  185 + });
  186 +
  187 + }
  188 +
  189 +}
  190 +
  191 +
  192 +function update_mediation_comments(mediation, recursive) {
  193 +
  194 + if (jQuery("#right-tab.show").size() != 0) {
  195 +
  196 + if (jQuery(".hub #mediation-comment-list-" + mediation).css('display') != "none") {
  197 +
  198 + var hub_id = jQuery(".hub").attr('id');
  199 +
  200 + if (jQuery("#mediation-comment-list-" + mediation + " li").first().length == 0) {
  201 + var latest_post_id = 0;
  202 + }
  203 + else {
  204 + var latest_post_id = jQuery("#mediation-comment-list-" + mediation + " li.mediation-comment").last().attr('id');
  205 + }
  206 +
  207 + jQuery.ajax({
  208 + url: '/plugin/community_hub/public/newer_mediation_comment',
  209 + type: 'get',
  210 + data: { latest_post: latest_post_id, mediation: mediation },
  211 + success: function(data) {
  212 + if (data.trim().length > 0) {
  213 + jQuery("#mediation-comment-list-" + mediation + "").append(data);
  214 + jQuery("#mediation-comment-total-" + mediation).html(jQuery("#mediation-comment-list-" + mediation + " li.mediation-comment").size());
  215 + }
  216 + },
  217 + error: function(ajax, stat, errorThrown) {
  218 + }
  219 + });
  220 +
  221 + }
  222 +
  223 + }
  224 +
  225 + if (recursive) {
  226 + setTimeout(function() {
  227 + update_mediation_comments(mediation, true);
  228 + }, 5000);
  229 + }
  230 +}
  231 +
  232 +
  233 +function update_mediations() {
  234 +
  235 + if (jQuery("#right-tab.show").size() != 0) {
  236 +
  237 + var hub_id = jQuery(".hub").attr('id');
  238 +
  239 + if (jQuery("#mediation-posts li").first().length == 0) {
  240 + var latest_post_id = 0;
  241 + }
  242 + else {
  243 + var latest_post_id = jQuery("#mediation-posts li").first().attr('id');
  244 + }
  245 +
  246 + jQuery.ajax({
  247 + url: '/plugin/community_hub/public/newer_articles',
  248 + type: 'get',
  249 + data: { latest_post: latest_post_id, hub: hub_id },
  250 + success: function(data) {
  251 + jQuery("body").removeClass("hub-loading");
  252 + if (data.trim().length > 0) {
  253 + jQuery("#mediation-posts").prepend(data);
  254 + }
  255 + },
  256 + error: function(ajax, stat, errorThrown) {
  257 + }
  258 + });
  259 +
  260 + }
  261 +
  262 + setTimeout(update_mediations, 10000);
  263 +}
  264 +
  265 +
  266 +function update_live_stream() {
  267 + if (jQuery("#left-tab.show").size() != 0) {
  268 +
  269 + var hub_id = jQuery(".hub").attr('id');
  270 +
  271 + if (jQuery("#live-posts li").first().length == 0) {
  272 + var latest_post_id = 0;
  273 + }
  274 + else {
  275 + var latest_post_id = jQuery("#live-posts li").first().attr('id');
  276 + }
  277 +
  278 + jQuery.ajax({
  279 + url: '/plugin/community_hub/public/newer_comments',
  280 + type: 'get',
  281 + data: { latest_post: latest_post_id, hub: hub_id },
  282 + success: function(data) {
  283 +
  284 + if (data.trim().length > 0) {
  285 + jQuery("#live-posts").prepend(data);
  286 + if (jQuery("#auto_scrolling").prop('checked', true)) {
  287 + jQuery("#live-posts").scrollTop(0);
  288 + }
  289 + else {
  290 + jQuery("#live-posts").scrollTop(live_scroll_position);
  291 + }
  292 + }
  293 +
  294 + if (first_hub_load) {
  295 + jQuery("body").removeClass("hub-loading");
  296 + first_hub_load = false;
  297 + }
  298 +
  299 + }
  300 + });
  301 +
  302 + }
  303 +}
  304 +
  305 +function hub_left_tab_click() {
  306 + jQuery("#right-tab").removeClass('show');
  307 + jQuery("#right-tab").addClass('hide');
  308 + jQuery("#left-tab").removeClass('hide');
  309 + jQuery("#left-tab").addClass('show');
  310 +}
  311 +
  312 +function hub_right_tab_click() {
  313 + jQuery("#left-tab").removeClass('show');
  314 + jQuery("#left-tab").addClass('hide');
  315 + jQuery("#right-tab").removeClass('hide');
  316 + jQuery("#right-tab").addClass('show');
  317 + jQuery(".hub #right-tab.show h1.live").click(hub_left_tab_click);
  318 + if (first_mediations_load) {
  319 + jQuery("body").addClass("hub-loading");
  320 + first_mediations_load = false;
  321 + update_mediations();
  322 + }
  323 +}
  324 +
  325 +first_hub_load = true;
  326 +first_mediations_load = true;
  327 +
  328 +jQuery(".hub .envelope").scroll(function() {
  329 + jQuery("#auto_scrolling").prop('checked', false);
  330 +
  331 +
  332 + // live stream tab...
  333 + if (jQuery("#left-tab.show").size() != 0) {
  334 + current_envelope = jQuery(".hub .live .envelope");
  335 + current_list_posts = jQuery(".hub ul#live-posts");
  336 + tab = 'live';
  337 + }
  338 + else {
  339 + // mediation tab...
  340 + if (jQuery("#right-tab.show").size() != 0) {
  341 + current_envelope = jQuery(".hub .mediation .envelope");
  342 + current_list_posts = jQuery(".hub ul#mediation-posts");
  343 + tab = 'mediation';
  344 + }
  345 + }
  346 +
  347 + if (current_envelope.scrollTop() == (current_list_posts.height() - current_envelope.height() + 23)) {
  348 + load_more(tab);
  349 + }
  350 +
  351 +});
  352 +
  353 +
  354 +jQuery(document).ready(function() {
  355 +
  356 + jQuery("#live-posts").scroll(function() {
  357 + live_scroll_position = jQuery("#live-posts").scrollTop();
  358 + });
  359 +
  360 + jQuery(".hub #left-tab.show h1.mediation").click(hub_right_tab_click);
  361 +
  362 + jQuery("body").addClass("hub-loading");
  363 +
  364 + message_interval_id = setInterval(function() { update_live_stream() }, 5000);
  365 +
  366 +});
... ...
plugins/community_hub/public/style.css 0 → 100644
... ... @@ -0,0 +1,649 @@
  1 +#banner-embed-container {
  2 + width: 49%;
  3 + float: right;
  4 +}
  5 +
  6 +#input-panel {
  7 + width: 100%;
  8 + padding-top: 10px;
  9 + display: inline-block;
  10 +}
  11 +
  12 +#hub-loading {
  13 + float: right;
  14 +}
  15 +
  16 +.hub-loading {
  17 + cursor: wait;
  18 +}
  19 +
  20 +.icon-newcommunity-hub,
  21 +.icon-community-hub {
  22 + background-image: url(/plugins/community_hub/icons/community-hub.png)
  23 +}
  24 +
  25 +.hub ul {padding-left: 0px; margin-top: 0;}
  26 +
  27 +#content .hub h1{
  28 + margin-bottom: 0;
  29 +}
  30 +
  31 +#content .hub .content-tab h1{
  32 + border-color: #D71410 #CCCCCC -moz-use-text-color;
  33 + border-left: 1px solid #CCCCCC;
  34 + border-right: 1px solid #CCCCCC;
  35 + border-style: solid solid none;
  36 + border-width: 1px 1px 0;
  37 + border-top: 1px solid #D71410;
  38 +}
  39 +
  40 +#content .hub .title {
  41 + font-size: 33px;
  42 + font-weight: normal;
  43 + padding-right: 70px;
  44 + color: #4b7421;
  45 + font-variant: normal;
  46 +}
  47 +
  48 +.hub .description {
  49 + font-size: 14px;
  50 +}
  51 +
  52 +.hub .post {
  53 + border-top: 1px solid #ddd;
  54 + background: url("images/hub-time-bg.gif") repeat-y left top #fff;
  55 + padding: 5px 0;
  56 +}
  57 +
  58 +
  59 +.hub .time{
  60 + display: inline-block;
  61 + font-size: 8px;
  62 + padding-top: 10px;
  63 + text-align: center;
  64 + vertical-align: top;
  65 + width: 35px;
  66 +}
  67 +
  68 +.hub .avatar{
  69 + background-color: lightGray;
  70 + display: inline-block;
  71 + height: 43px;
  72 + margin: 0 10px;
  73 + vertical-align: top;
  74 + width: 43px;
  75 + text-align: center;
  76 +}
  77 +
  78 +.hub .avatar img{
  79 + max-height: 43px;
  80 + max-width: 43px;
  81 +}
  82 +
  83 +.hub .message {
  84 + display: inline-block;
  85 + width: 65%;
  86 + float: none;
  87 + clear: both;
  88 +}
  89 +
  90 +.hub .show .message {
  91 + width: 80%;
  92 +}
  93 +
  94 +.hub .message .author {
  95 + font-weight: bold;
  96 + display: inline-block;
  97 + margin-right: 5px;
  98 + padding-bottom: 0px;
  99 +}
  100 +
  101 +.hub .mediation-bar {
  102 + display: inline-block;
  103 + margin: 10px 0 10px 104px;
  104 + width: 83%;
  105 +}
  106 +
  107 +.hub .mediation-bar ul {}
  108 +
  109 +.hub .mediation-bar ul li {
  110 + display: inline-block;
  111 + overflow: hidden;
  112 + width: 16px;
  113 +}
  114 +
  115 +.hub .mediation-bar ul li.likes-dislikes{
  116 + overflow: visible;
  117 + text-indent: 0px;
  118 + width: auto;
  119 + vertical-align: top;
  120 +}
  121 +
  122 +.hub .mediation-bar ul li.pin {
  123 + height: 25px;
  124 + /*margin-right: 15px;*/
  125 +}
  126 +
  127 +.hub .remove{}
  128 +
  129 +.hub .mediation-bar ul li a{
  130 + text-indent: -10000px;
  131 +}
  132 +
  133 +.hub .not-promoted {
  134 + opacity: 0.5;
  135 + filter: alpha(opacity=50);
  136 +}
  137 +
  138 +.hub .pin .not-pinned {
  139 + opacity: 0.5;
  140 + filter: alpha(opacity=50);
  141 +}
  142 +
  143 +.hub .mediation-bar ul li.pin {
  144 + float: right;
  145 +}
  146 +
  147 +.hub ul.mediation-comment-list{
  148 + margin-left: 80px;
  149 + display: inline-block;
  150 + padding: 10px 0;
  151 + background-color: #fff;
  152 + margin-bottom: 10px;
  153 +}
  154 +
  155 +.hub ul.mediation-comment-list li {
  156 + margin-bottom: 2px;
  157 +}
  158 +
  159 +.hub .mediation-comment-form {
  160 + margin-left: 70px;
  161 + margin-top: 10px;
  162 +}
  163 +
  164 +.hub input.button.with-text.icon-add.submit{
  165 + display: block;
  166 +}
  167 +
  168 +.hub .live {
  169 + border: 0px solid lightGray;
  170 + display: inline-block;
  171 + float: left;
  172 + width: 49%;
  173 + margin-bottom: 2em;
  174 +}
  175 +
  176 +.hub .envelope {
  177 + height: 500px;
  178 + overflow-x: hidden;
  179 + overflow-y: scroll;
  180 + margin-top: -1px;
  181 + margin-bottom: 14px;
  182 + border: 1px solid lightgray;
  183 + /*border-bottom: 1px solid lightgray;
  184 + border-left: 1px solid lightgray;*/
  185 + /*display: inline-block;*/
  186 + width: 100%;
  187 +}
  188 +
  189 +.hub ul#live-posts, .hub ul#mediation-posts{
  190 + /*border-width: 0 1px 1px;*/
  191 + /*border-style: solid;*/
  192 + /*border-color: lightGray;*/
  193 + border-bottom: 1px solid lightgray;
  194 + padding-top: 10px;
  195 + /*clear: both;*/
  196 + /*height: 475px;*/
  197 + /*overflow-y: scroll;*/
  198 +
  199 +}
  200 +
  201 +/*modificação da scroll bar*/
  202 +.hub div.envelope::-webkit-scrollbar-button /*,
  203 +.hub ul#live-posts::-webkit-scrollbar-button,
  204 +.hub ul#mediation-posts::-webkit-scrollbar-button */ {
  205 + height: 0;
  206 + width: 0;
  207 +}
  208 +
  209 +.hub div.envelope::-webkit-scrollbar-thumb /*,
  210 +.hub ul#live-posts::-webkit-scrollbar-thumb,
  211 +.hub ul#mediation-posts::-webkit-scrollbar-thumb */ {
  212 + background-clip: padding-box;
  213 + background-color: rgba(0,0,0,.3);
  214 + border: 5px solid transparent;
  215 + border-radius: 10px;
  216 + min-height: 20px;
  217 + min-width: 20px;
  218 + height: 5px;
  219 + width: 5px;
  220 +}
  221 +
  222 +.hub div.envelope::-webkit-scrollbar /*,
  223 +.hub ul#live-posts::-webkit-scrollbar,
  224 +.hub ul#mediation-posts::-webkit-scrollbar */ {
  225 + height: 15px;
  226 + width: 15px;
  227 +}
  228 +
  229 +/*fim de modificação da scroll bar*/
  230 +
  231 +.hub #live-posts .post {
  232 + background-color:#fff;
  233 +}
  234 +
  235 +.hub ul#mediation-posts .post{
  236 + background-color:#eee;
  237 + border-color: #fff;
  238 + padding-bottom: 10px;
  239 +}
  240 +
  241 +/*novos elementos: h1.live - h1.mediation*/
  242 +#content .main-block .hub .live h1.live,
  243 +#content .main-block .hub .mediation h1.mediation {
  244 + /*background-color: #f4f4f4;*/
  245 + border: 1px solid lightGray;
  246 + border-top: 1px solid #96110D;
  247 + border-bottom: 0px solid #FFFFFF;
  248 + top: 3px;
  249 + float: left;
  250 + font-weight: normal;
  251 + margin-bottom: -1px;
  252 + position: relative;
  253 + width: 60%;
  254 +background-color: white;
  255 +z-index: 99;
  256 +}
  257 +
  258 +#content .main-block .hub .live h1.live{
  259 + float: left;
  260 + margin-right: 20px;
  261 + margin-left: 0px;
  262 + text-align: center;
  263 +}
  264 +
  265 +#content .main-block .hub .mediation h1.mediation{
  266 + float: right;
  267 + margin-right: 0px;
  268 + margin-left: 20px;
  269 + top: 0px;
  270 + left: 2px;
  271 + text-align: center;
  272 +}
  273 +
  274 +#content .main-block .hub .live h1.mediation,
  275 +#content .main-block .hub .mediation h1.live {
  276 + display: inline-block;
  277 + background-color: #EEEEEE;
  278 + border-bottom: 1px solid #CCCCCC;
  279 + border-top-color: #CCCCCC;
  280 + color: gray;
  281 + position: relative;
  282 + width: 30%;
  283 + text-align: center;
  284 + margin-top: 8px;
  285 + line-height: 27px;
  286 + cursor: pointer;
  287 +
  288 +}
  289 +
  290 +#content .main-block .hub .live h1.mediation {}
  291 +
  292 +#content .main-block .hub .mediation h1.live {
  293 + margin-left: 20px;
  294 + top:1px;
  295 + /*border-bottom: 0px;*/
  296 +}
  297 +
  298 +
  299 +/*fim de novos elementos: h1.live - h1.mediation*/
  300 +
  301 +
  302 +#content .hub .live .title {
  303 + color: #D71410;
  304 + display: inline-block;
  305 + font-size: 14px;
  306 + font-family: /*Arial Black, */arial, sans-serif;
  307 + padding-right: 0;
  308 + width: 70%;
  309 +}
  310 +#content .hub .live .on-air {
  311 + background-color: #96110D;
  312 + border-radius: 10px 10px 10px 10px;
  313 + color: white;
  314 + display: inline-block;
  315 + font-size: 16px;
  316 + font-weight: bold;
  317 + padding: 0 0.5em;
  318 + text-align: center;
  319 + /*text-transform: uppercase;*/
  320 + vertical-align: top;
  321 + width: 20%;
  322 +
  323 +}
  324 +#content .hub .live .off-air {
  325 + background-color: gray;
  326 + border-radius: 10px 10px 10px 10px;
  327 + color: black;
  328 + display: inline-block;
  329 + font-size: 16px;
  330 + font-weight: bold;
  331 + padding: 0 0.5em;
  332 + text-align: center;
  333 + text-transform: uppercase;
  334 + vertical-align: top;
  335 + width: 20%;
  336 +}
  337 +
  338 +/****aba live fechada****/
  339 +
  340 +
  341 +.hub .live.hide {
  342 + width: 10%;
  343 +}
  344 +
  345 +.hub .live.hide ul#live-posts{
  346 + overflow-x: visible;
  347 + overflow-y: visible;
  348 + background-color: lightGray;
  349 +}
  350 +
  351 +.hub .live.hide ul#live-posts li{
  352 + display: none;
  353 +}
  354 +
  355 +#content .hub .live.hide h1 {
  356 + text-align: center;
  357 + cursor: pointer;
  358 +}
  359 +
  360 +#content .hub .live.hide h1 .title {
  361 + display: none;
  362 +}
  363 +#content .hub .live.hide .on-air,
  364 +#content .hub .live.hide .off-air {
  365 + width: auto;
  366 +}
  367 +
  368 +/****fim aba live fechada****/
  369 +
  370 +/****aba live aberta****/
  371 +
  372 +.hub .live.show {
  373 + /*width: 85%;*/
  374 + width: 100%;
  375 +}
  376 +
  377 +.hub .live.show ul#live-posts .li{
  378 +
  379 +}
  380 +
  381 +#content .hub .live.show .on-air,
  382 +#content .hub .live.show .off-air{
  383 +margin-right: 10px;
  384 +}
  385 +
  386 +/****fim aba mlive aberta****/
  387 +
  388 +
  389 +/**************************************/
  390 +
  391 +
  392 +.hub .mediation {
  393 + border: 0px solid lightGray;
  394 + display: inline-block;
  395 + clear: right;
  396 + float: none;
  397 + margin-left: 1%;
  398 + width: 50%;
  399 + margin-bottom: 2em;
  400 +}
  401 +
  402 +#content .main-block .hub .mediation h1{
  403 + /* background-color: #ed8e01;*/
  404 + border: 1px solid lightGray
  405 +}
  406 +
  407 +#content .hub .mediation .title {
  408 + color: gray;
  409 + display: inline-block;
  410 + font-size: 14px;
  411 + font-family: Arial, sans-serif;
  412 + padding-right: 0;
  413 + /* width: 100%;*/
  414 +}
  415 +.hub .mediation .expand {
  416 + float: right;
  417 + padding-right: 1em;
  418 +}
  419 +
  420 +/****aba mediation fechada****/
  421 +
  422 +.hub .mediation.hide {
  423 + border: 0px solid lightGray;
  424 + display: inline-block;
  425 + clear: right;
  426 + float: none;
  427 + margin-left: 1%;
  428 + width: 10%;
  429 + margin-bottom: 0em;
  430 +}
  431 +
  432 +#content .main-block .hub .mediation.hide h1{
  433 +/*
  434 + background-color: #ed8e01;
  435 + border: 1px solid lightGray;
  436 + text-align: center;
  437 + padding-left: 0;
  438 +*/
  439 + cursor: pointer;
  440 +}
  441 +
  442 +#content .hub .mediation.hide .title {
  443 +/* color: white;*/
  444 + display: inline-block;
  445 + font-size: 14px;
  446 + font-family: /*Arial Black, */arial, sans-serif;
  447 + padding-right: 0;
  448 + width: 100%;
  449 +}
  450 +.hub .mediation.hide .expand {
  451 + display: none;
  452 +}
  453 +
  454 +
  455 +.hub .mediation.hide ul#mediation-posts {
  456 +height: 500px;
  457 +overflow-x: visible;
  458 +overflow-y: visible;
  459 +border-width: 0 1px 1px;
  460 +border-style: solid;
  461 +border-color: lightGray;
  462 +padding-top: 10px;
  463 +background-color: lightgray;
  464 +}
  465 +
  466 +.hub .mediation.hide ul#mediation-posts li{
  467 +display: none;
  468 +}
  469 +
  470 +/****fim aba mediation fechada****/
  471 +
  472 +/****aba mediation aberta****/
  473 +
  474 +.hub .mediation.show {
  475 +/* border: 0px solid lightGray;
  476 + display: inline-block;
  477 + clear: right;
  478 + float: none;
  479 + margin-left: 1%;
  480 + margin-bottom: 2em;
  481 +*/
  482 + /*width: 85%;*/
  483 + width: 100%;
  484 +}
  485 +
  486 +#content .main-block .hub .mediation.show h1{
  487 +/* background-color: #ed8e01;*/
  488 +/* border: 1px solid lightGray*/
  489 +}
  490 +
  491 +#content .hub .mediation.show .title {
  492 + /*color: white;*/
  493 + display: inline-block;
  494 +/* font-size: 20px;*/
  495 + font-family: /*Arial Black, */arial, sans-serif;
  496 + padding-right: 0;
  497 +/* width: 60%;*/
  498 +}
  499 +
  500 +/*
  501 +.hub .mediation.show .expand {
  502 + float: right;
  503 + padding-right: 1em;
  504 + display: block;
  505 +}*/
  506 +
  507 +
  508 +.hub .mediation.show ul#mediation-posts {
  509 +/*
  510 + height: 500px;
  511 + overflow-x: hidden;
  512 + overflow-y: scroll;
  513 + border-width: 0 1px 1px;
  514 + border-style: solid;
  515 + border-color: lightGray;
  516 + padding-top: 10px;
  517 + background-color: #f9f9f9;
  518 +*/
  519 +}
  520 +
  521 +.hub .mediation.show ul#mediation-posts li{
  522 +
  523 +}
  524 +
  525 +/****fim aba mediation aberta****/
  526 +
  527 +.hub .mediation.hide,
  528 +.hub .live.hide {
  529 + display: none;
  530 + visibility: hidden;
  531 +}
  532 +
  533 +.hub .mediation.show,
  534 +.hub .live.show {
  535 + display: inline-block;
  536 + visibility: visible;
  537 +}
  538 +
  539 +.hub .form-mediation {
  540 + /*width: 93%;*/
  541 + /*width: 426px;*/
  542 + width: 60%;
  543 + display: inline-block;
  544 + /*height: 376px;*/
  545 + padding: 10px;
  546 + border: 1px solid #c0c0c0;
  547 + overflow-x: hidden;
  548 +}
  549 +
  550 +.hub .form-message {
  551 + height: 148px;
  552 + padding: 10px;
  553 + border: 1px solid #c0c0c0;
  554 +}
  555 +
  556 +
  557 +.hub div.settings {
  558 + /*width: 96%;*/
  559 + /*margin-top: 10px;*/
  560 + /*margin-left: 10px;*/
  561 + /*width: 240px;*/
  562 + display: inline-block;
  563 + margin-left: 1%;
  564 + margin-top: 10px;
  565 + vertical-align: top;
  566 + width: 35%;
  567 +}
  568 +
  569 +.hub ul.settings li {
  570 + height: 50px;
  571 + line-height: 50px;
  572 + margin-bottom: 10px;
  573 + padding: 0 10px;
  574 + background: url("images/hub-arrow-right.png") no-repeat 90% top #ed8e01;
  575 +}
  576 +
  577 +.hub ul.settings span.collapse {
  578 + padding-right: 0.5em;
  579 + float: right;
  580 +}
  581 +
  582 +.hub div.banner {
  583 + height: 70px;
  584 + background-color: #6d6d6d;
  585 + text-align: center;
  586 + padding-top: 30px;
  587 +}
  588 +
  589 +.hub div.banner span {
  590 + color: white;
  591 + font-family: Arial Black, arial, sans-serif;
  592 + font-size: large;
  593 + font-weight: normal;
  594 + display: block;
  595 +}
  596 +
  597 +.hub div.embed {
  598 + margin-top: 10px;
  599 + padding: 8px;
  600 + border: 1px solid #c0c0c0;
  601 +}
  602 +
  603 +.hub div.embed textarea.code {
  604 + background-color: #f0f0f0;
  605 + border: 1px solid #f0f0f0;
  606 + height: 195px;
  607 + resize: none;
  608 +}
  609 +
  610 +#content .hub ul.settings li a{
  611 + color: white;
  612 + font-family: Arial Black, arial, sans-serif;
  613 + font-size: large;
  614 + font-weight: normal;
  615 +}
  616 +
  617 +#content .hub ul.settings li a:hover {
  618 + text-decoration: none;
  619 +}
  620 +
  621 +#content .hub form input.button.submit {
  622 + font-family: Arial Black, arial, sans-serif;
  623 + font-size: small;
  624 + font-weight: normal;
  625 + color: #333;
  626 + margin-top: 10px;
  627 +}
  628 +
  629 +textarea#message_body {
  630 + width: auto;
  631 + margin-left: 80px;
  632 +}
  633 +
  634 +.comment-count {
  635 + margin-left: 80px;
  636 + padding: 5px 10px;
  637 + background-color: white;
  638 + display: inline-block;
  639 + border-radius: 5px;
  640 + font-weight: bold;
  641 + display: block;
  642 + margin-bottom: -5px;
  643 + margin-top: 10px;
  644 + max-width: 100px;
  645 +}
  646 +
  647 +.hub ul.mediation-comment-list {
  648 + width: 80%;
  649 +}
... ...
plugins/community_hub/script/hub_updater 0 → 100755
... ... @@ -0,0 +1,15 @@
  1 +#!/usr/bin/env ruby
  2 +
  3 +require 'daemons'
  4 +require 'optparse'
  5 +
  6 +NOOSFERO_ROOT = File.expand_path(File.dirname(__FILE__) + '/../../../')
  7 +
  8 +Daemons.run_proc('hub') do
  9 + require NOOSFERO_ROOT + '/config/environment'
  10 + unless ENV['PROXY'].blank?
  11 + ENV['NO_PROXY'] ||= 'localhost'
  12 + require 'proxifier/env'
  13 + end
  14 + CommunityHubPlugin::Listener.run
  15 +end
... ...
plugins/community_hub/test/functional/community_hub_plugin_cms_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,73 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class CmsController; def rescue_action(e) raise e end; end
  4 +
  5 +class CmsControllerTest < ActionController::TestCase
  6 +
  7 + def setup
  8 + @controller = CmsController.new
  9 + @request = ActionController::TestRequest.new
  10 + @response = ActionController::TestResponse.new
  11 +
  12 + user = create_user('testinguser')
  13 +
  14 + @environment = user.environment
  15 +
  16 + @community = Community.create!(
  17 + :name => 'Sample community',
  18 + :identifier => 'sample-community',
  19 + :environment => @environment
  20 + )
  21 +
  22 + @community.add_admin(user.person)
  23 +
  24 + @community.save!
  25 +
  26 + @hub = CommunityHubPlugin::Hub.new(
  27 + :abstract => 'abstract',
  28 + :body => 'body',
  29 + :name => 'test-hub',
  30 + :profile => @community,
  31 + :last_changed_by_id => user
  32 + )
  33 +
  34 + @hub.save!
  35 +
  36 + login_as(user.login)
  37 +
  38 + end
  39 +
  40 + should 'be able to edit hub settings' do
  41 + get :edit, :id => @hub.id, :profile => @community.identifier
  42 + assert_tag :tag => 'input', :attributes => { :id => 'article_name' }
  43 + assert_tag :tag => 'textarea', :attributes => { :id => 'article_body' }
  44 + assert_tag :tag => 'input', :attributes => { :id => 'article_twitter_enabled' }
  45 + assert_tag :tag => 'input', :attributes => { :id => 'article_twitter_hashtags' }
  46 + assert_tag :tag => 'input', :attributes => { :id => 'article_facebook_enabled' }
  47 + assert_tag :tag => 'input', :attributes => { :id => 'article_facebook_hashtag' }
  48 + assert_tag :tag => 'input', :attributes => { :id => 'article_facebook_access_token' }
  49 + end
  50 +
  51 + should 'be able to save hub' do
  52 + get :edit, :id => @hub.id, :profile => @community.identifier
  53 + post :edit, :id => @hub.id, :profile => @community.identifier, :article => {
  54 + :name => 'changed',
  55 + :body => 'changed',
  56 + :twitter_enabled => true,
  57 + :twitter_hashtags => 'changed',
  58 + :facebook_enabled => true,
  59 + :facebook_hashtag => 'changed',
  60 + :facebook_access_token => 'changed'
  61 + }
  62 + @hub.reload
  63 + assert_equal 'changed', @hub.name
  64 + assert_equal 'changed', @hub.body
  65 + assert_equal true, @hub.twitter_enabled
  66 + assert_equal 'changed', @hub.twitter_hashtags
  67 + assert_equal true, @hub.facebook_enabled
  68 + assert_equal 'changed', @hub.facebook_hashtag
  69 + assert_equal 'changed', @hub.facebook_access_token
  70 + end
  71 +
  72 +end
  73 +
... ...
plugins/community_hub/test/functional/community_hub_plugin_content_viewer_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,108 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +require 'content_viewer_controller'
  3 +
  4 +class ContentViewerController; def rescue_action(e) raise e end; end
  5 +
  6 +class ContentViewerControllerTest < ActionController::TestCase
  7 +
  8 + all_fixtures
  9 +
  10 + def setup
  11 + @controller = ContentViewerController.new
  12 + @request = ActionController::TestRequest.new
  13 + @response = ActionController::TestResponse.new
  14 +
  15 + @user = create_user('testinguser').person
  16 +
  17 + @environment = @user.environment
  18 +
  19 + @community = Community.create!(
  20 + :name => 'Sample community',
  21 + :identifier => 'sample-community',
  22 + :environment => @environment
  23 + )
  24 +
  25 + @hub = CommunityHubPlugin::Hub.new(
  26 + :abstract => 'abstract',
  27 + :body => 'body',
  28 + :name => 'test-hub',
  29 + :profile => community,
  30 + :last_changed_by_id => user.id
  31 + )
  32 +
  33 + @hub.save!
  34 +
  35 + end
  36 +
  37 + attr_reader :user, :environment, :community, :hub
  38 + should 'display live tab' do
  39 + get :view_page, @hub.url
  40 + assert_tag :tag => 'div', :attributes => { :id => 'left-tab' }
  41 + end
  42 +
  43 + should 'display mediation tab' do
  44 + get :view_page, @hub.url
  45 + assert_tag :tag => 'div', :attributes => { :id => 'right-tab' }
  46 + end
  47 +
  48 + should 'display auto scroll checkbox for live stream content' do
  49 + get :view_page, @hub.url
  50 + assert_tag :tag => 'div', :attributes => { :id => 'left-tab' }, :descendant => {
  51 + :tag => 'span', :descendant => {
  52 + :tag => 'input', :attributes => { :id => 'auto_scrolling', :type => 'checkbox' }
  53 + }
  54 + }
  55 + end
  56 +
  57 + should 'not display auto scroll setting for mediation content' do
  58 + get :view_page, @hub.url
  59 + assert_no_tag :tag => 'div', :attributes => { :id => 'right-tab' }, :descendant => {
  60 + :tag => 'span', :descendant => {
  61 + :tag => 'input', :attributes => { :id => 'auto_scrolling', :type => 'checkbox' }
  62 + }
  63 + }
  64 + end
  65 +
  66 + should 'not display message form if user is not logged' do
  67 + get :view_page, @hub.url
  68 + assert_no_tag :tag => 'div', :attributes => { :class => 'form-message' }
  69 + end
  70 +
  71 + should 'not display mediation form if user is not loged' do
  72 + get :view_page, @hub.url
  73 + assert_no_tag :tag => 'div', :attributes => { :class => 'form-mediation' }
  74 + end
  75 +
  76 + should 'display message form if user is logged' do
  77 + user = create_user('visitor')
  78 + login_as(user.login)
  79 + get :view_page, @hub.url
  80 + assert_tag :tag => 'div', :attributes => { :class => 'form-message' }
  81 + end
  82 +
  83 + should 'display mediation form if user is logged and is hub''s mediator' do
  84 + login_as(user.user.login)
  85 + get :view_page, @hub.url
  86 + assert_tag :tag => 'div', :attributes => { :class => 'form-mediation' }
  87 + end
  88 +
  89 + should 'not display mediation form if user is logged but is not hub''s mediator' do
  90 + visitor = create_user('visitor')
  91 + login_as(visitor.login)
  92 + assert_no_tag :tag => 'div', :attributes => { :class => 'form-mediation' }
  93 + end
  94 +
  95 + should 'display link to hub''s settings if user is mediator' do
  96 + login_as(user.user.login)
  97 + get :view_page, @hub.url
  98 + assert_tag :tag => 'div', :attributes => { :class => 'settings' }
  99 + end
  100 +
  101 + should 'not display link to hub''s settings if user is not mediator' do
  102 + visitor = create_user('visitor')
  103 + login_as(visitor.login)
  104 + get :view_page, @hub.url
  105 + assert_no_tag :tag => 'div', :attributes => { :class => 'settings' }
  106 + end
  107 +
  108 +end
... ...
plugins/community_hub/test/functional/community_hub_plugin_public_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,186 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +require File.dirname(__FILE__) + '/../../controllers/public/community_hub_plugin_public_controller'
  3 +
  4 +class CommunityHubPluginPublicController; def rescue_action(e) raise e end; end
  5 +
  6 +class CommunityHubPluginPublicControllerTest < ActionController::TestCase
  7 +
  8 + all_fixtures
  9 +
  10 + def setup
  11 + @controller = CommunityHubPluginPublicController.new
  12 + @request = ActionController::TestRequest.new
  13 + @response = ActionController::TestResponse.new
  14 +
  15 + @user = create_user('testinguser').person
  16 +
  17 + @environment = @user.environment
  18 +
  19 + @community = Community.create!(
  20 + :name => 'Sample community',
  21 + :identifier => 'sample-community',
  22 + :environment => @environment
  23 + )
  24 +
  25 + @community.save!
  26 +
  27 + @hub = CommunityHubPlugin::Hub.new(
  28 + :abstract => 'abstract',
  29 + :body => 'body',
  30 + :name => 'test-hub',
  31 + :profile => community,
  32 + :last_changed_by_id => user.id
  33 + )
  34 +
  35 + @hub.save!
  36 +
  37 + end
  38 +
  39 + attr_reader :user, :environment, :community, :hub
  40 +
  41 + should 'display pin message flag if user is logged and mediator' do
  42 + message = create_message( hub, user )
  43 + login_as(user.user.login)
  44 + xhr :get, :newer_comments, { :latest_post => 0, :hub => hub.id }
  45 + assert_tag :tag => 'li', :attributes => { :class => 'pin' }
  46 + end
  47 +
  48 + should 'not display pin message flag if user is not mediator' do
  49 + message = create_message( hub, user )
  50 + visitor = create_user('visitor')
  51 + login_as(visitor.login)
  52 + xhr :get, :newer_comments, { :latest_post => 0, :hub => hub.id }
  53 + assert_no_tag :tag => 'li', :attributes => { :class => 'pin' }
  54 + end
  55 +
  56 + should 'pin message flag is link if message has not been pinned' do
  57 + message = create_message( hub, user )
  58 + login_as(user.user.login)
  59 + xhr :get, :newer_comments, { :latest_post => 0, :hub => hub.id }
  60 + assert_tag :tag => 'li', :attributes => { :class => 'pin' }, :descendant => {
  61 + :tag => 'a', :descendant => {
  62 + :tag => 'img', :attributes => { :class => 'not-pinned' }
  63 + }
  64 + }
  65 + end
  66 +
  67 + should 'ping message flag is not link if message has beem pinned' do
  68 + message = create_message( hub, user )
  69 + hub.pinned_messages += [message.id]
  70 + hub.save
  71 + login_as(user.user.login)
  72 + xhr :get, :newer_comments, { :latest_post => 0, :hub => hub.id }
  73 + assert_tag :tag => 'li', :attributes => { :class => 'pin' }, :descendant => {
  74 + :tag => 'img', :attributes => { :class => 'pinned' }
  75 + }
  76 + end
  77 +
  78 + should 'display promote user flag if user is logged and mediator' do
  79 + mediation = create_mediation(hub, user, community)
  80 + login_as(user.user.login)
  81 + xhr :get, :newer_articles, { :latest_post => 0, :hub => hub.id }
  82 + assert_tag :tag => 'li', :attributes => { :class => 'promote' }
  83 + end
  84 +
  85 + should 'not display promote user flag if user is not mediator' do
  86 + mediation = create_mediation(hub, user, community)
  87 + visitor = create_user('visitor')
  88 + login_as(visitor.login)
  89 + xhr :get, :newer_articles, { :latest_post => 0, :hub => hub.id }
  90 + assert_no_tag :tag => 'li', :attributes => { :class => 'promote' }
  91 + end
  92 +
  93 + should 'promote user flag is link if user has not been promoted' do
  94 + visitor = create_user('visitor').person
  95 + mediation = create_mediation(hub, visitor, community)
  96 + login_as(user.user.login)
  97 + xhr :get, :newer_articles, { :latest_post => 0, :hub => hub.id }
  98 + assert_tag :tag => 'li', :attributes => { :class => 'promote' }, :descendant => {
  99 + :tag => 'a', :descendant => {
  100 + :tag => 'img', :attributes => { :class => 'not-promoted' }
  101 + }
  102 + }
  103 + end
  104 +
  105 + should 'promote user flag is not link if user has been promoted' do
  106 + mediation = create_mediation(hub, user, community)
  107 + login_as(user.user.login)
  108 + xhr :get, :newer_articles, { :latest_post => 0, :hub => hub.id }
  109 + assert_tag :tag => 'li', :attributes => { :class => 'promote' }, :descendant => {
  110 + :tag => 'img', :attributes => { :class => 'promoted' }
  111 + }
  112 + end
  113 +
  114 + should 'promote user flag is not link if user is hub''s owner' do
  115 + mediation = create_mediation(hub, user, community)
  116 + login_as(user.user.login)
  117 + xhr :get, :newer_articles, { :latest_post => 0, :hub => hub.id }
  118 + assert_tag :tag => 'li', :attributes => { :class => 'promote' }, :descendant => {
  119 + :tag => 'img', :attributes => { :class => 'promoted' }
  120 + }
  121 + end
  122 +
  123 + should 'should create new message' do
  124 + login_as(user.user.login)
  125 + xhr :post, :new_message, { :article_id => hub.id, :message => {"body"=>"testmessage"} }
  126 + response = JSON.parse(@response.body)
  127 + assert_equal true, response['ok']
  128 + end
  129 +
  130 + should 'should create new mediation' do
  131 + login_as(user.user.login)
  132 + xhr :post, :new_mediation, { :profile_id => community.id, :article => { "parent_id" => hub.id , "body" => "<p>testmediation</p>" } }
  133 + response = JSON.parse(@response.body)
  134 + assert_equal true, response['ok']
  135 + end
  136 +
  137 + should 'should create new mediation comment' do
  138 + login_as(user.user.login)
  139 + mediation = create_mediation(hub, user, community)
  140 + xhr :post, :new_message, { "article_id" => mediation.id, "message" => {"body"=>"testmediationcomment"} }
  141 + response = JSON.parse(@response.body)
  142 + assert_equal true, response['ok']
  143 + end
  144 +
  145 + should 'should get newer messages' do
  146 + message1 = create_message( hub, user )
  147 + message2 = create_message( hub, user )
  148 + message3 = create_message( hub, user )
  149 + xhr :get, :newer_comments, { :latest_post => message2.id, :hub => hub.id }
  150 + assert_tag :tag => 'li', :attributes => { :id => message3.id }
  151 + end
  152 +
  153 + should 'should get oldest messages' do
  154 + message1 = create_message( hub, user )
  155 + message2 = create_message( hub, user )
  156 + message3 = create_message( hub, user )
  157 + xhr :get, :older_comments, { :oldest_id => message2.id, :hub => hub.id }
  158 + assert_tag :tag => 'li', :attributes => { :id => message1.id }
  159 + end
  160 +
  161 + should 'should get newer mediations' do
  162 + mediation1 = create_mediation(hub, user, community)
  163 + mediation2 = create_mediation(hub, user, community)
  164 + mediation3 = create_mediation(hub, user, community)
  165 + xhr :get, :newer_articles, { :latest_post => mediation2.id, :hub => hub.id }
  166 + assert_tag :tag => 'li', :attributes => { :id => mediation3.id }
  167 + end
  168 +
  169 + should 'should promote user' do
  170 + login_as(user.user.login)
  171 + visitor = create_user('visitor').person
  172 + xhr :post, :promote_user, { :hub => hub.id, :user => visitor.id }
  173 + response = JSON.parse(@response.body)
  174 + assert_equal true, response['ok']
  175 + end
  176 +
  177 + should 'should pin message' do
  178 + login_as(user.user.login)
  179 + message = create_message( hub, user )
  180 + xhr :post, :pin_message, { :hub => hub.id, :message => message.id }
  181 + response = JSON.parse(@response.body)
  182 + assert_equal true, response['ok']
  183 + end
  184 +
  185 +end
  186 +
... ...
plugins/community_hub/test/test_helper.rb 0 → 100644
... ... @@ -0,0 +1,38 @@
  1 +require File.dirname(__FILE__) + '/../../../test/test_helper'
  2 +
  3 +def create_hub(name, community, user)
  4 + hub = CommunityHubPlugin::Hub.new(:abstract => 'abstract', :body => 'body', :name => name, :profile => community, :last_changed_by_id => user.id )
  5 + hub.save!
  6 + hub
  7 +end
  8 +
  9 +def create_mediation(hub, community)
  10 + mediation = CommunityHubPlugin::Mediation.new(:profile => community)
  11 + mediation.name = CommunityHubPlugin::Mediation.timestamp
  12 + mediation.save!
  13 + mediation
  14 +end
  15 +
  16 +def create_message(hub,user)
  17 + message = Comment.new
  18 + message.author = user
  19 + message.title = "hub-message-#{(Time.now.to_f * 1000).to_i}"
  20 + message.body = 'body'
  21 + message.article = hub
  22 + message.save!
  23 + message
  24 +end
  25 +
  26 +def create_mediation(hub, user, community)
  27 +
  28 + #raise community.inspect
  29 + mediation = CommunityHubPlugin::Mediation.new
  30 + mediation.name = CommunityHubPlugin::Mediation.timestamp
  31 + mediation.profile = community
  32 + mediation.last_changed_by = user
  33 + mediation.created_by_id = user.id
  34 + mediation.source = 'local'
  35 + mediation.parent_id = hub.id
  36 + mediation.save!
  37 + mediation
  38 +end
... ...
plugins/community_hub/test/unit/community_hub_plugin/hub_helper_test.rb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +require File.dirname(__FILE__) + '/../../test_helper'
  2 +
  3 +class HubHelperTest < ActiveSupport::TestCase
  4 +
  5 + include CommunityHubPlugin::HubHelper
  6 + include NoosferoTestHelper
  7 +
  8 + should 'return time formated to hh:mm' do
  9 + t = Time.utc(2014,"jan",1,17,40,0)
  10 + assert_equal post_time(t), "17:40"
  11 + end
  12 +
  13 + should 'return empty string if param is not time' do
  14 + i = 1
  15 + assert_equal post_time(i), ''
  16 + end
  17 +
  18 +end
... ...
plugins/community_hub/test/unit/community_hub_plugin/hub_test.rb 0 → 100644
... ... @@ -0,0 +1,128 @@
  1 +require File.dirname(__FILE__) + '/../../test_helper'
  2 +
  3 +class HubTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @env = fast_create(Environment)
  7 + @user = create_user('testuser', :environment => @env).person
  8 + @comm = fast_create(Community, :environment_id => @env.id)
  9 + @hub = create_hub('hub', @comm, @user)
  10 + end
  11 +
  12 + should 'has setting twitter_enable' do
  13 + assert_respond_to @hub, :twitter_enabled
  14 + end
  15 +
  16 + should 'default value of setting twitter_enabled is false' do
  17 + assert_equal @hub.twitter_enabled, false
  18 + end
  19 +
  20 + should 'has setting twitter_hashtags' do
  21 + assert_respond_to @hub, :twitter_hashtags
  22 + end
  23 +
  24 + should 'default value of setting twitter_hashtags is blank' do
  25 + assert_equal @hub.twitter_hashtags, ""
  26 + end
  27 +
  28 + should 'has setting twitter_consumer_key' do
  29 + assert_respond_to @hub, :twitter_consumer_key
  30 + end
  31 +
  32 + should 'default value of setting twitter_consumer is blank' do
  33 + assert_equal @hub.twitter_consumer_key, ""
  34 + end
  35 +
  36 + should 'has setting twitter_consumer_secret' do
  37 + assert_respond_to @hub, :twitter_consumer_secret
  38 + end
  39 +
  40 + should 'default value of setting twitter_consumer_secret is blank' do
  41 + assert_equal @hub.twitter_consumer_secret, ""
  42 + end
  43 +
  44 + should 'has setting twitter_access_token' do
  45 + assert_respond_to @hub, :twitter_access_token
  46 + end
  47 +
  48 + should 'default value of setting twitter_access_token is blank' do
  49 + assert_equal @hub.twitter_access_token, ""
  50 + end
  51 +
  52 + should 'has setting twitter_access_token_secret' do
  53 + assert_respond_to @hub, :twitter_access_token_secret
  54 + end
  55 +
  56 + should 'default value of setting twitter_access_token_secret' do
  57 + assert_equal @hub.twitter_access_token_secret, ""
  58 + end
  59 +
  60 + should 'has setting facebook_enabled' do
  61 + assert_respond_to @hub, :facebook_enabled
  62 + end
  63 +
  64 + should 'default value of setting facebook_enabled is false' do
  65 + assert_equal @hub.facebook_enabled, false
  66 + end
  67 +
  68 + should 'has setting facebook_pooling_time' do
  69 + assert_respond_to @hub, :facebook_pooling_time
  70 + end
  71 +
  72 + should 'default value of setting facebook_pooling_time id five (5)' do
  73 + assert_equal @hub.facebook_pooling_time, 5
  74 + end
  75 +
  76 + should 'has setting facebook_access_token' do
  77 + assert_respond_to @hub, :facebook_access_token
  78 + end
  79 +
  80 + should 'default value of setting facebook_access_token is blank' do
  81 + assert_equal @hub.facebook_access_token, ""
  82 + end
  83 +
  84 + should 'has pinned_messages' do
  85 + assert_respond_to @hub, :pinned_messages
  86 + end
  87 +
  88 + should 'default value of pinned_messags' do
  89 + assert_equal @hub.pinned_messages, []
  90 + end
  91 +
  92 + should 'has pinned_mediations' do
  93 + assert_respond_to @hub, :pinned_mediations
  94 + end
  95 +
  96 + should 'default value of pinned_mediations' do
  97 + assert_equal @hub.pinned_mediations, []
  98 + end
  99 +
  100 + should 'has mediators' do
  101 + assert_respond_to @hub, :mediators
  102 + end
  103 +
  104 + should 'hub creator is mediator by default' do
  105 + assert @hub.mediators.include?(@user.id)
  106 + end
  107 +
  108 + should 'describe yourself' do
  109 + assert CommunityHubPlugin::Hub.description
  110 + end
  111 +
  112 + should 'has a short descriptionf' do
  113 + assert CommunityHubPlugin::Hub.short_description
  114 + end
  115 +
  116 + should 'accept comments by default' do
  117 + assert @hub.accept_comments?
  118 + end
  119 +
  120 + should 'do not notify comments by default' do
  121 + assert !@hub.notify_comments
  122 + end
  123 +
  124 + should 'has a view page' do
  125 + assert @hub.view_page
  126 + end
  127 +
  128 +end
... ...
plugins/community_hub/test/unit/community_hub_plugin/listener_test.rb 0 → 100644
... ... @@ -0,0 +1,31 @@
  1 +require File.dirname(__FILE__) + '/../../test_helper'
  2 +
  3 +class ListenerTest < ActiveSupport::TestCase
  4 +
  5 + should 'initialize logger' do
  6 + logger = CommunityHubPlugin::Listener.initialize_logger
  7 + logfile = logger.instance_variable_get(:@logdev).instance_variable_get(:@filename)
  8 + assert_instance_of(Logger, logger)
  9 + assert File.exists?(logfile)
  10 + end
  11 +
  12 + should 'log message' do
  13 + logdir = File.join(Rails.root, 'log', CommunityHubPlugin::Listener.name.underscore)
  14 +
  15 + if File.exists?(logdir)
  16 + Dir.foreach(logdir) { |f|
  17 + fn = File.join(logdir, f);
  18 + File.delete(fn) if f != '.' && f != '..'
  19 + }
  20 + end
  21 +
  22 + logger = CommunityHubPlugin::Listener.initialize_logger
  23 + CommunityHubPlugin::Listener.log('testmessage')
  24 +
  25 + logfile = logger.instance_variable_get(:@logdev).instance_variable_get(:@filename)
  26 + text = File.open(logfile).read
  27 +
  28 + assert_match /testmessage/, text
  29 + end
  30 +
  31 +end
... ...
plugins/community_hub/test/unit/community_hub_plugin/mediation_test.rb 0 → 100644
... ... @@ -0,0 +1,33 @@
  1 +require File.dirname(__FILE__) + '/../../test_helper'
  2 +
  3 +class MediationTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @env = fast_create(Environment)
  7 + @user = create_user('testuser', :environment => @env).person
  8 + @comm = fast_create(Community, :environment_id => @env.id)
  9 + @hub = create_hub('hub', @comm, @user)
  10 + @mediation = create_mediation(@hub, @user, @comm)
  11 + end
  12 +
  13 + should 'has setting profile_picture' do
  14 + assert_respond_to @mediation, :profile_picture
  15 + end
  16 +
  17 + should 'default value of setting profile_picture is blank' do
  18 + assert_equal @mediation.profile_picture, ""
  19 + end
  20 +
  21 + should 'generate timestamp for mediation' do
  22 + assert CommunityHubPlugin::Mediation.timestamp
  23 + end
  24 +
  25 + should 'default value of advertise is false' do
  26 + assert !@mediation.advertise
  27 + end
  28 +
  29 + should 'default value of notify comments is false' do
  30 + assert !@mediation.notify_comments
  31 + end
  32 +
  33 +end
... ...
plugins/community_hub/test/unit/community_hub_plugin_test.rb 0 → 100644
... ... @@ -0,0 +1,49 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class CommunityHubPluginTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @plugin = CommunityHubPlugin.new
  7 + @profile = fast_create(Community)
  8 + @params = {}
  9 + @plugin.stubs(:context).returns(self)
  10 + end
  11 +
  12 + attr_reader :profile, :params
  13 +
  14 + should 'has name' do
  15 + assert CommunityHubPlugin.plugin_name
  16 + end
  17 +
  18 + should 'describe yourself' do
  19 + assert CommunityHubPlugin.plugin_description
  20 + end
  21 +
  22 + should 'has stylesheet' do
  23 + assert @plugin.stylesheet?
  24 + end
  25 +
  26 + should 'return Hub as a content type if profile is a community' do
  27 + assert_includes @plugin.content_types, CommunityHubPlugin::Hub
  28 + end
  29 +
  30 + should 'do not return Hub as a content type if profile is not a community' do
  31 + @profile = Organization.new
  32 + assert_not_includes @plugin.content_types, CommunityHubPlugin::Hub
  33 + end
  34 +
  35 + should 'do not return Hub as a content type if there is a parent' do
  36 + parent = fast_create(Blog, :profile_id => @profile.id)
  37 + @params[:parent_id] = parent.id
  38 + assert_not_includes @plugin.content_types, CommunityHubPlugin::Hub
  39 + end
  40 +
  41 + should 'return true at content_remove_new if page is a Hub' do
  42 + assert @plugin.content_remove_new(CommunityHubPlugin::Hub.new)
  43 + end
  44 +
  45 + should 'return false at content_remove_new if page is not a Hub' do
  46 + assert !@plugin.content_remove_new(Article.new)
  47 + end
  48 +
  49 +end
... ...
plugins/community_hub/twitter/stream.rb 0 → 100644
... ... @@ -0,0 +1,63 @@
  1 +require 'rubygems'
  2 +require 'twitter'
  3 +require 'iconv'
  4 +
  5 +#Filters non-UTF8 octets
  6 +def UTF8Filter(string)
  7 + ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
  8 + #Attention please, don't remove + ' ')[0..-2] it is used for UTF8 validation
  9 + ic.iconv(string + ' ')[0..-2]
  10 +end
  11 +
  12 +def listen_twitter_stream(hub, author_id)
  13 +
  14 + connected = false
  15 + tries = 0
  16 + while !connected
  17 + begin
  18 + tries += 1
  19 + client = Twitter::Streaming::Client.new do |config|
  20 + config.consumer_key = hub.twitter_consumer_key
  21 + config.consumer_secret = hub.twitter_consumer_secret
  22 + config.access_token = hub.twitter_access_token
  23 + config.access_token_secret = hub.twitter_access_token_secret
  24 + end
  25 + puts client.inspect
  26 + connected = true
  27 + tries = 0
  28 + rescue => e
  29 + puts "Error connecting to twitter stream: #{e.inspect}"
  30 + sleep (10 + 2 ** tries)
  31 + end
  32 + end
  33 +
  34 + tries = 0
  35 + while true
  36 + begin
  37 + tries += 1
  38 + client.filter(:track => hub.twitter_hashtags) do |object|
  39 + if object.is_a?(Twitter::Tweet)
  40 + comment = Comment.new
  41 + comment.title = 'hub-message-twitter'
  42 + comment.source = hub
  43 + comment.body = UTF8Filter(object.text)
  44 + comment.profile_picture = object.user.profile_image_url.to_s
  45 + comment.author_id = author_id
  46 + comment.name = UTF8Filter(object.user.screen_name)
  47 + comment.email = 'admin@localhost.local'
  48 +
  49 + tries = 0
  50 + begin
  51 + comment.save!
  52 + rescue => e
  53 + puts "Error writing twitter comment #{e.inspect}"
  54 + end
  55 + end
  56 + end
  57 + rescue => e
  58 + puts "Error reading twitter stream #{e.inspect}"
  59 + sleep (10 + 2 ** tries)
  60 + break
  61 + end
  62 + end
  63 +end
... ...
plugins/community_hub/views/cms/community_hub_plugin/_hub.html.erb 0 → 100644
... ... @@ -0,0 +1,36 @@
  1 +<div class='hub'>
  2 + <h1><%= _("HUB Settings:") %></h1>
  3 + <%= required_fields_message %>
  4 + <div>
  5 + <%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64', :maxlength => 150)) %>
  6 + </div>
  7 + <div>
  8 + <%= required labelled_form_field(_('Description'), text_area(:article, 'body', :style => 'width: 99%;')) %>
  9 + </div>
  10 + <br />
  11 +
  12 + <div>
  13 + <%= _('General Streaming Settings:') %>
  14 + </div>
  15 + <br />
  16 + <div>
  17 + <%= _('Twitter Settings:') %>
  18 + </div>
  19 + <br />
  20 + <%= check_box(:article, :twitter_enabled) %> <span><%= _("Turn on TWITTER") %></span>
  21 + <br /><br />
  22 + <span><%= required labelled_form_field(_('Twitter\'s Hashtags, comma separated words<br>(example: participa.br,participabr,arenanetmundial,netmundial'), text_field(:article, :twitter_hashtags)) %></span>
  23 + <br />
  24 + <div>
  25 + <%= _('Facebook Settings:') %>
  26 + </div>
  27 + <br />
  28 + <%= check_box(:article, :facebook_enabled) %> <span><%= _("Turn on FACEBOOK") %></span>
  29 + <br /><br />
  30 + <span><%= required labelled_form_field(_('Facebook\'s hashtag (example: #participabr)'), text_field(:article, :facebook_hashtag)) %></span>
  31 + <br />
  32 + <span><%= required labelled_form_field(_('Facebook\'s access token'), text_field(:article, :facebook_access_token)) %></span>
  33 + <br />
  34 + <a href='https://smashballoon.com/custom-facebook-feed/access-token/' ><%= _('How to get a new access token?') %><a>
  35 + <br />
  36 +</div>
0 37 \ No newline at end of file
... ...
plugins/community_hub/views/community_hub_plugin_public/_banner.html.erb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<div class="banner">
  2 + <span><%= _("BANNER SPACE") %></span>
  3 +</div>
0 4 \ No newline at end of file
... ...
plugins/community_hub/views/community_hub_plugin_public/_embed.html.erb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<% extend CommunityHubPlugin::HubHelper %>
  2 +
  3 +<div class="embed">
  4 + <span class="label">Embed / <%= _("Embed") %></span>
  5 + <textarea cols="38"
  6 + id="comment_body"
  7 + name="comment[body]"
  8 + rows="10"
  9 + class="code"
  10 + style="width: 99%;"><%= embed_code(@page) %></textarea>
  11 +</div>
0 12 \ No newline at end of file
... ...
plugins/community_hub/views/community_hub_plugin_public/_mediation.html.erb 0 → 100644
... ... @@ -0,0 +1,87 @@
  1 +<% extend CommunityHubPlugin::HubHelper %>
  2 +
  3 +<li id="<%= mediation.id %>" class="post">
  4 +
  5 + <ul>
  6 + <li class="time"><%= post_time(mediation.created_at) %></li>
  7 +
  8 + <li class="avatar">
  9 + <% if mediation.source == 'twitter' %>
  10 + <%= image_tag(mediation.profile_picture, :alt => "Twitter") %>
  11 + <% elsif mediation.source == 'facebook' %>
  12 + <%= image_tag('/plugins/community_hub/icons/logo_facebook_50x50.png', :alt => "Facebook") %>
  13 + <% else %>
  14 + <%= image_tag(profile_icon(mediation.author, :minor)) if mediation.author %>
  15 + <% end %>
  16 + </li>
  17 + <li class="message"><span class="author"><%= mediation.setting[:author_name] %>:</span> <%= mediation.body %></li>
  18 +
  19 + <% if mediator?(hub) %>
  20 + <li class="mediation-bar">
  21 +
  22 + <ul>
  23 +
  24 + <% if mediation.source != 'twitter' && mediation.source != 'facebook' %>
  25 +
  26 + <li class="promote">
  27 + <% if !promoted?(hub, mediation.author.id) %>
  28 + <a id="<%= mediation.id %>" href="#" onclick="promote_user(<%= mediation.id %>,<%= mediation.author.id %>); return false;">
  29 + <img class="not-promoted" src="/plugins/community_hub/icons/hub-not-promote-icon.png" title="<%= _("User not promoted") %>" />
  30 + </a>
  31 + <% else %>
  32 + <img class="promoted" src="/plugins/community_hub/icons/hub-not-promote-icon.png" title="<%= _("User promoted") %>" />
  33 + <% end %>
  34 + </li>
  35 +
  36 + <% end %>
  37 +
  38 + <% if pinned_mediation?(hub, mediation.id) %>
  39 + <li class="pin">
  40 + <img class="pinned" src="/plugins/community_hub/icons/hub-not-pinned-icon.png" title="<%= _("Message pinned")%>" />
  41 + </li>
  42 + <% end %>
  43 +
  44 + </ul>
  45 +
  46 + </li>
  47 + <% end %>
  48 +
  49 + </ul>
  50 +
  51 + <% total_mediation_comments = mediation.comments.count %>
  52 +
  53 + <span class="comment-count">
  54 + <%= link_to( "<span id='mediation-comment-total-#{mediation.id}'>#{total_mediation_comments}</span> " + _("Comments") , '#',
  55 + :class => 'display-comment-form',
  56 + :id => 'top-post-comment-button',
  57 + :onclick => "toogle_mediation_comments(#{mediation.id}); return false;") %>
  58 + </span>
  59 +
  60 + <script type="text/javascript">
  61 + mediations.push( [ <%= mediation.id %>, setInterval(function() { update_mediation_comments('<%= mediation.id %>', false)}, 5000) ] );
  62 + </script>
  63 +
  64 + <ul id="mediation-comment-list-<%=mediation.id%>" class="mediation-comment-list" style="display:none;">
  65 + <% if mediation.accept_comments? && mediation.comments.count > 0 %>
  66 + <%= render :partial => 'community_hub_plugin_public/mediation_comment', :collection => mediation.comments %>
  67 + <% end %>
  68 + </ul>
  69 +
  70 + <% if logged_in? && mediation.accept_comments? %>
  71 + <div id='mediation-comment-form-<%=mediation.id%>' class='mediation-comment-form' style="display:none;">
  72 + <%= render :partial => 'community_hub_plugin_public/mediation_comment_form',
  73 + :locals => {
  74 + :hub => hub,
  75 + :mediation => mediation,
  76 + :comment => Comment.new,
  77 + :url => {
  78 + :controller => :comment,
  79 + :action => :create
  80 + },
  81 + :display_link => true,
  82 + :cancel_triggers_hide => true
  83 + } %>
  84 + </div>
  85 + <% end %>
  86 +
  87 +</li>
... ...
plugins/community_hub/views/community_hub_plugin_public/_mediation_comment.html.erb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +<li id="<%= mediation_comment.id %>" class="mediation-comment">
  2 + <ul>
  3 + <li class="avatar"><%= image_tag(profile_icon(mediation_comment.author, :minor)) %></li>
  4 + <li class="message"><span class="author"><%= mediation_comment.author_name %>:</span> <%= mediation_comment.body %></li>
  5 + </ul>
  6 +</li>
0 7 \ No newline at end of file
... ...
plugins/community_hub/views/community_hub_plugin_public/_mediation_comment_form.html.erb 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +<%= form_for :message,
  2 + :method => 'post',
  3 + :url => {
  4 + :controller => 'community_hub_plugin_public',
  5 + :action => 'new_message',
  6 + :article_id => mediation.id
  7 + } do |f| %>
  8 + <%= f.text_area(:body,
  9 + :rows => 4,
  10 + :placeholder => _('Type your comment here')) %>
  11 + <%= submit_button('add', _('Post'), :onclick => "new_mediation_comment(this,#{mediation.id}); return false;") %>
  12 +<% end %>
... ...
plugins/community_hub/views/community_hub_plugin_public/_post.html.erb 0 → 100644
... ... @@ -0,0 +1,34 @@
  1 +<% extend CommunityHubPlugin::HubHelper %>
  2 +
  3 +<li id="<%= post.id %>" class="post">
  4 + <ul>
  5 + <li class="time"><%= post_time(post.created_at) %></li>
  6 + <li class="avatar">
  7 + <% if post.title == 'hub-message-twitter' %>
  8 + <%= image_tag(post.profile_picture, :alt => "Twitter") %>
  9 + <% elsif post.title == 'hub-message-facebook' %>
  10 + <%= image_tag('/plugins/community_hub/icons/logo_facebook_50x50.png', :alt => "Facebook") %>
  11 + <% else %>
  12 + <%= image_tag(profile_icon(post.author, :minor)) if post.author %>
  13 + <% end %>
  14 + </li>
  15 + <li class="message"><span class="author"><%= post.author_name %>:</span> <%= post.body %></li>
  16 +
  17 + <% if mediator?(hub) && post.title != 'hub-message-facebook' %>
  18 + <li class="mediation-bar">
  19 + <ul>
  20 + <li class="pin">
  21 + <% if !pinned_message?(hub, post.id) %>
  22 + <a id="<%= post.id %>" href="#" onclick="pin_message(<%= post.id %>); return false;">
  23 + <img class="not-pinned" src="/plugins/community_hub/icons/hub-pinned-icon.png" title="<%= _("Pin message")%>" />
  24 + </a>
  25 + <% else %>
  26 + <img class="pinned" src="/plugins/community_hub/icons/hub-not-pinned-icon.png" title="<%= _("Message pinned")%>" />
  27 + <% end %>
  28 + </li>
  29 + </ul>
  30 + </li>
  31 + <% end %>
  32 +
  33 + </ul>
  34 +</li>
... ...
plugins/community_hub/views/community_hub_plugin_public/_settings.html.erb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +<div class="settings">
  2 + <ul class="settings">
  3 + <li class="general">
  4 + <%= link_to _("General settings"), :controller => 'cms', :action => 'edit', :id => @page.id %>
  5 + </li>
  6 + </ul>
  7 +</div>
... ...
plugins/community_hub/views/content_viewer/hub.rhtml 0 → 100644
... ... @@ -0,0 +1,96 @@
  1 +<% extend CommunityHubPlugin::HubHelper %>
  2 +
  3 +<div id="<%=@page.id%>" class="hub">
  4 +
  5 + <div class="title"><%= @page.title %> HUB</div>
  6 +
  7 + <div class="description"><%= @page.body %></div>
  8 +
  9 + <br />
  10 +
  11 + <div id="left-tab" class="live content-tab show">
  12 +
  13 + <h1 class="live">
  14 + <span class="on-air"><%= _("Live") %></span>
  15 + </h1>
  16 +
  17 + <h1 class="mediation">
  18 + <span class="title"><%= _("Mediation") %></span>
  19 + </h1>
  20 +
  21 + <div class="envelope">
  22 + <ul id="live-posts"></ul>
  23 + </div>
  24 +
  25 + <span><%= check_box_tag 'auto_scrolling', 'yes', true %><%= _("Auto scrolling") %></span>
  26 +
  27 + <% if logged_in? %>
  28 + <div id="input-panel">
  29 + <div class="form-message">
  30 +
  31 + <%= form_for :message,
  32 + :method => 'post',
  33 + :url => {
  34 + :controller => 'community_hub_plugin_public',
  35 + :action => 'new_message',
  36 + :article_id => @page.id
  37 + } do |f| %>
  38 + <span><%= _("Streaming") %></span>
  39 + <br />
  40 + <%= f.text_area :body, :style => "width: 99%;", :cols => "38", :rows => "5", :placeholder => _("Type your message here") %>
  41 + <%= submit_button('add', _('Post'), :onclick => 'new_message(this); return false;') %>
  42 + <% end %>
  43 +
  44 + </div>
  45 + </div>
  46 + <% end %>
  47 +
  48 + </div>
  49 +
  50 + <div id="right-tab" class="mediation content-tab hide">
  51 +
  52 + <h1 class="live">
  53 + <span class="on-air"><%= _("Live") %></span>
  54 + </h1>
  55 +
  56 + <h1 class="mediation">
  57 + <span class="title"><%= _("Mediation") %></span>
  58 + </h1>
  59 +
  60 + <div class="envelope">
  61 + <ul id="mediation-posts"></ul>
  62 + </div>
  63 +
  64 + <% if logged_in? && mediator?(@page) %>
  65 + <div class="form-mediation">
  66 +
  67 + <%= render :file => 'shared/tiny_mce' %>
  68 +
  69 + <%= form_for :article,
  70 + :method => 'post',
  71 + :url => {
  72 + :controller => 'community_hub_plugin_public',
  73 + :action => 'new_mediation',
  74 + :profile_id => profile.id
  75 + } do |f| %>
  76 + <%= f.hidden_field :parent_id, :value => @page.id %>
  77 + <%= f.text_area :body, :style => "width: 100%;", :class => "mceEditor" %>
  78 + <%= submit_button('add', _('Post'), :onclick => 'new_mediation(this); return false;') %>
  79 + <% end %>
  80 +
  81 + </div>
  82 +
  83 + <%= render :partial => "community_hub_plugin_public/settings" %>
  84 +
  85 + <% end %>
  86 +
  87 + </div>
  88 +
  89 +</div>
  90 +
  91 +<script type="text/javascript">
  92 + DEFAULT_PIN_QUESTION = '<%= _("Are you sure that you want to pin this message?") %>';
  93 + DEFAULT_PROMOTE_QUESTION = '<%= _("Are you sure that you want to promote this user?") %>';
  94 +</script>
  95 +
  96 +<%= javascript_include_tag '/plugins/community_hub/javascripts/community_hub.js' %>
... ...
public/javascripts/application.js
... ... @@ -683,7 +683,6 @@ function hide_and_show(hide_elements, show_elements) {
683 683  
684 684 function limited_text_area(textid, limit) {
685 685 var text = jQuery('#' + textid).val();
686   - grow_text_area(textid);
687 686 var textlength = text.length;
688 687 jQuery('#' + textid + '_left span').html(limit - textlength);
689 688 if (textlength > limit) {
... ... @@ -698,14 +697,9 @@ function limited_text_area(textid, limit) {
698 697 }
699 698 }
700 699  
701   -function grow_text_area(textid) {
702   - var height = jQuery('#' + textid).attr('scrollHeight');
703   - if (jQuery.browser.webkit) {
704   - height -= parseInt(jQuery('#' + textid).css('padding-top')) +
705   - parseInt(jQuery('#' + textid).css('padding-bottom'));
706   - }
707   - jQuery('#' + textid).css('height', height + 'px');
708   -}
  700 +jQuery(function($) {
  701 + $('.autogrow').autogrow();
  702 +});
709 703  
710 704 jQuery(function($) {
711 705 $('a').each(function() {
... ...
public/javascripts/autogrow.js 0 → 100644
... ... @@ -0,0 +1,93 @@
  1 +;(function($){
  2 + //pass in just the context as a $(obj) or a settings JS object
  3 + $.fn.autogrow = function(opts) {
  4 + var that = $(this).css({overflow: 'hidden', resize: 'none'}) //prevent scrollies
  5 + , selector = that.selector
  6 + , defaults = {
  7 + context: $(document) //what to wire events to
  8 + , animate: true //if you want the size change to animate
  9 + , speed: 200 //speed of animation
  10 + , fixMinHeight: true //if you don't want the box to shrink below its initial size
  11 + , cloneClass: 'autogrowclone' //helper CSS class for clone if you need to add special rules
  12 + , onInitialize: false //resizes the textareas when the plugin is initialized
  13 + }
  14 + ;
  15 + opts = $.isPlainObject(opts) ? opts : {context: opts ? opts : $(document)};
  16 + opts = $.extend({}, defaults, opts);
  17 + that.each(function(i, elem){
  18 + var min, clone;
  19 + elem = $(elem);
  20 + //if the element is "invisible", we get an incorrect height value
  21 + //to get correct value, clone and append to the body.
  22 + if (elem.is(':visible') || parseInt(elem.css('height'), 10) > 0) {
  23 + min = parseInt(elem.css('height'), 10) || elem.innerHeight();
  24 + } else {
  25 + clone = elem.clone()
  26 + .addClass(opts.cloneClass)
  27 + .val(elem.val())
  28 + .css({
  29 + position: 'absolute'
  30 + , visibility: 'hidden'
  31 + , display: 'block'
  32 + })
  33 + ;
  34 + $('body').append(clone);
  35 + min = clone.innerHeight();
  36 + clone.remove();
  37 + }
  38 + if (opts.fixMinHeight) {
  39 + elem.data('autogrow-start-height', min); //set min height
  40 + }
  41 + elem.css('height', min);
  42 +
  43 + if (opts.onInitialize) {
  44 + resize.call(elem);
  45 + }
  46 + });
  47 + opts.context
  48 + .on('keyup paste', selector, resize)
  49 + ;
  50 +
  51 + function resize (e){
  52 + var box = $(this)
  53 + , oldHeight = box.innerHeight()
  54 + , newHeight = this.scrollHeight
  55 + , minHeight = box.data('autogrow-start-height') || 0
  56 + , clone
  57 + ;
  58 + if (oldHeight < newHeight) { //user is typing
  59 + this.scrollTop = 0; //try to reduce the top of the content hiding for a second
  60 + opts.animate ? box.stop().animate({height: newHeight}, opts.speed) : box.innerHeight(newHeight);
  61 + } else if (!e || e.which == 8 || e.which == 46 || (e.ctrlKey && e.which == 88)) { //user is deleting, backspacing, or cutting
  62 + if (oldHeight > minHeight) { //shrink!
  63 + //this cloning part is not particularly necessary. however, it helps with animation
  64 + //since the only way to cleanly calculate where to shrink the box to is to incrementally
  65 + //reduce the height of the box until the $.innerHeight() and the scrollHeight differ.
  66 + //doing this on an exact clone to figure out the height first and then applying it to the
  67 + //actual box makes it look cleaner to the user
  68 + clone = box.clone()
  69 + .addClass(opts.cloneClass) //add clone class for extra css rules
  70 + .css({position: 'absolute', zIndex:-10}) //make "invisible"
  71 + .val(box.val()) //populate with content for consistent measuring
  72 + ;
  73 + box.after(clone); //append as close to the box as possible for best CSS matching for clone
  74 + do { //reduce height until they don't match
  75 + newHeight = clone[0].scrollHeight - 1;
  76 + clone.innerHeight(newHeight);
  77 + } while (newHeight === clone[0].scrollHeight);
  78 + newHeight++; //adding one back eliminates a wiggle on deletion
  79 + clone.remove();
  80 + box.focus(); // Fix issue with Chrome losing focus from the textarea.
  81 +
  82 + //if user selects all and deletes or holds down delete til beginning
  83 + //user could get here and shrink whole box
  84 + newHeight < minHeight && (newHeight = minHeight);
  85 + oldHeight > newHeight && opts.animate ? box.stop().animate({height: newHeight}, opts.speed) : box.innerHeight(newHeight);
  86 + } else { //just set to the minHeight
  87 + box.innerHeight(minHeight);
  88 + }
  89 + }
  90 + }
  91 + return that;
  92 + }
  93 +})(jQuery);
... ...
test/unit/person_test.rb
... ... @@ -1152,21 +1152,6 @@ class PersonTest &lt; ActiveSupport::TestCase
1152 1152 assert_includes Person.more_active, profile
1153 1153 end
1154 1154  
1155   - should 'handle multiparameter attribute exception on birth date field' do
1156   - assert_nothing_raised ActiveRecord::MultiparameterAssignmentErrors do
1157   - p = Person.new(
1158   - :name => 'birthday', :identifier => 'birthday',
1159   - 'birth_date(1i)' => '', 'birth_date(2i)' => '6', 'birth_date(3i)' => '16'
1160   - )
1161   - end
1162   - end
1163   -
1164   - should 'not accept an empty year on birth date' do
1165   - p = Person.new(:birth_date => "1115")
1166   - p.valid?
1167   - assert p.errors[:birth_date.to_s].present?
1168   - end
1169   -
1170 1155 should 'associate report with the correct complaint' do
1171 1156 p1 = create_user('user1').person
1172 1157 p2 = create_user('user2').person
... ...