Commit 87ccdfde777a2f6d1ef511c6a052ae68e298fee9

Authored by Victor Costa
2 parents 40c630fd 24b3013c
Exists in staging

Merge branch 'master' into staging

Showing 2490 changed files with 297841 additions and 297897 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 2490 files displayed.

.gitlab-ci.yml
... ... @@ -59,18 +59,18 @@ selenium-6:
59 59 # NOOSFERO_BUNDLE_OPTS=install makes migrations fails
60 60 # probably because of rubygems-integration
61 61 plugins-1:
62   - script: SLICE=1/5 bundle exec rake test:noosfero_plugins
  62 + script: SLICE=1/5 bundle exec rake test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install
63 63 stage: all-tests
64 64 plugins-2:
65   - script: SLICE=2/5 bundle exec rake test:noosfero_plugins
  65 + script: SLICE=2/5 bundle exec rake test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install
66 66 stage: all-tests
67 67 plugins-3:
68   - script: SLICE=3/5 bundle exec rake test:noosfero_plugins
  68 + script: SLICE=3/5 bundle exec rake test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install
69 69 stage: all-tests
70 70 plugins-4:
71   - script: SLICE=4/5 bundle exec rake test:noosfero_plugins
  71 + script: SLICE=4/5 bundle exec rake test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install
72 72 stage: all-tests
73 73 plugins-5:
74   - script: SLICE=5/5 bundle exec rake test:noosfero_plugins
  74 + script: SLICE=5/5 bundle exec rake test:noosfero_plugins NOOSFERO_BUNDLE_OPTS=install
75 75 stage: all-tests
76 76  
... ...
.travis.yml
... ... @@ -19,6 +19,10 @@ language: ruby
19 19 rvm:
20 20 - 2.3.1
21 21  
  22 +services:
  23 + - postgresql
  24 + - elasticsearch
  25 +
22 26 addons:
23 27 apt:
24 28 packages:
... ...
AUTHORS.md
... ... @@ -8,38 +8,42 @@ noosfero, that's not a problem).
8 8 Developers
9 9 ==========
10 10  
11   -Ábner Silva de Oliveira <abner.oliveira@serpro.gov.br>
12 11 Alan Freihof Tygel <alantygel@gmail.com>
  12 +Alessandro Caetano <alesasndro.caetanob@gmail.com>
  13 +Alessandro Caetano <alessandro.caetanob@gmail.com>
13 14 Alessandro Palmeira <alessandro.palmeira@gmail.com>
  15 +Alex Campelo <campelo.al1@gmail.com>
14 16 Alexandre Barbosa <alexandreab@live.com>
15 17 Alexandre Torres <alexandrekry@gmail.com>
16   -Alex Campelo <campelo.al1@gmail.com>
17   -Álvaro Fernando <alvarofernandoms@gmail.com>
  18 +Alipio muñiz <alipio@ecoalternative.net>
18 19 Ana Losnak <analosnak@gmail.com>
19 20 Ana Paula Vargas <anapaulavnoronha@gmail.com>
20 21 Andre Bedran <bedran.fleck@gmail.com>
21   -André Guedes <andrebsguedes@gmail.com>
22 22 Andrey Aleksanyants <aaleksanyants@yahoo.com>
  23 +André Guedes <andrebsguedes@gmail.com>
23 24 Antonio Terceiro <terceiro@colivre.coop.br>
24 25 Arthur Del Esposte <arthurmde@gmail.com>
  26 +Arthur Jahn <stutrzbecher@gmail.com>
  27 +Artur Bersan de Faria <arturbersan@gmail.com>
25 28 Athos Ribeiro <athoscribeiro@gmail.com>
26   -Aurelio A. Heckert <aurelio@colivre.coop.br>
  29 +Aurelio A. Heckert <aurium@colivre.coop.br>
  30 +Becca Cook <b.cook28@gmail.com>
27 31 Braulio Bhavamitra <braulio@eita.org.br>
28 32 Brenddon Gontijo <brenddongontijo@msn.com>
29 33 Caio Formiga <caio.formiga@gmail.com>
30   -Caio Salgado <caio.csalgado@gmail.com>
31 34 Caio SBA <caio@colivre.coop.br>
  35 +Caio Salgado <caio.csalgado@gmail.com>
32 36 Caio Tiago Oliveira <caiotiago@colivre.coop.br>
33 37 Carlos Andre de Souza <carlos.andre.souza@msn.com>
34 38 Carlos Morais <carlos88morais@gmail.com>
35 39 Carlos Purificacao <carloseugenio@gmail.com>
36 40 Christophe DANIEL <papaeng@gmail.com>
37   -Daniela Feitosa <alessandro.palmeira@gmail.com>
38 41 Daniel Alves <danpaulalves@gmail.com>
39   -Daniela Soares Feitosa <danielafeitosa@colivre.coop.br>
40 42 Daniel Bucher <daniel.bucher88@gmail.com>
41 43 Daniel Cunha <daniel@colivre.coop.br>
  44 +Daniel Henrique <danielhmarinho@gmail.com>
42 45 Daniel Tygel <dtygel@eita.org.br>
  46 +Daniela Soares Feitosa <danielafeitosa@colivre.coop.br>
43 47 David Carlos <ddavidcarlos1392@gmail.com>
44 48 Diego Araujo <diegoamc90@gmail.com>
45 49 Dylan Guedes <djmgguedes@gmail.com>
... ... @@ -48,76 +52,91 @@ Eduardo Passos &lt;eduardosteps@gmail.com&gt;
48 52 Eduardo Tourinho Edington <eduardo.edington@serpro.gov.br>
49 53 Eduardo Vital <vitaldu@gmail.com>
50 54 Evandro Magalhaes Leite Junior <evandro.leite@serpro.gov.br>
51   -Fabio Teixeira <fabio1079@gmail.com>
52 55 FAMMA TV NOTICIAS MEDIOS DE CO <revistafammatvmusic.oficial@gmail.com>
  56 +Fabio Teixeira <fabio1079@gmail.com>
  57 +Fagner Rodrigues <fagner128@gmail.com>
53 58 Fernanda Lopes <nanda.listas+psl@gmail.com>
54 59 Filipe Ribeiro <firibeiro77@live.com>
55 60 Francisco Marcelo de Araújo Lima Júnior <francisco.lima-junior@serpro.gov.br>
56   -Gabriela Navarro <navarro1703@gmail.com>
57 61 Gabriel Silva <gabriel93.silva@gmail.com>
  62 +Gabriel Silva <grabriel93.silva@gmail.com>
  63 +Gabriela Navarro <navarro1703@gmail.com>
58 64 Gonzalo Exequiel Pedone <hipersayan.x@gmail.com>
59 65 Grazieno Pellegrino <grazieno@gmail.com>
60 66 Guilherme C. Muniz <guilherme.cmuniz@gmail.com>
61 67 Guilherme Rojas <guilhermehrojas@gmail.com>
  68 +Gustavo Cavalcante <gustavo.cavalcante.oliveira@live.com>
62 69 Gustavo Coelho <gust.rod.coelho@gmail.com>
  70 +Gustavo Jaruga <darkshades@gmail.com>
63 71 Gustavo Jaruga <darksshades@gmail.com>
64 72 Hebert Douglas <hebertdougl@gmail.com>
65 73 Hugo Melo <hugo@riseup.net>
  74 +Iago Rodrigues <iago006@hotmailcom>
66 75 Iolane Andrade <andrade.icaa@gmail.com>
  76 +Iryna Pruitt <jdpruitt2807@prodigy.net>
67 77 Isaac Canan <isaac@intelletto.com.br>
68 78 Italo Valcy <italo@dcc.ufba.br>
  79 +Izharul Haq <atoz.chevara.2013@gmail.com>
69 80 Jefferson Fernandes <jeffs.fernandes@gmail.com>
70   -Jérôme Jutteau <j.jutteau@gmail.com>
71   -Jéssica Cristina <jessica.cris1127@gmail.com>
72   -João Machini
73   -João M. M. da Silva <jaodsilv@linux.ime.usp.br>
74 81 Joenio Costa <joenio@colivre.coop.br>
75   -Josef Spillner <josef.spillner@tu-dresden.de>
  82 +Joenio Costa <joenio@joenio.me>
76 83 Jose Pedro <1jpsneto@gmail.com>
  84 +Josef Spillner <josef.spillner@tu-dresden.de>
  85 +João M. M. da Silva <jaodsilv@linux.ime.usp.br>
  86 +João Machini
77 87 Junior Silva <juniorsilva1001@gmail.com>
  88 +Jérôme Jutteau <j.jutteau@gmail.com>
  89 +Jéssica Cristina <jessica.cris1127@gmail.com>
  90 +Karine Valença <valenca.karine@gmail.com>
78 91 Keilla Menezes <keilla@colivre.coop.br>
79 92 Larissa Reis <larissa@colivre.coop.br>
80 93 Leandro Alves <leandrosustenido@gmail.com>
81 94 Leandro Nunes dos Santos <leandro.santos@serpro.gov.br>
82 95 Leandro Veloso <leandrovelosorodrigues@gmail.com>
83 96 LinguÁgil 2010 <linguagil.bahia@gmail.com>
  97 +Luan Guimarães <guimaraesluan@me.com>
84 98 Lucas Couto <loc.unb@gmail.com>
85 99 Lucas Kanashiro <kanashiro.duarte@gmail.com>
86 100 Lucas Melo <lucaspradomelo@gmail.com>
  101 +Lucas Moura <lucas.moura128@gmail.com>
  102 +Lucas Severo <lucassalves65@gmail.com>
87 103 Luciano Prestes Cavalcanti <lucianopcbr@gmail.com>
88 104 Luis David Aguilar Carlos <ludwig9003@gmail.com>
89 105 Luiz Fernando de Freitas Matos <luiz@luizff.matos@gmail.com>
90 106 Luiz Matos <luizff.matos@gmail.com>
  107 +M for Momo <mo@rtnp.org>
91 108 Macartur de Sousa <macartur.sc@gmail.com>
92 109 Marcelo Júnior <maljunior@gmail.com>
93 110 Marcos Ramos <ms.ramos@outlook.com>
94 111 Marcos Ronaldo <marcos.rpj2@gmail.com>
95   -María Vecino <mariavecino@ecoalternative.net>
96 112 Mariel Zasso <noosfero-br@listas.softwarelivre.org>
97 113 Martín Olivera <molivera@solar.org.ar>
  114 +María Vecino <mariavecino@ecoalternative.net>
98 115 Matheus Faria <matheus.sousa.faria@gmail.com>
  116 +Matheus Miranda <matheusmirandalacerda@gmail.com>
99 117 Maurilio Atila <cabelotaina@gmail.com>
100 118 Melissa Wen <melissa.srw@gmail.com>
101   -M for Momo <mo@rtnp.org>
102 119 Michal Čihař <michal@cihar.com>
103 120 Michel Felipe de Oliveira Ferreira <michel.ferreira@serpro.gov.br>
104 121 Moises Machado <moises@colivre.coop.br>
105   -Naíla Alves <naila@colivre.coop.br>
  122 +Murilo Duarte <muriloduartegoncalves@hotmail.com>
106 123 Nanda Lopes <nanda.listas+psl@gmail.com>
  124 +Naíla Alves <naila@colivre.coop.br>
107 125 Niemand Jedermann <predatorix@web.de>
108 126 Omar Junior <omarroinuj@gmail.com>
109 127 Parley Martins <parleypachecomartins@gmail.com>
110 128 Paulo Meirelles <paulo@softwarelivre.org>
111   -Pedro de Lyra <pedrodelyra@gmail.com>
  129 +Paulo Tada <paulohtfs@gmail.com>
112 130 Pedro Leal
  131 +Pedro de Lyra <pedrodelyra@gmail.com>
113 132 Phillip Rohmberger <rohmberger@hotmail.de>
114   -Rafael de Souza Queiroz <querafael@live.com>
115 133 Rafael Gomes <rafaelgomes@techfree.com.br>
116 134 Rafael Martins <rmmartins@gmail.com>
117 135 Rafael Reggiani Manzo <rr.manzo@gmail.com>
  136 +Rafael de Souza Queiroz <querafael@live.com>
118 137 Raphaël Rousseau <raph@r4f.org>
119   -Raquel Lira <raquel.lira@gmail.com>
120 138 Raquel <rcordioli@gmail.com>
  139 +Raquel Lira <raquel.lira@gmail.com>
121 140 Renan Costa <renan2727@hotmail.com>
122 141 Renan Teruo <renanteruoc@gmail.com>
123 142 Rodrigo Medeiros <rodrigo.mss01@gmail.com>
... ... @@ -125,16 +144,18 @@ Rodrigo Siqueira &lt;siqueira@kuniri.org&gt;
125 144 Rodrigo Souto <rodrigo@colivre.coop.br>
126 145 Ronnie Simon <ronniesimonf@gmail.com>
127 146 Ronny Kursawe <kursawe.ronny@googlemail.com>
  147 +Sabryna Sousa <sabryna.sousa1323@gmail.com>
128 148 Samuel R. C. Vale <srcvale@holoscopio.com>
129 149 Simião Carvalho <simiaosimis@gmail.com>
  150 +TWS <tablettws@gmail.com>
130 151 Tallys Martins <tallysmartins@yahoo.com.br>
131 152 Thiago Casotti <thiago.casotti@uol.com.br>
132 153 Thiago Kairala <thiagor.kairala@gmail.com>
133 154 Thiago Ribeiro <thiagitosouza@hotmail.com>
134 155 Thiago Zoroastro <thiago.zoroastro@bol.com.br>
135 156 Tuux <tuxa@galaxie.eu.org>
136   -TWS <tablettws@gmail.com>
137 157 Valessio Brito <contato@valessiobrito.com.br>
  158 +Valet 322 <petymakar@gmail.com>
138 159 Victor Costa <vfcosta@gmail.com>
139 160 Victor Hugo Alves de Carvalho <victorhugodf.ac@gmail.com>
140 161 Victor Navarro <victor.matias.navarro@gmail.com>
... ... @@ -142,6 +163,9 @@ Vinicius Brand &lt;viniciuscb@gmail.com&gt;
142 163 Vitor Barbosa <vitornga15@gmail.com>
143 164 Wilton Rodrigues <braynwilton@gmail.com>
144 165 Yann Lugrin <yann.lugrin@liquid-concept.ch>
  166 +Ábner Silva de Oliveira <abner.oliveira@serpro.gov.br>
  167 +Álvaro Fernando <alvarofernandoms@gmail.com>
  168 +Вадим Кардашьян <iosphone77@gmail.com>
145 169  
146 170 Ideas, specifications and incentive
147 171 ===================================
... ...
Gemfile
... ... @@ -27,7 +27,7 @@ gem &#39;rest-client&#39;, &#39;~&gt; 1.6&#39;
27 27 gem 'exception_notification', '~> 4.0.1'
28 28 gem 'gettext', '~> 3.1', :require => false
29 29 gem 'locale', '~> 2.1'
30   -gem 'whenever', :require => false
  30 +gem 'whenever', '~> 0.9.4', :require => false
31 31 gem 'eita-jrails', '~> 0.10.0', require: 'jrails'
32 32 gem 'diffy', '~> 3.0'
33 33 gem 'slim'
... ... @@ -35,17 +35,16 @@ gem &#39;activerecord-session_store&#39;, (&#39;1.0.0.pre&#39; if RUBY_VERSION &gt;= &#39;2.3.0&#39;)
35 35  
36 36 # API dependencies
37 37 gem 'grape', '~> 0.12'
38   -gem 'grape-entity', '0.4.8'
  38 +gem 'grape-entity', '~>0.4.8'
39 39 gem 'grape_logging'
40 40 gem 'grape-swagger'
41 41 gem 'swagger-ui_rails'
42 42 gem 'kramdown'
43 43 gem 'rack-cors'
44 44 gem 'rack-contrib'
45   -gem 'liquid', '~> 3.0.3'
46 45  
47 46 gem 'api-pagination', '>= 4.1.1'
48   -gem 'liquid', '~> 3.0.3'
  47 +gem 'liquid', '>= 3.0.3'
49 48  
50 49 # asset pipeline
51 50 gem 'uglifier', '>= 1.0.3'
... ... @@ -81,10 +80,10 @@ end
81 80 group :cucumber do
82 81 gem 'capybara', '~> 2.2'
83 82 gem 'launchy'
84   - gem 'cucumber'
  83 + gem 'cucumber', '~> 1.3'
85 84 gem 'cucumber-rails', '~> 1.4.2', :require => false
86 85 gem 'database_cleaner', '~> 1.3'
87   - gem 'selenium-webdriver', '>= 2.50'
  86 + gem 'selenium-webdriver', '>= 2.53'
88 87 gem 'chromedriver-helper' if ENV['SELENIUM_DRIVER'] == 'chrome'
89 88 end
90 89  
... ...
HACKING.md
... ... @@ -31,7 +31,7 @@ If you want to use a different port than 3000, pass `-p &lt;PORT&gt;` to `./script/dev
31 31 Instructions for other systems
32 32 ------------------------------
33 33  
34   -On other OS, you have 2 options:
  34 +On other OS, you have many options:
35 35  
36 36 ### 1) using a chroot or a VM with Debian stable (easier)
37 37  
... ... @@ -45,6 +45,14 @@ You can check `./script/install-dependencies/debian-squeeze.sh` to have an idea
45 45  
46 46 If you write such script for your own OS, *please* share it with us at the development mailing list so that we can include it in the official repository. This way other people using the same OS will have to put less effort to develop Noosfero.
47 47  
  48 +### 3) Installing dependencies via Rubygems and RVM
  49 +
  50 +To setup the development environment through Rubygems you just need to install some basic deps and then install the gems defined on the Gemfile. Further instructions can be found on: http://noosfero.org/bin/view/Development/DepsWithRVMAndGems
  51 +
  52 +### 4) Using a docker image
  53 +
  54 +Use a docker image to run an out-of-the-box development environment. Further information can be found on: https://hub.docker.com/r/noosfero/dev-rails4/
  55 +
48 56 Submitting your changes back
49 57 ----------------------------
50 58  
... ...
app/api/entities.rb
... ... @@ -38,9 +38,17 @@ module Api
38 38 PERMISSIONS[current_permission] <= PERMISSIONS[permission]
39 39 end
40 40  
  41 + def self.expose_optional_field?(field, options = {})
  42 + return false if options[:params].nil?
  43 + optional_fields = options[:params][:optional_fields] || []
  44 + optional_fields.include?(field.to_s)
  45 + end
  46 +
  47 +
41 48 class Image < Entity
42 49 root 'images', 'image'
43 50  
  51 + expose :filename
44 52 expose :url do |image, options|
45 53 image.public_filename
46 54 end
... ... @@ -153,6 +161,9 @@ module Api
153 161 expose :articles_count do |person, options|
154 162 person.articles.count
155 163 end
  164 + expose :friends_count do |person, options|
  165 + person.friends.size
  166 + end
156 167 end
157 168  
158 169 class Enterprise < Profile
... ... @@ -166,7 +177,8 @@ module Api
166 177 community.admins.map{|admin| {"name"=>admin.name, "id"=>admin.id, "username" => admin.identifier}}
167 178 end
168 179 expose :categories, :using => Category
169   - expose :members, :using => Person , :if => lambda{ |community, options| community.display_info_to? options[:current_person] }
  180 + expose :members_count
  181 + expose :members, :if => lambda {|community, options| Entities.expose_optional_field?(:members, options)}
170 182 end
171 183  
172 184 class CommentBase < Entity
... ... @@ -174,6 +186,10 @@ module Api
174 186 expose :created_at, :format_with => :timestamp
175 187 expose :author, :using => Profile
176 188 expose :reply_of, :using => CommentBase
  189 + expose :permissions do |comment, options|
  190 + Entities.permissions_for_entity(comment, options[:current_person],
  191 + :allow_destroy?)
  192 + end
177 193 end
178 194  
179 195 class Comment < CommentBase
... ... @@ -209,7 +225,7 @@ module Api
209 225 expose :comments_count
210 226 expose :archived, :documentation => {:type => "Boolean", :desc => "Defines if a article is readonly"}
211 227 expose :type
212   - expose :comments, using: CommentBase, :if => lambda{|obj,opt| opt[:params] && ['1','true',true].include?(opt[:params][:show_comments])}
  228 + expose :comments, using: CommentBase, :if => lambda{|comment,options| Entities.expose_optional_field?(:comments, options)}
213 229 expose :published
214 230 expose :accept_comments?, as: :accept_comments
215 231 end
... ... @@ -304,7 +320,7 @@ module Api
304 320 end
305 321 expose :params, :if => lambda { |activity, options| activity.kind_of?(ActionTracker::Record)}
306 322 expose :content, :if => lambda { |activity, options| activity.kind_of?(Scrap)}
307   - expose :verb do |activity, options|
  323 + expose :verb do |activity, options|
308 324 activity.kind_of?(Scrap) ? 'leave_scrap' : activity.verb
309 325 end
310 326  
... ...
app/api/helpers.rb
... ... @@ -57,9 +57,11 @@ module Api
57 57 def present_partial(model, options)
58 58 if(params[:fields].present?)
59 59 begin
60   - fields = JSON.parse(params[:fields])
  60 + fields = JSON.parse((params.to_hash[:fields] || params.to_hash['fields']).to_json)
61 61 if fields.present?
62   - options.merge!(fields.symbolize_keys.slice(:only, :except))
  62 + fields = fields.symbolize_keys
  63 + options.merge!(:only => fields[:only]) if fields[:only].present?
  64 + options.merge!(:except => fields[:except]) if fields[:except].present?
63 65 end
64 66 rescue
65 67 fields = params[:fields]
... ... @@ -116,7 +118,7 @@ module Api
116 118 def post_article(asset, params)
117 119 return forbidden! unless current_person.can_post_content?(asset)
118 120  
119   - klass_type = params[:content_type] || params[:article].delete(:type) || TinyMceArticle.name
  121 + klass_type = params[:content_type] || params[:article].delete(:type) || TextArticle.name
120 122 return forbidden! unless klass_type.constantize <= Article
121 123  
122 124 article = klass_type.constantize.new(params[:article])
... ... @@ -447,12 +449,12 @@ module Api
447 449 end
448 450  
449 451 def asset_with_image params
450   - if params.has_key? :image_builder
  452 + if !params.nil? && params.has_key?(:image_builder)
451 453 asset_api_params = params
452 454 asset_api_params[:image_builder] = base64_to_uploadedfile(asset_api_params[:image_builder])
453 455 return asset_api_params
454 456 end
455   - params
  457 + params
456 458 end
457 459  
458 460 def base64_to_uploadedfile(base64_image)
... ... @@ -503,11 +505,9 @@ module Api
503 505  
504 506 def parse_content_type(content_type)
505 507 return nil if content_type.blank?
506   - content_types = content_type.split(',').map do |content_type|
507   - content_type = content_type.camelcase
508   - content_type == 'TextArticle' ? Article.text_article_types : content_type
  508 + content_type.split(',').map do |content_type|
  509 + content_type.camelcase
509 510 end
510   - content_types.flatten.uniq
511 511 end
512 512  
513 513 def period(from_date, until_date)
... ...
app/api/v1/articles.rb
... ... @@ -284,7 +284,7 @@ module Api
284 284  
285 285 if params[:path].present?
286 286 article = profile.articles.find_by path: params[:path]
287   - if !article || !article.display_to?(current_person)
  287 + if article && !article.display_to?(current_person)
288 288 article = forbidden!
289 289 end
290 290  
... ...
app/api/v1/blocks.rb
... ... @@ -6,6 +6,7 @@ module Api
6 6 get ':id' do
7 7 block = Block.find(params["id"])
8 8 return forbidden! unless block.visible_to_user?(current_person) || block.allow_edit?(current_person)
  9 + block.api_content_params = params.except("id")
9 10 present block, :with => Entities::Block, display_api_content: true, current_person: current_person
10 11 end
11 12  
... ...
app/api/v1/communities.rb
... ... @@ -18,7 +18,7 @@ module Api
18 18 communities = select_filtered_collection_of(environment, 'communities', params)
19 19 communities = profiles_for_person(communities, current_person)
20 20 communities = communities.by_location(params) # Must be the last. May return Exception obj
21   - present communities, :with => Entities::Community, :current_person => current_person
  21 + present communities, :with => Entities::Community, :current_person => current_person, :params => params
22 22 end
23 23  
24 24  
... ... @@ -49,7 +49,7 @@ module Api
49 49  
50 50 get ':id' do
51 51 community = profiles_for_person(environment.communities, current_person).find_by_id(params[:id])
52   - present community, :with => Entities::Community, :current_person => current_person
  52 + present community, :with => Entities::Community, :current_person => current_person, :params => params
53 53 end
54 54  
55 55 end
... ...
app/api/v1/profiles.rb
... ... @@ -28,7 +28,7 @@ module Api
28 28 authenticate!
29 29 profile = environment.profiles.find_by(id: params[:id])
30 30 return forbidden! unless profile.allow_edit?(current_person)
31   - profile.update_attributes!(params[:profile])
  31 + profile.update_attributes!(asset_with_image(params[:profile]))
32 32 present profile, :with => Entities::Profile, :current_person => current_person
33 33 end
34 34  
... ...
app/api/v1/session.rb
... ... @@ -174,14 +174,13 @@ module Api
174 174 # Example Request:
175 175 # PATCH /new_password?code=xxxx&password=secret&password_confirmation=secret
176 176 patch "/new_password" do
177   - change_password = ChangePassword.find_by code: params[:code]
178   - not_found! if change_password.nil?
179   -
180   - if change_password.update_attributes(:password => params[:password], :password_confirmation => params[:password_confirmation])
  177 + begin
  178 + change_password = ChangePassword.find_by! code: params[:code]
  179 + change_password.update_attributes!(:password => params[:password], :password_confirmation => params[:password_confirmation])
181 180 change_password.finish
182 181 present change_password.requestor.user, :with => Entities::UserLogin, :current_person => current_person
183   - else
184   - something_wrong!
  182 + rescue Exception => ex
  183 + render_api_error!(ex.message, 400)
185 184 end
186 185 end
187 186  
... ...
app/controllers/my_profile/cms_controller.rb
... ... @@ -146,11 +146,14 @@ class CmsController &lt; MyProfileController
146 146 parent = check_parent(params[:parent_id])
147 147 if parent
148 148 @article.parent = parent
  149 + @article.published = parent.published
  150 + @article.show_to_followers = parent.show_to_followers
149 151 @parent_id = parent.id
150 152 end
151 153  
152 154 @article.profile = profile
153 155 @article.author = user
  156 + @article.editor = current_person.editor
154 157 @article.last_changed_by = user
155 158 @article.created_by = user
156 159  
... ... @@ -399,8 +402,7 @@ class CmsController &lt; MyProfileController
399 402  
400 403 def available_article_types
401 404 articles = [
402   - TinyMceArticle,
403   - TextileArticle,
  405 + TextArticle,
404 406 Event
405 407 ]
406 408 articles += special_article_types if params && params[:cms]
... ... @@ -408,9 +410,6 @@ class CmsController &lt; MyProfileController
408 410 if @parent && @parent.blog?
409 411 articles -= Article.folder_types.map(&:constantize)
410 412 end
411   - if user.is_admin?(profile.environment)
412   - articles << RawHTMLArticle
413   - end
414 413 articles
415 414 end
416 415  
... ...
app/controllers/my_profile/profile_editor_controller.rb
... ... @@ -12,7 +12,7 @@ class ProfileEditorController &lt; MyProfileController
12 12 include CategoriesHelper
13 13  
14 14 def index
15   - @pending_tasks = Task.to(profile).pending.without_spam.select{|i| user.has_permission?(i.permission, profile)}
  15 + @pending_tasks = Task.to(profile).pending.without_spam
16 16 @show_appearance_option = user.is_admin?(environment) || environment.enabled?('enable_appearance')
17 17 @show_header_footer_option = user.is_admin?(environment) || (!profile.enterprise? && !environment.enabled?('disable_header_and_footer'))
18 18 end
... ... @@ -95,7 +95,7 @@ class ProfileEditorController &lt; MyProfileController
95 95 end
96 96  
97 97 def welcome_page
98   - @welcome_page = profile.welcome_page || TinyMceArticle.new(:name => 'Welcome Page', :profile => profile, :published => false)
  98 + @welcome_page = profile.welcome_page || TextArticle.new(:name => 'Welcome Page', :profile => profile, :published => false)
99 99 if request.post?
100 100 begin
101 101 @welcome_page.update!(params[:welcome_page])
... ...
app/controllers/public/account_controller.rb
... ... @@ -164,6 +164,7 @@ class AccountController &lt; ApplicationController
164 164 def logout
165 165 if logged_in?
166 166 self.current_user.forget_me
  167 + current_user.update({:chat_status_at => DateTime.now}.merge({:last_chat_status => current_user.chat_status, :chat_status => 'offline'}))
167 168 end
168 169 reset_session
169 170 session[:notice] = _("You have been logged out.")
... ...
app/controllers/public/chat_controller.rb
... ... @@ -113,8 +113,20 @@ class ChatController &lt; PublicController
113 113 end
114 114  
115 115 #TODO Ideally this is done through roster table on ejabberd.
116   - def roster_groups
117   - render :text => user.memberships.map {|m| {:jid => m.jid, :name => m.name}}.to_json
  116 + def rosters
  117 + rooms = user.memberships.map {|m| {:jid => m.jid, :name => m.name}}
  118 + friends = user.friends.map {|f| {:jid => f.jid, :name => f.name}}
  119 + rosters = {:rooms => rooms, :friends => friends}
  120 + render :text => rosters.to_json
  121 + end
  122 +
  123 + def availabilities
  124 + availabilities = user.friends.map do |friend|
  125 + status = friend.user.chat_status
  126 + status = 'offline' if status.blank?
  127 + {:jid => friend.jid, :status => status}
  128 + end
  129 + render :text => availabilities.to_json
118 130 end
119 131  
120 132 protected
... ...
app/controllers/public/invite_controller.rb
... ... @@ -98,6 +98,8 @@ class InviteController &lt; PublicController
98 98 scope = profile.invite_friends_only ? user.friends : environment.people
99 99 scope = scope.not_members_of(profile) if profile.organization?
100 100 scope = scope.not_friends_of(profile) if profile.person?
  101 + scope = scope.distinct(false).group("profiles.id")
  102 +
101 103 results = find_by_contents(:people, environment, scope, params['q'], {:page => 1}, {:joins => :user})[:results]
102 104 render :text => prepare_to_token_input(results).to_json
103 105 end
... ...
app/controllers/public/profile_controller.rb
... ... @@ -13,12 +13,13 @@ class ProfileController &lt; PublicController
13 13  
14 14 protect 'send_mail_to_members', :profile, :only => [:send_mail]
15 15  
  16 + ACTIVITIES_PER_PAGE = 15
  17 +
16 18 def index
17   - @network_activities = !@profile.is_a?(Person) ? @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) : []
18   - if logged_in? && current_person.follows?(@profile)
19   - @network_activities = @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) if @network_activities.empty?
20   - @activities = @profile.activities.paginate(:per_page => 15, :page => params[:page])
21   - end
  19 + @offsets = {:wall => 0, :network => 0}
  20 + page = (params[:page] || 1).to_i
  21 + @network_activities = loop_fetch_activities(@profile.tracked_notifications, :network, page) if !@profile.is_a?(Person) || follow_profile?
  22 + @activities = loop_fetch_activities(@profile.activities, :wall, page) if follow_profile?
22 23 @tags = profile.article_tags
23 24 allow_access_to_page
24 25 end
... ... @@ -231,6 +232,7 @@ class ProfileController &lt; PublicController
231 232 @scrap = Scrap.new(params[:scrap])
232 233 @scrap.sender= sender
233 234 @scrap.receiver= receiver
  235 + @scrap.marked_people = treat_followed_entries(params[:filter_followed])
234 236 @tab_action = params[:tab_action]
235 237 @message = @scrap.save ? _("Message successfully sent.") : _("You can't leave an empty message.")
236 238 activities = @profile.activities.paginate(:per_page => 15, :page => params[:page]) if params[:not_load_scraps].nil?
... ... @@ -249,18 +251,44 @@ class ProfileController &lt; PublicController
249 251 render :partial => 'profile_activities_list', :locals => {:activities => activities}
250 252 else
251 253 network_activities = @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page])
252   - render :partial => 'profile_network_activities', :locals => {:network_activities => network_activities}
  254 + render :partial => 'profile_network_activities', :locals => {:activities => network_activities}
253 255 end
254 256 end
255 257  
256   - def view_more_activities
257   - @activities = @profile.activities.paginate(:per_page => 10, :page => params[:page])
258   - render :partial => 'profile_activities_list', :locals => {:activities => @activities}
  258 + def search_followed
  259 + result = []
  260 + circles = find_by_contents(:circles, user, user.circles.where(:profile_type => 'Person'), params[:q])[:results]
  261 + followed = find_by_contents(:followed, user, Profile.followed_by(user), params[:q])[:results]
  262 + result = circles + followed
  263 + render :text => prepare_to_token_input_by_class(result).to_json
259 264 end
260 265  
261   - def view_more_network_activities
262   - @activities = @profile.tracked_notifications.paginate(:per_page => 10, :page => params[:page])
263   - render :partial => 'profile_network_activities', :locals => {:network_activities => @activities}
  266 + def loop_fetch_activities(base_activities, kind, page)
  267 + activities = nil
  268 + while activities.nil? || (activities.empty? && page <= activities.total_pages)
  269 + activities = base_activities.offset(@offsets[kind.to_sym]).paginate(:per_page => ACTIVITIES_PER_PAGE, :page => page)
  270 + activities = filter_activities(activities, kind.to_sym)
  271 + page += 1
  272 + end
  273 + activities
  274 + end
  275 +
  276 + def view_more_activities
  277 + @activities = nil
  278 + @offsets = params[:offsets]
  279 + page = (params[:page] || 1).to_i
  280 + kind = params[:kind]
  281 +
  282 + if kind == 'wall'
  283 + base_activities = @profile.activities
  284 + partial = 'profile_activities_list'
  285 + else
  286 + base_activities = @profile.tracked_notifications
  287 + partial = 'profile_network_activities'
  288 + end
  289 +
  290 + @activities = loop_fetch_activities(base_activities, kind, page)
  291 + render :partial => partial, :locals => {:activities => @activities}
264 292 end
265 293  
266 294 def more_comments
... ... @@ -434,7 +462,6 @@ class ProfileController &lt; PublicController
434 462 end
435 463 end
436 464  
437   -
438 465 protected
439 466  
440 467 def check_access_to_profile
... ... @@ -480,4 +507,39 @@ class ProfileController &lt; PublicController
480 507 render_not_found unless profile.allow_followers?
481 508 end
482 509  
  510 + def treat_followed_entries(entries)
  511 + return [] if entries.blank? || profile != user
  512 +
  513 + followed = []
  514 + entries.split(',').map do |entry|
  515 + klass, identifier = entry.split('_')
  516 + case klass
  517 + when 'Person'
  518 + followed << Person.find(identifier)
  519 + when 'Circle'
  520 + circle = Circle.find(identifier)
  521 + followed += Profile.in_circle(circle)
  522 + end
  523 + end
  524 + followed.uniq
  525 + end
  526 +
  527 + def filter_activities(activities, kind)
  528 + @offsets ||= {:wall => 0, :network => 0}
  529 + return activities if environment.admins.include?(user)
  530 + activities = Array(activities)
  531 + initial_count = activities.count
  532 + activities.delete_if do |activity|
  533 + activity = ActivityPresenter.for(activity)
  534 + next if activity.involved?(user)
  535 + activity.hidden_for?(user)
  536 + end
  537 + @offsets[kind] = @offsets[kind].to_i
  538 + @offsets[kind] += initial_count - activities.count
  539 + activities
  540 + end
  541 +
  542 + def follow_profile?
  543 + logged_in? && current_person.follows?(@profile)
  544 + end
483 545 end
... ...
app/helpers/application_helper.rb
... ... @@ -111,10 +111,6 @@ module ApplicationHelper
111 111 content = capture(&block)
112 112 end
113 113  
114   - if options[:type] == :textile
115   - content = RedCloth.new(content).to_html
116   - end
117   -
118 114 options[:class] = '' if ! options[:class]
119 115 options[:class] += ' button icon-help' # with-text
120 116  
... ... @@ -132,13 +128,6 @@ module ApplicationHelper
132 128 text
133 129 end
134 130  
135   - # alias for <tt>help(content, :textile)</tt>. You can pass a block in the
136   - # same way you would do if you called <tt>help</tt> directly.
137   - def help_textile(content = nil, link_name = nil, options = {}, &block)
138   - options[:type] = :textile
139   - help(content, link_name, options, &block)
140   - end
141   -
142 131 # TODO: do something more useful here
143 132 # TODO: test this helper
144 133 # TODO: add an icon?
... ... @@ -531,8 +520,6 @@ module ApplicationHelper
531 520 html = "\n"
532 521 values.each { |val, h_val|
533 522 id = object_name.to_s() +'_'+ method.to_s() +'_'+ val.to_s()
534   - # Não está apresentando o sexo selecionado ao revisitar
535   - # http://localhost:3000/myprofile/manuel/profile_editor/edit :-(
536 523 html += self.class.content_tag( 'span',
537 524 @template.radio_button( object_name, method, val,
538 525 :id => id, :object => @object ) +
... ... @@ -543,6 +530,7 @@ module ApplicationHelper
543 530 html += "<br />\n".html_safe
544 531 end
545 532 }
  533 + html = html.html_safe
546 534 html += "<br />\n".html_safe if line_size == 0 || ( values.size % line_size ) > 0
547 535 column = object.class.columns_hash[method.to_s] if object
548 536 text =
... ... @@ -552,7 +540,7 @@ module ApplicationHelper
552 540 )
553 541 label_html = self.class.content_tag 'label', text,
554 542 :class => 'formlabel'
555   - control_html = self.class.content_tag 'div', html,
  543 + control_html = self.class.content_tag 'div', html.html_safe,
556 544 :class => 'formfield type-radio '+
557 545 'fieldgroup linesize'+line_size.to_s()
558 546  
... ... @@ -977,11 +965,16 @@ module ApplicationHelper
977 965 content_tag(:div, _('Source: %s') % source_url, :id => 'article-source') unless source_url.nil?
978 966 end
979 967  
980   - def task_information(task)
  968 + def task_information(task, params = {})
981 969 values = {}
982 970 values.merge!(task.information[:variables]) if task.information[:variables]
983 971 values.merge!({:requestor => link_to(task.requestor.name, task.requestor.url)}) if task.requestor
984   - values.merge!({:target => link_to(task.target.name, task.target.url)}) if (task.target && task.target.respond_to?(:url))
  972 + if (task.target && task.target.respond_to?(:url))
  973 + values.merge!({:target => link_to(task.target.name, task.target.url)})
  974 + target_detail = _("in %s").html_safe % values[:target]
  975 + target_detail = '' if task.target.identifier == params[:profile]
  976 + values.merge!({:target_detail => target_detail})
  977 + end
985 978 values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject
986 979 values.merge!({:linked_subject => link_to(content_tag('span', task.linked_subject[:text], :class => 'task_target'), task.linked_subject[:url])}) if task.linked_subject
987 980 (task.information[:message] % values).html_safe
... ... @@ -992,8 +985,8 @@ module ApplicationHelper
992 985 end
993 986  
994 987 def add_zoom_to_images
995   - stylesheet_link_tag('jquery.fancybox') +
996   - javascript_include_tag('jquery.fancybox.pack') +
  988 + stylesheet_link_tag('vendor/jquery.fancybox') +
  989 + javascript_include_tag('vendor/jquery.fancybox.pack') +
997 990 javascript_tag("apply_zoom_to_images(#{_('Zoom in').to_json})")
998 991 end
999 992  
... ... @@ -1185,10 +1178,10 @@ module ApplicationHelper
1185 1178 end
1186 1179  
1187 1180 controller_target = suggestion.suggestion_type == 'Person' ? :friends : :memberships
1188   - profiles << link_to("<big> +#{suggestion.profile_connections.count - 4}</big>", :controller => controller_target, :action => :connections, :id => suggestion.suggestion_id) if suggestion.profile_connections.count > 4
  1181 + profiles << link_to("<big> +#{suggestion.profile_connections.count - 4}</big>".html_safe, :controller => controller_target, :action => :connections, :id => suggestion.suggestion_id) if suggestion.profile_connections.count > 4
1189 1182  
1190 1183 if profiles.present?
1191   - content_tag(:div, profiles.join , :class => 'profile-connections')
  1184 + content_tag(:div, profiles.safe_join , :class => 'profile-connections')
1192 1185 else
1193 1186 ''
1194 1187 end
... ... @@ -1245,4 +1238,15 @@ module ApplicationHelper
1245 1238 content.html_safe
1246 1239 end
1247 1240  
  1241 + def current_editor_is?(editor)
  1242 + editor.blank? ? false : current_editor == editor
  1243 + end
  1244 +
  1245 + def current_editor(mode = '')
  1246 + editor = @article.editor || Article::Editor::TINY_MCE unless @article.nil?
  1247 + editor ||= (current_person.nil? || current_person.editor.nil?) ? Article::Editor::TINY_MCE : current_person.editor
  1248 + editor += '_' + mode unless mode.blank?
  1249 + editor
  1250 + end
  1251 +
1248 1252 end
... ...
app/helpers/article_helper.rb
... ... @@ -61,7 +61,9 @@ module ArticleHelper
61 61 'div',
62 62 check_box(:article, :display_versions) +
63 63 content_tag('label', _('I want this article to display a link to older versions'), :for => 'article_display_versions')
64   - ) : '')
  64 + ) : '') +
  65 +
  66 + (self.respond_to?(:extra_options) ? self.extra_options : "")
65 67 )
66 68 end
67 69  
... ... @@ -161,6 +163,10 @@ module ArticleHelper
161 163 array.map { |object| {:label => object.name, :value => object.name} }
162 164 end
163 165  
  166 + def prepare_to_token_input_by_class(array)
  167 + array.map { |object| {:id => "#{object.class.name}_#{object.id || object.name}", :name => "#{object.name} (#{_(object.class.name)})", :class => object.class.name}}
  168 + end
  169 +
164 170 def cms_label_for_new_children
165 171 _('New article')
166 172 end
... ...
app/helpers/chat_helper.rb
... ... @@ -14,7 +14,7 @@ module ChatHelper
14 14 :class => icon_class + ' simplemenu-trigger'
15 15 ) +
16 16 content_tag('ul',
17   - links.map{|link| content_tag('li', link_to(link[1], '', :class => link[0], :id => link[2], 'data-jid' => user.jid), :class => 'simplemenu-item') }.join("\n"),
  17 + links.map{|link| content_tag('li', link_to(link[1], '', :class => link[0], :id => link[2], 'data-jid' => user.jid), :class => 'simplemenu-item') }.join("\n").html_safe,
18 18 :style => 'display: none; z-index: 100',
19 19 :class => 'simplemenu-submenu'
20 20 ),
... ...
app/helpers/email_template_helper.rb
... ... @@ -6,7 +6,7 @@ module EmailTemplateHelper
6 6 params[:subject] = params[:email_template].parsed_subject(params[:template_params])
7 7 params[:content_type] = "text/html"
8 8 end
9   - mail(params.except(:email_template))
  9 + mail(params.except(:email_template, :template_params))
10 10 end
11 11  
12 12 end
... ...
app/helpers/gallery_helper.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +module GalleryHelper
  2 +
  3 + include ArticleHelper
  4 +
  5 + def extra_options
  6 + content_tag(
  7 + 'div',
  8 + check_box(:article, :allow_download) +
  9 + content_tag('label', _('Allow images from this gallery to be downloaded'), :for => 'article_allow_download')
  10 + )
  11 + end
  12 +
  13 +end
... ...
app/helpers/profile_editor_helper.rb
... ... @@ -158,4 +158,8 @@ module ProfileEditorHelper
158 158 end
159 159 end
160 160  
  161 + def select_editor(title, object, method, options)
  162 + labelled_form_field(title, select(object, method, current_person.available_editors.map { |k,v| [v, k] }))
  163 + end
  164 +
161 165 end
... ...
app/helpers/search_helper.rb
... ... @@ -45,7 +45,8 @@ module SearchHelper
45 45 def search_page_title(title, category = nil)
46 46 title = "<h1>" + title
47 47 title += ' - <small>' + category.name + '</small>' if category
48   - title + "</h1>"
  48 + title += "</h1>"
  49 + title.html_safe
49 50 end
50 51  
51 52 def category_context(category, url)
... ...
app/helpers/tags_helper.rb
... ... @@ -67,4 +67,17 @@ module TagsHelper
67 67 end.join("\n").html_safe
68 68 end
69 69  
  70 + def linked_article_tags(article)
  71 + if @profile
  72 + # We are rendering a page inside a profile, so link to the profile tag search.
  73 + url = { :controller => 'profile', :profile => @profile.identifier, :action => 'tags' }
  74 + tagname_option = :id
  75 + else
  76 + # We are rendering a page outside a profile, so link to the global tag search.
  77 + url = { :action => 'tag' }
  78 + tagname_option = :tag
  79 + end
  80 + article.tags.map { |t| link_to(t, url.merge(tagname_option=>t.name) ) }.join("\n")
  81 + end
  82 +
70 83 end
... ...
app/helpers/tinymce_helper.rb
... ... @@ -18,7 +18,8 @@ module TinymceHelper
18 18 insertdatetime media nonbreaking save table contextmenu directionality
19 19 emoticons template paste textcolor colorpicker textpattern],
20 20 :image_advtab => true,
21   - :language => tinymce_language
  21 + :language => tinymce_language,
  22 + :selector => '.' + current_editor(options[:mode])
22 23  
23 24 options[:toolbar1] = toolbar1(options[:mode])
24 25 options[:menubar] = menubar(options[:mode])
... ...
app/helpers/token_helper.rb
... ... @@ -5,10 +5,11 @@ module TokenHelper
5 5 end
6 6  
7 7 def token_input_field_tag(name, element_id, search_action, options = {}, text_field_options = {}, html_options = {})
8   - options[:min_chars] ||= 3
  8 + options[:min_chars] ||= 2
9 9 options[:hint_text] ||= _("Type in a search term")
10 10 options[:no_results_text] ||= _("No results")
11 11 options[:searching_text] ||= _("Searching...")
  12 + options[:placeholder] ||= 'null'
12 13 options[:search_delay] ||= 1000
13 14 options[:prevent_duplicates] ||= true
14 15 options[:backspace_delete_item] ||= false
... ... @@ -20,6 +21,9 @@ module TokenHelper
20 21 options[:on_delete] ||= 'null'
21 22 options[:on_ready] ||= 'null'
22 23 options[:query_param] ||= 'q'
  24 + options[:theme] ||= 'null'
  25 + options[:results_formatter] ||= 'null'
  26 + options[:token_formatter] ||= 'null'
23 27  
24 28 result = text_field_tag(name, nil, text_field_options.merge(html_options.merge({:id => element_id})))
25 29 result += javascript_tag("jQuery('##{element_id}')
... ... @@ -29,6 +33,7 @@ module TokenHelper
29 33 hintText: #{options[:hint_text].to_json},
30 34 noResultsText: #{options[:no_results_text].to_json},
31 35 searchingText: #{options[:searching_text].to_json},
  36 + placeholder: #{options[:placeholder].to_json},
32 37 searchDelay: #{options[:search_delay].to_json},
33 38 preventDuplicates: #{options[:prevent_duplicates].to_json},
34 39 backspaceDeleteItem: #{options[:backspace_delete_item].to_json},
... ... @@ -39,6 +44,9 @@ module TokenHelper
39 44 onAdd: #{options[:on_add]},
40 45 onDelete: #{options[:on_delete]},
41 46 onReady: #{options[:on_ready]},
  47 + theme: #{options[:theme] == 'null' ? options[:theme] : options[:theme].to_json},
  48 + resultsFormater: #{options[:results_formatter]},
  49 + tokenFormater: #{options[:token_formatter]},
42 50 });
43 51 ")
44 52 result += javascript_tag("jQuery('##{element_id}').focus();") if options[:focus]
... ...
app/jobs/notify_activity_to_profiles_job.rb
... ... @@ -11,7 +11,7 @@ class NotifyActivityToProfilesJob &lt; Struct.new(:tracked_action_id)
11 11 tracked_action = ActionTracker::Record.find(tracked_action_id)
12 12 return unless tracked_action.user.present?
13 13 target = tracked_action.target
14   - if target.is_a?(Community) && ( NOTIFY_ONLY_COMMUNITY.include?(tracked_action.verb) || ! target.public_profile )
  14 + if target.is_a?(Community) && NOTIFY_ONLY_COMMUNITY.include?(tracked_action.verb)
15 15 ActionTrackerNotification.create(:profile_id => target.id, :action_tracker_id => tracked_action.id)
16 16 return
17 17 end
... ... @@ -19,8 +19,13 @@ class NotifyActivityToProfilesJob &lt; Struct.new(:tracked_action_id)
19 19 # Notify the user
20 20 ActionTrackerNotification.create(:profile_id => tracked_action.user.id, :action_tracker_id => tracked_action.id)
21 21  
22   - # Notify all followers
23   - ActionTrackerNotification.connection.execute("INSERT INTO action_tracker_notifications(profile_id, action_tracker_id) SELECT DISTINCT c.person_id, #{tracked_action.id} FROM profiles_circles AS p JOIN circles as c ON c.id = p.circle_id WHERE p.profile_id = #{tracked_action.user.id} AND (c.person_id NOT IN (SELECT atn.profile_id FROM action_tracker_notifications AS atn WHERE atn.action_tracker_id = #{tracked_action.id}))")
  22 + if target.is_a?(Scrap) && target.marked_people.present?
  23 + # Notify only marked people
  24 + ActionTrackerNotification.connection.execute("INSERT INTO action_tracker_notifications(profile_id, action_tracker_id) SELECT DISTINCT profiles.id, #{tracked_action.id} FROM profiles WHERE profiles.id IN (#{target.marked_people.map(&:id).join(',')})")
  25 + else
  26 + # Notify all followers
  27 + ActionTrackerNotification.connection.execute("INSERT INTO action_tracker_notifications(profile_id, action_tracker_id) SELECT DISTINCT c.person_id, #{tracked_action.id} FROM profiles_circles AS p JOIN circles as c ON c.id = p.circle_id WHERE p.profile_id = #{tracked_action.user.id} AND (c.person_id NOT IN (SELECT atn.profile_id FROM action_tracker_notifications AS atn WHERE atn.action_tracker_id = #{tracked_action.id}))")
  28 + end
24 29  
25 30 if tracked_action.user.is_a? Organization
26 31 ActionTrackerNotification.connection.execute "insert into action_tracker_notifications(profile_id, action_tracker_id) " +
... ...
app/models/approve_article.rb
... ... @@ -9,9 +9,6 @@ class ApproveArticle &lt; Task
9 9 if target.person? && requestor != target
10 10 self.errors.add(:requestor, _('You can not post articles to other users.'))
11 11 end
12   - if target.organization? && !target.members.include?(requestor) && target.environment.portal_community != target
13   - self.errors.add(:requestor, _('Only members can post articles on communities.'))
14   - end
15 12 end
16 13 end
17 14  
... ...
app/models/article.rb
1 1  
2 2 class Article < ApplicationRecord
3 3  
  4 + module Editor
  5 + TEXTILE = 'textile'
  6 + TINY_MCE = 'tiny_mce'
  7 + RAW_HTML = 'raw_html'
  8 + end
  9 +
4 10 include SanitizeHelper
5 11  
6 12 attr_accessible :name, :body, :abstract, :profile, :tag_list, :parent,
... ... @@ -11,7 +17,7 @@ class Article &lt; ApplicationRecord
11 17 :highlighted, :notify_comments, :display_hits, :slug,
12 18 :external_feed_builder, :display_versions, :external_link,
13 19 :image_builder, :show_to_followers, :archived,
14   - :author, :display_preview, :published_at, :person_followers
  20 + :author, :display_preview, :published_at, :person_followers, :editor
15 21  
16 22 extend ActsAsHavingImage::ClassMethods
17 23 acts_as_having_image
... ... @@ -57,7 +63,7 @@ class Article &lt; ApplicationRecord
57 63 _('Content')
58 64 end
59 65  
60   - track_actions :create_article, :after_create, :keep_params => [:name, :url, :lead, :first_image], :if => Proc.new { |a| a.is_trackable? && !a.image? }
  66 + track_actions :create_article, :after_create, :keep_params => [:name, :url, :lead, :first_image], :if => Proc.new { |a| a.notifiable? }
61 67  
62 68 # xss_terminate plugin can't sanitize array fields
63 69 # sanitize_tag_list is used with SanitizeHelper
... ... @@ -177,10 +183,6 @@ class Article &lt; ApplicationRecord
177 183 end
178 184 end
179 185  
180   - def is_trackable?
181   - self.published? && self.notifiable? && self.advertise? && self.profile.public_profile
182   - end
183   -
184 186 def external_link=(link)
185 187 if !link.blank? && link !~ /^[a-z]+:\/\//i
186 188 link = 'http://' + link
... ... @@ -518,17 +520,12 @@ class Article &lt; ApplicationRecord
518 520 ['Folder', 'Blog', 'Forum', 'Gallery']
519 521 end
520 522  
521   - def self.text_article_types
522   - ['TextArticle', 'TextileArticle', 'TinyMceArticle']
523   - end
524   -
525 523 scope :published, -> { where 'articles.published = ?', true }
526 524 scope :folders, -> profile { where 'articles.type IN (?)', profile.folder_types }
527 525 scope :no_folders, -> profile { where 'articles.type NOT IN (?)', profile.folder_types }
528 526 scope :galleries, -> { where "articles.type IN ('Gallery')" }
529 527 scope :images, -> { where :is_image => true }
530 528 scope :no_images, -> { where :is_image => false }
531   - scope :text_articles, -> { where 'articles.type IN (?)', text_article_types }
532 529 scope :files, -> { where :type => 'UploadedFile' }
533 530 scope :with_types, -> types { where 'articles.type IN (?)', types }
534 531  
... ... @@ -711,10 +708,6 @@ class Article &lt; ApplicationRecord
711 708 false
712 709 end
713 710  
714   - def tiny_mce?
715   - false
716   - end
717   -
718 711 def folder?
719 712 false
720 713 end
... ... @@ -848,7 +841,7 @@ class Article &lt; ApplicationRecord
848 841 end
849 842  
850 843 def create_activity
851   - if is_trackable? && !image?
  844 + if notifiable? && !image?
852 845 save_action_for_verb 'create_article', [:name, :url, :lead, :first_image], Proc.new{}, :author
853 846 end
854 847 end
... ... @@ -879,6 +872,10 @@ class Article &lt; ApplicationRecord
879 872 true
880 873 end
881 874  
  875 + def editor?(editor)
  876 + self.editor == editor
  877 + end
  878 +
882 879 private
883 880  
884 881 def sanitize_tag_list
... ...
app/models/block.rb
... ... @@ -320,6 +320,8 @@ class Block &lt; ApplicationRecord
320 320 false
321 321 end
322 322  
  323 + attr_accessor :api_content_params
  324 +
323 325 private
324 326  
325 327 def home_page_path
... ...
app/models/circle.rb
1 1 class Circle < ApplicationRecord
  2 + SEARCHABLE_FIELDS = {
  3 + :name => {:label => _('Name'), :weight => 1}
  4 + }
  5 +
  6 + _('Circle')
  7 +
2 8 has_many :profile_followers
3 9 belongs_to :person
4 10  
... ...
app/models/comment.rb
... ... @@ -212,6 +212,9 @@ class Comment &lt; ApplicationRecord
212 212 user == author || user == profile || user.has_permission?(:moderate_comments, profile)
213 213 end
214 214  
  215 + # method used by the API
  216 + alias_method :allow_destroy?, :can_be_destroyed_by?
  217 +
215 218 def can_be_marked_as_spam_by?(user)
216 219 return if user.nil?
217 220 user == profile || user.has_permission?(:moderate_comments, profile)
... ...
app/models/event.rb
... ... @@ -122,10 +122,6 @@ class Event &lt; Article
122 122 true
123 123 end
124 124  
125   - def tiny_mce?
126   - true
127   - end
128   -
129 125 def notifiable?
130 126 true
131 127 end
... ...
app/models/external_feed.rb
... ... @@ -25,7 +25,7 @@ class ExternalFeed &lt; ApplicationRecord
25 25 end
26 26 content = doc.to_s
27 27  
28   - article = TinyMceArticle.new
  28 + article = TextArticle.new
29 29 article.name = title
30 30 article.profile = blog.profile
31 31 article.body = content
... ...
app/models/favorite_enterprise_person.rb
... ... @@ -2,7 +2,7 @@ class FavoriteEnterprisePerson &lt; ApplicationRecord
2 2  
3 3 attr_accessible :person, :enterprise
4 4  
5   - track_actions :favorite_enterprise, :after_create, keep_params: [:enterprise_name, :enterprise_url], if: proc{ |f| f.is_trackable? }
  5 + track_actions :favorite_enterprise, :after_create, keep_params: [:enterprise_name, :enterprise_url], if: proc{ |f| f.notifiable? }
6 6  
7 7 belongs_to :enterprise
8 8 belongs_to :person
... ... @@ -13,7 +13,7 @@ class FavoriteEnterprisePerson &lt; ApplicationRecord
13 13  
14 14 protected
15 15  
16   - def is_trackable?
  16 + def notifiable?
17 17 self.enterprise.public?
18 18 end
19 19  
... ...
app/models/gallery.rb
1 1 class Gallery < Folder
2 2  
  3 + settings_items :allow_download, :type => :boolean, :default => false
  4 + attr_accessible :allow_download
  5 +
3 6 def self.type_name
4 7 _('Gallery')
5 8 end
... ...
app/models/organization.rb
... ... @@ -20,33 +20,28 @@ class Organization &lt; Profile
20 20 # visible.
21 21 # 4) The user is not a member of the organization but the organization is
22 22 # visible, public and enabled.
23   - def self.listed_for_person(person)
  23 + scope :listed_for_person, lambda { |person|
  24 +
24 25 joins('LEFT JOIN "role_assignments" ON ("role_assignments"."resource_id" = "profiles"."id"
25 26 AND "role_assignments"."resource_type" = \'Profile\') OR (
26 27 "role_assignments"."resource_id" = "profiles"."environment_id" AND
27 28 "role_assignments"."resource_type" = \'Environment\' )')
28 29 .joins('LEFT JOIN "roles" ON "role_assignments"."role_id" = "roles"."id"')
29 30 .where(
30   - ['( (roles.key = ? OR roles.key = ?) AND role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? )
31   - OR
32   - ( ( ( role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? ) OR
33   - ( profiles.enabled = ? ) ) AND
34   - ( profiles.visible = ? ) )',
35   - 'profile_admin', 'environment_administrator', Profile.name, person.id,
36   - Profile.name, person.id, true, true]
37   - ).uniq
38   - end
39   -
40   - def self.visible_for_person(person)
41   - listed_for_person(person).where(
42   - ['( (roles.key = ? OR roles.key = ?) AND role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? )
43   - OR
  31 + ['( (roles.key = ? OR roles.key = ?) AND role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? ) OR (
  32 + ( ( role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? ) OR ( profiles.enabled = ?)) AND (profiles.visible = ?) )',
  33 + 'profile_admin', 'environment_administrator', Profile.name, person.id, Profile.name, person.id, true, true]
  34 + ).uniq
  35 + }
  36 +
  37 + scope :visible_for_person, lambda { |person|
  38 + listed_for_person(person).where( ['
44 39 ( ( role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? ) OR
45   - ( profiles.enabled = ? AND profiles.public_profile = ? ) )',
46   - 'profile_admin', 'environment_administrator', Profile.name, person.id,
  40 + ( profiles.enabled = ? AND profiles.public_profile = ? ) )',
47 41 Profile.name, person.id, true, true]
48 42 )
49   - end
  43 + }
  44 +
50 45  
51 46 settings_items :closed, :type => :boolean, :default => false
52 47 def closed?
... ...
app/models/person.rb
1 1 # A person is the profile of an user holding all relationships with the rest of the system
2 2 class Person < Profile
3 3  
4   - attr_accessible :organization, :contact_information, :sex, :birth_date, :cell_phone, :comercial_phone, :jabber_id, :personal_website, :nationality, :address_reference, :district, :schooling, :schooling_status, :formation, :custom_formation, :area_of_study, :custom_area_of_study, :professional_activity, :organization_website, :following_articles
  4 + attr_accessible :organization, :contact_information, :sex, :birth_date, :cell_phone, :comercial_phone, :jabber_id, :personal_website, :nationality, :address_reference, :district, :schooling, :schooling_status, :formation, :custom_formation, :area_of_study, :custom_area_of_study, :professional_activity, :organization_website, :following_articles, :editor
5 5  
6 6 SEARCH_FILTERS = {
7 7 :order => %w[more_recent more_popular more_active],
... ... @@ -124,6 +124,8 @@ class Person &lt; Profile
124 124 where 'profile_suggestions.suggestion_type = ? AND profile_suggestions.enabled = ?', 'Community', true
125 125 }, through: :suggested_profiles, source: :suggestion
126 126  
  127 + has_and_belongs_to_many :marked_scraps, :join_table => :private_scraps, :class_name => 'Scrap'
  128 +
127 129 scope :more_popular, -> { order 'friends_count DESC' }
128 130  
129 131 scope :abusers, -> {
... ... @@ -344,6 +346,8 @@ class Person &lt; Profile
344 346  
345 347 validates_associated :user
346 348  
  349 + validates :editor, inclusion: { in: lambda { |p| p.available_editors } }
  350 +
347 351 def email
348 352 self.user.nil? ? nil : self.user.email
349 353 end
... ... @@ -616,8 +620,21 @@ class Person &lt; Profile
616 620 Profile.followed_by self
617 621 end
618 622  
  623 + def editor?(editor)
  624 + self.editor == editor
  625 + end
  626 +
619 627 def in_social_circle?(person)
620 628 self.is_a_friend?(person) || super
621 629 end
622 630  
  631 + def available_editors
  632 + available_editors = {
  633 + Article::Editor::TINY_MCE => _('TinyMCE'),
  634 + Article::Editor::TEXTILE => _('Textile')
  635 + }
  636 + available_editors.merge!({Article::Editor::RAW_HTML => _('Raw HTML')}) if self.is_admin?
  637 + available_editors
  638 + end
  639 +
623 640 end
... ...
app/models/profile.rb
... ... @@ -885,13 +885,14 @@ private :generate_url, :url_options
885 885  
886 886 # returns +true+ if the given +user+ can see profile information about this
887 887 # +profile+, and +false+ otherwise.
888   - def display_info_to?(user)
  888 + def display_info_to?(user = nil)
889 889 if self.public?
890 890 true
891 891 else
892 892 display_private_info_to?(user)
893 893 end
894 894 end
  895 + alias_method :display_to?, :display_info_to?
895 896  
896 897 after_save :update_category_from_region
897 898 def update_category_from_region
... ...
app/models/raw_html_article.rb
... ... @@ -1,17 +0,0 @@
1   -class RawHTMLArticle < TextArticle
2   -
3   - def self.type_name
4   - _('HTML')
5   - end
6   -
7   - def self.short_description
8   - _('Raw HTML text article')
9   - end
10   -
11   - def self.description
12   - _('Allows HTML without filter (only for admins).')
13   - end
14   -
15   - xss_terminate :only => [ ]
16   -
17   -end
app/models/scrap.rb
... ... @@ -2,7 +2,7 @@ class Scrap &lt; ApplicationRecord
2 2  
3 3 include SanitizeHelper
4 4  
5   - attr_accessible :content, :sender_id, :receiver_id, :scrap_id
  5 + attr_accessible :content, :sender_id, :receiver_id, :scrap_id, :marked_people
6 6  
7 7 SEARCHABLE_FIELDS = {
8 8 :content => {:label => _('Content'), :weight => 1},
... ... @@ -19,6 +19,8 @@ class Scrap &lt; ApplicationRecord
19 19 where profile_activities: {activity_type: 'Scrap'}
20 20 }, foreign_key: :activity_id, dependent: :destroy
21 21  
  22 + has_and_belongs_to_many :marked_people, :join_table => :private_scraps, :class_name => 'Person'
  23 +
22 24 after_create :create_activity
23 25 after_update :update_activity
24 26  
... ... @@ -65,6 +67,10 @@ class Scrap &lt; ApplicationRecord
65 67 sender != receiver && (is_root? ? root.receiver.receives_scrap_notification? : receiver.receives_scrap_notification?)
66 68 end
67 69  
  70 + def display_to?(user = nil)
  71 + marked_people.blank? || marked_people.include?(user)
  72 + end
  73 +
68 74 protected
69 75  
70 76 def create_activity
... ...
app/models/suggest_article.rb
... ... @@ -44,7 +44,7 @@ class SuggestArticle &lt; Task
44 44 type = article[:type].constantize
45 45 return type if type < Article
46 46 end
47   - TinyMceArticle
  47 + TextArticle
48 48 end
49 49  
50 50 def perform
... ... @@ -65,7 +65,7 @@ class SuggestArticle &lt; Task
65 65  
66 66 def information
67 67 variables = requestor.blank? ? {:requestor => sender} : {}
68   - { :message => _('%{requestor} suggested the publication of the article: %{subject}.').html_safe,
  68 + { :message => _('%{requestor} suggested the publication %{target_detail} of the article: %{subject}.').html_safe,
69 69 :variables => variables }
70 70 end
71 71  
... ...
app/models/text_article.rb
1 1 # a base class for all text article types.
2 2 class TextArticle < Article
3 3  
4   - xss_terminate :only => [ :name ], :on => 'validation'
  4 + def self.short_description
  5 + _('Text article')
  6 + end
  7 +
  8 + def self.description
  9 + _('Text article to create user content.')
  10 + end
  11 +
  12 + xss_terminate :only => [ :name, :body, :abstract ], :with => 'white_list', :on => 'validation', :if => lambda { |a| !a.editor?(Article::Editor::TEXTILE) && !a.editor?(Article::Editor::RAW_HTML) }
  13 +
  14 + include WhiteListFilter
  15 + filter_iframes :abstract, :body
  16 + def iframe_whitelist
  17 + profile && profile.environment && profile.environment.trusted_sites_for_iframe
  18 + end
5 19  
6 20 def self.type_name
7 21 _('Article')
... ... @@ -21,6 +35,18 @@ class TextArticle &lt; Article
21 35 true
22 36 end
23 37  
  38 + def can_display_media_panel?
  39 + true
  40 + end
  41 +
  42 + def self.can_display_blocks?
  43 + false
  44 + end
  45 +
  46 + def notifiable?
  47 + true
  48 + end
  49 +
24 50 before_save :set_relative_path
25 51  
26 52 def set_relative_path
... ... @@ -43,4 +69,24 @@ class TextArticle &lt; Article
43 69 parent && parent.kind_of?(Blog) && parent.display_preview
44 70 end
45 71  
  72 + def to_html(options ={})
  73 + content = super(options)
  74 + content = convert_textile_to_html(content) if self.editor?(Article::Editor::TEXTILE)
  75 + content
  76 + end
  77 +
  78 + def lead(length = nil)
  79 + content = super(length)
  80 + content = convert_textile_to_html(content) if self.editor?(Article::Editor::TEXTILE)
  81 + content
  82 + end
  83 +
  84 + protected
  85 +
  86 + def convert_textile_to_html(textile)
  87 + converter = RedCloth.new(textile|| '')
  88 + converter.hard_breaks = false
  89 + sanitize_html(converter.to_html, :white_list)
  90 + end
  91 +
46 92 end
... ...
app/models/textile_article.rb
... ... @@ -1,44 +0,0 @@
1   -class TextileArticle < TextArticle
2   - include SanitizeHelper
3   -
4   - def self.short_description
5   - _('Text article with Textile markup language')
6   - end
7   -
8   - def self.description
9   - _('Accessible alternative for visually impaired users.')
10   - end
11   -
12   - def to_html(options ={})
13   - convert_to_html(body)
14   - end
15   -
16   - def lead(length = nil)
17   - if abstract.blank?
18   - super
19   - else
20   - convert_to_html(abstract)
21   - end
22   - end
23   -
24   - def notifiable?
25   - true
26   - end
27   -
28   - def can_display_media_panel?
29   - true
30   - end
31   -
32   - def self.can_display_blocks?
33   - false
34   - end
35   -
36   - protected
37   -
38   - def convert_to_html(textile)
39   - converter = RedCloth.new(textile|| '')
40   - converter.hard_breaks = false
41   - sanitize_html(converter.to_html, :white_list)
42   - end
43   -
44   -end
app/models/tiny_mce_article.rb
... ... @@ -1,37 +0,0 @@
1   -class TinyMceArticle < TextArticle
2   -
3   - def self.short_description
4   - _('Text article with visual editor')
5   - end
6   -
7   - def self.description
8   - _('Not accessible for visually impaired users.')
9   - end
10   -
11   - xss_terminate :only => [ ]
12   -
13   - xss_terminate :only => [ :name, :abstract, :body ], :with => 'white_list', :on => 'validation'
14   -
15   - include WhiteListFilter
16   - filter_iframes :abstract, :body
17   - def iframe_whitelist
18   - profile && profile.environment && profile.environment.trusted_sites_for_iframe
19   - end
20   -
21   - def notifiable?
22   - true
23   - end
24   -
25   - def tiny_mce?
26   - true
27   - end
28   -
29   - def can_display_media_panel?
30   - true
31   - end
32   -
33   - def self.can_display_blocks?
34   - false
35   - end
36   -
37   -end
app/models/uploaded_file.rb
... ... @@ -9,6 +9,12 @@ class UploadedFile &lt; Article
9 9  
10 10 attr_accessible :uploaded_data, :title
11 11  
  12 + include Noosfero::Plugin::HotSpot
  13 +
  14 + def environment
  15 + profile.environment
  16 + end
  17 +
12 18 def self.type_name
13 19 _('File')
14 20 end
... ... @@ -184,8 +190,12 @@ class UploadedFile &lt; Article
184 190 true
185 191 end
186 192  
  193 + def image?
  194 + mime_type =~ /^image\//
  195 + end
  196 +
187 197 def notifiable?
188   - true
  198 + !image?
189 199 end
190 200  
191 201 end
... ...
app/presenters/activity/generic.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +class ActivityPresenter::Generic < ActivityPresenter
  2 + def self.accepts?(instance)
  3 + 1
  4 + end
  5 +end
... ...
app/presenters/activity_presenter.rb 0 → 100644
... ... @@ -0,0 +1,44 @@
  1 +class ActivityPresenter < Presenter
  2 + def self.base_class
  3 + ActionTracker::Record
  4 + end
  5 +
  6 + def self.available?(instance)
  7 + instance.kind_of?(ActionTracker::Record) || instance.kind_of?(ProfileActivity)
  8 + end
  9 +
  10 + def self.target(instance)
  11 + if instance.kind_of?(ProfileActivity)
  12 + target(instance.activity)
  13 + elsif instance.kind_of?(ActionTracker::Record)
  14 + instance.target
  15 + else
  16 + instance
  17 + end
  18 + end
  19 +
  20 + def self.owner(instance)
  21 + instance.kind_of?(ProfileActivity) ? instance.profile : instance.user
  22 + end
  23 +
  24 + def target
  25 + self.class.target(encapsulated_instance)
  26 + end
  27 +
  28 + def owner
  29 + self.class.owner(encapsulated_instance)
  30 + end
  31 +
  32 + def hidden_for?(user)
  33 + target.respond_to?(:display_to?) && !target.display_to?(user)
  34 + end
  35 +
  36 + def involved?(user)
  37 + owner == user || target == user
  38 + end
  39 +end
  40 +
  41 +# Preload ActivityPresenter subclasses to allow `Presenter.for()` to work
  42 +Dir.glob(File.join('app', 'presenters', 'activity', '*.rb')) do |file|
  43 + load file
  44 +end
... ...
app/presenters/file/generic.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +# Made to encapsulate any UploadedFile
  2 +class FilePresenter::Generic < FilePresenter
  3 + # if returns low priority, because it is generic.
  4 + def self.accepts?(f)
  5 + 1 if f.is_a? UploadedFile
  6 + end
  7 +end
... ...
app/presenters/file/image.rb 0 → 100644
... ... @@ -0,0 +1,23 @@
  1 +class FilePresenter::Image < FilePresenter
  2 + def self.accepts?(f)
  3 + return nil unless f.respond_to? :image?
  4 + f.image? ? 10 : nil
  5 + end
  6 +
  7 + def sized_icon(size)
  8 + public_filename size
  9 + end
  10 +
  11 + def icon_name
  12 + public_filename :icon
  13 + end
  14 +
  15 + def short_description
  16 + _('Image (%s)') % content_type.split('/')[1].upcase
  17 + end
  18 +
  19 + #Overwriting method from FilePresenter to allow download of images
  20 + def download?(view = nil)
  21 + view.blank? || view == 'false'
  22 + end
  23 +end
... ...
app/presenters/file_presenter.rb 0 → 100644
... ... @@ -0,0 +1,80 @@
  1 +class FilePresenter < Presenter
  2 + def self.base_class
  3 + Article
  4 + end
  5 +
  6 + def self.available?(instance)
  7 + instance.kind_of?(UploadedFile) && !instance.kind_of?(Image)
  8 + end
  9 +
  10 + def download? view = nil
  11 + view.blank?
  12 + end
  13 +
  14 + def short_description
  15 + file_type = if content_type.present?
  16 + content_type.sub(/^application\//, '').sub(/^x-/, '').sub(/^image\//, '')
  17 + else
  18 + _('Unknown')
  19 + end
  20 + _("File (%s)") % file_type
  21 + end
  22 +
  23 + # Define the css classes to style the page fragment with the file related
  24 + # content. If you want other classes to identify this area to your
  25 + # customized presenter, so do this:
  26 + # def css_class_list
  27 + # [super, 'myclass'].flatten
  28 + # end
  29 + def css_class_list
  30 + [ encapsulated_instance.css_class_list,
  31 + 'file-' + self.class.to_s.split(/:+/).map(&:underscore)[1..-1].join('-'),
  32 + 'content-type_' + self.content_type.split('/')[0],
  33 + 'content-type_' + self.content_type.gsub(/[^a-z0-9]/i,'-')
  34 + ].flatten
  35 + end
  36 +
  37 + # Enable file presenter to customize the css classes on view_page.rhtml
  38 + # You may not overwrite this method on your customized presenter.
  39 + def css_class_name
  40 + [css_class_list].flatten.compact.join(' ')
  41 + end
  42 +
  43 + # The generic icon class-name or the specific file path.
  44 + # You may replace this method on your custom FilePresenter.
  45 + # See the current used icons class-names in public/designs/icons/tango/style.css
  46 + def icon_name
  47 + if mime_type
  48 + [ mime_type.split('/')[0], mime_type.gsub(/[^a-z0-9]/i, '-') ]
  49 + else
  50 + 'upload-file'
  51 + end
  52 + end
  53 +
  54 + # Automatic render `file_presenter/<custom>.html.erb` to display your
  55 + # custom presenter html content.
  56 + # You may not overwrite this method on your customized presenter.
  57 + # A variable with the same presenter name will be created to refer
  58 + # to the file object.
  59 + # Example:
  60 + # The `FilePresenter::Image` render `file_presenter/image.html.erb`
  61 + # inside the `file_presenter/image.html.erb` you can access the
  62 + # required `FilePresenter::Image` instance in the `image` variable.
  63 + def to_html(options = {})
  64 + file = self
  65 + proc do
  66 + render :partial => file.class.to_s.underscore,
  67 + :locals => { :options => options },
  68 + :object => file
  69 + end
  70 + end
  71 +end
  72 +
  73 +Dir.glob(File.join('app', 'presenters', 'file', '*.rb')) do |file|
  74 + load file
  75 +end
  76 +
  77 +# Preload FilePresenters from plugins to allow `FilePresenter.for()` to work
  78 +Dir.glob(File.join('plugins', '*', 'lib', 'presenters', '*.rb')) do |file|
  79 + load file
  80 +end
... ...
app/presenters/generic.rb
... ... @@ -1,7 +0,0 @@
1   -# Made to encapsulate any UploadedFile
2   -class FilePresenter::Generic < FilePresenter
3   - # if returns low priority, because it is generic.
4   - def self.accepts?(f)
5   - 1 if f.is_a? UploadedFile
6   - end
7   -end
app/presenters/image.rb
... ... @@ -1,23 +0,0 @@
1   -class FilePresenter::Image < FilePresenter
2   - def self.accepts?(f)
3   - return nil unless f.respond_to? :image?
4   - f.image? ? 10 : nil
5   - end
6   -
7   - def sized_icon(size)
8   - public_filename size
9   - end
10   -
11   - def icon_name
12   - public_filename :icon
13   - end
14   -
15   - def short_description
16   - _('Image (%s)') % content_type.split('/')[1].upcase
17   - end
18   -
19   - #Overwriting method from FilePresenter to allow download of images
20   - def download?(view = nil)
21   - view.blank? || view == 'false'
22   - end
23   -end
app/views/account/_signup_form.html.erb
... ... @@ -111,7 +111,7 @@
111 111  
112 112 <% unless @terms_of_use.blank? %>
113 113 <div id='terms-of-use-box' class='formfieldline'>
114   - <%= labelled_check_box(_('I accept the %s') % link_to(_('terms of use'), {:controller => 'home', :action => 'terms'}, :target => '_blank'), 'user[terms_accepted]') %>
  114 + <%= labelled_check_box(_('I accept the %s').html_safe % link_to(_('terms of use'), {:controller => 'home', :action => 'terms'}, :target => '_blank'), 'user[terms_accepted]') %>
115 115 </div>
116 116 <% end %>
117 117  
... ...
app/views/admin_panel/_signup_intro.html.erb
... ... @@ -2,4 +2,4 @@
2 2 <%= _('This text will be shown to the user on the top of the sign up form.') %>
3 3 </div>
4 4  
5   -<%= labelled_form_field(_('Body'), text_area(:environment, :signup_intro, :cols => 40, :style => 'width: 100%', :class => 'mceEditor')) %>
  5 +<%= labelled_form_field(_('Body'), text_area(:environment, :signup_intro, :cols => 40, :style => 'width: 100%', :class => current_editor)) %>
... ...
app/views/admin_panel/_signup_welcome_screen.html.erb
1 1 <div class='description'>
2 2 <%= _('If you enable this feature on the "Features" section of the Administration Panel, this text will be shown as a welcome message to users after signup.') %>
3 3 </div>
4   -<%= labelled_form_field(_('Body'), text_area(:environment, :signup_welcome_screen_body, :cols => 40, :style => 'width: 100%', :class => 'mceEditor')) %>
  4 +<%= labelled_form_field(_('Body'), text_area(:environment, :signup_welcome_screen_body, :cols => 40, :style => 'width: 100%', :class => current_editor)) %>
5 5  
6 6 <div class='description'>
7 7 <%= _('If this content is left blank, the following page will be displayed to the user:') %>
... ...
app/views/admin_panel/_signup_welcome_text.html.erb
... ... @@ -4,4 +4,4 @@
4 4 </div>
5 5  
6 6 <%= labelled_form_field(_('Subject'), text_field(:environment, :signup_welcome_text_subject, :style => 'width:100%')) %>
7   -<%= labelled_form_field(_('Body'), text_area(:environment, :signup_welcome_text_body, :cols => 40, :style => 'width: 100%', :class => 'mceEditor')) %>
  7 +<%= labelled_form_field(_('Body'), text_area(:environment, :signup_welcome_text_body, :cols => 40, :style => 'width: 100%', :class => current_editor)) %>
... ...
app/views/admin_panel/_site_info.html.erb
... ... @@ -31,4 +31,4 @@
31 31 <%= balanced_table(fields)%>
32 32  
33 33 <br />
34   -<%= labelled_form_field _('Homepage content'), text_area(:environment, :description, :cols => 40, :style => 'width: 90%', :class => 'mceEditor') %>
  34 +<%= labelled_form_field _('Homepage content'), text_area(:environment, :description, :cols => 40, :style => 'width: 90%', :class => current_editor) %>
... ...
app/views/admin_panel/_terms_of_use.html.erb
1   -<%= f.text_area :terms_of_use, :cols => 40, :style => 'width: 90%', :class => 'mceEditor' %>
  1 +<%= f.text_area :terms_of_use, :cols => 40, :style => 'width: 90%', :class => current_editor %>
... ...
app/views/admin_panel/message_for_disabled_enterprise.html.erb
1 1 <h2><%= _('Site info') %></h2>
2 2  
3   -<%= render :file => 'shared/tiny_mce' %>
4   -
5 3 <%= labelled_form_for :environment, :url => {:action => 'site_info'} do |f| %>
6 4  
7   - <%= f.text_area :message_for_disabled_enterprise, :cols => 40, :style => 'width: 90%' %>
  5 + <%= f.text_area :message_for_disabled_enterprise, :cols => 40, :style => 'width: 90%', :class => current_editor %>
8 6  
9 7 <%= button_bar do %>
10 8 <%= submit_button(:save, _('Save')) %>
... ...
app/views/admin_panel/site_info.html.erb
... ... @@ -2,8 +2,6 @@
2 2  
3 3 <%= error_messages_for :environment %>
4 4  
5   -<%= render :file => 'shared/tiny_mce' %>
6   -
7 5 <%= labelled_form_for :environment do |f| %>
8 6 <% tabs = [] %>
9 7 <% tabs << {:title => _('Site info'), :id => 'site-info',
... ...
app/views/api/index.html.erb
1 1 <h1>EndPoints</h1>
2 2  
3 3 <div style="float: right">
4   - <%= s_('api-playground|Try the %s') % link_to('API Playground', {:controller => 'api', :action => 'playground'}) %>
  4 + <%= s_('api-playground|Try the %s').html_safe % link_to('API Playground', {:controller => 'api', :action => 'playground'}) %>
5 5 </div>
6 6  
7 7 <%= endpoints.map do |endpoint|
8 8 app = endpoint.options[:app].to_s
9 9 unless app.blank?
10   - content_tag(:h2, app.split('::').last.to_s, title: app) +
  10 + content_tag(:h2, app.split('::').last.to_s, title: app).html_safe +
11 11 (content_tag :ul do
12 12 endpoint.routes.map do |route|
13 13 content_tag :li do
14 14 content_tag(:strong, route.route_method) + ' ' +
15   - route.route_path.gsub(':version', content_tag(:b, route.route_version))
  15 + route.route_path.gsub(':version', content_tag(:b, route.route_version)).html_safe
16 16 end
17   - end.join "\n"
  17 + end.safe_join "\n"
18 18 end)
19 19 end
20   -end.join "\n" %>
  20 +end.safe_join "\n" %>
... ...
app/views/categories/_form.html.erb
1   -<%= stylesheet_link_tag 'spectrum.css' %>
2   -<%= javascript_include_tag "spectrum.js" %>
  1 +<%= stylesheet_link_tag 'vendor/spectrum.css' %>
  2 +<%= javascript_include_tag "vendor/spectrum.js" %>
3 3 <%= javascript_include_tag "colorpicker-noosfero.js" %>
4 4  
5 5 <%= error_messages_for 'category' %>
... ...
app/views/cms/_article.html.erb
1   -_tiny_mce_article.html.erb
2 1 \ No newline at end of file
  2 +_text_article.html.erb
3 3 \ No newline at end of file
... ...
app/views/cms/_blog.html.erb
... ... @@ -2,8 +2,6 @@
2 2  
3 3 <h1><%= _('My Blog') %></h1>
4 4  
5   -<%= render :file => 'shared/tiny_mce' %>
6   -
7 5 <%= required f.text_field(:name, :size => '64', :maxlength => 150, :onchange => "updateUrlField(this, 'article_slug')") %>
8 6  
9 7 <%= render :partial => 'general_fields' %>
... ... @@ -53,7 +51,7 @@
53 51 %>
54 52 </div>
55 53  
56   -<%= labelled_form_field(_('Description:'), text_area(:article, :body, :rows => 10, :class => 'mceEditor')) %>
  54 +<%= labelled_form_field(_('Description:'), text_area(:article, :body, :rows => 10, :class => current_editor)) %>
57 55  
58 56 <div id="blog-image-builder">
59 57 <%= f.fields_for :image_builder, @article.image do |i| %>
... ...
app/views/cms/_enterprise_homepage.html.erb
1   -<%= render :file => 'shared/tiny_mce' %>
2   -
3   -<%= labelled_form_field(_('Text'), text_area(:article, 'body', :cols => 40, :style => 'width:99%', :class => 'mceEditor')) %>
  1 +<%= labelled_form_field(_('Text'), text_area(:article, 'body', :cols => 40, :style => 'width:99%', :class => current_editor)) %>
4 2  
... ...
app/views/cms/_event.html.erb
1 1 <%= required_fields_message %>
2 2  
3   -<%# TODO add Textile help here %>
4   -<%= render :file => 'shared/tiny_mce' %>
5   -
6 3 <%= required f.text_field('name', :size => '64', :maxlength => 150) %>
7 4  
8 5 <%= render :partial => 'general_fields' %>
... ... @@ -16,4 +13,4 @@
16 13  
17 14 <%= labelled_form_field(_('Address:'), text_field(:article, :address)) %>
18 15  
19   -<%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true, :body_label => 'Information about the event:'} %>
  16 +<%= render :partial => 'shared/lead_and_body', :locals => {:body_label => 'Information about the event:'} %>
... ...
app/views/cms/_forum.html.erb
... ... @@ -4,18 +4,16 @@
4 4  
5 5 <%= required_fields_message %>
6 6  
7   -<%= render :file => 'shared/tiny_mce' %>
8   -
9 7 <%= required f.text_field(:name, :size => '64', :maxlength => 150, :onchange => "updateUrlField(this, 'article_slug')") %>
10 8  
11 9 <%= render :partial => 'general_fields' %>
12 10  
13   -<%= labelled_form_field(_('Description:'), text_area(:article, :body, :class => 'mceEditor', :cols => 64, :rows => 10)) %>
  11 +<%= labelled_form_field(_('Description:'), text_area(:article, :body, :class => current_editor, :cols => 64, :rows => 10)) %>
14 12  
15 13 <%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, Forum.posts_per_page_options)) %>
16 14  
17 15 <%= labelled_form_field(_('Has terms of use:'), check_box(:article, :has_terms_of_use))%>
18 16  
19 17 <div id="text_area_terms_of_use">
20   - <%= labelled_form_field(_('Terms of use:'), text_area(:article, :terms_of_use, :class => 'mceEditor',:cols => 64, :rows => 10)) %>
  18 + <%= labelled_form_field(_('Terms of use:'), text_area(:article, :terms_of_use, :class => current_editor,:cols => 64, :rows => 10)) %>
21 19 </div>
... ...
app/views/cms/_raw_html_article.html.erb
... ... @@ -1,8 +0,0 @@
1   -<%= required_fields_message %>
2   -
3   -<%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64', :maxlength => 150)) %>
4   -
5   -<%= render :partial => 'text_fields' %>
6   -<%= render :partial => 'general_fields' %>
7   -<%= render :partial => 'translatable' %>
8   -<%= render :partial => 'shared/lead_and_body' %>
app/views/cms/_text_article.html.erb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +<%= required_fields_message %>
  2 +
  3 +<%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '72', :maxlength => 150)) %>
  4 +
  5 +<%= render :partial => 'text_fields' %>
  6 +<%= render :partial => 'general_fields' %>
  7 +<%= render :partial => 'translatable' %>
  8 +
  9 +<%= render :partial => 'shared/lead_and_body' %>
  10 +
... ...
app/views/cms/_text_editor_sidebar.html.erb
... ... @@ -7,7 +7,7 @@
7 7  
8 8 <div class='header'><strong><%= _('Insert media') %></strong><%= button('vertical-toggle', _('Show/Hide'), '#') %></div>
9 9  
10   - <%= render(:partial => 'textile_quick_reference') if @article.is_a?(TextileArticle) %>
  10 + <%= render(:partial => 'textile_quick_reference') if @article.editor?(Article::Editor::TEXTILE) %>
11 11 <div class='text-editor-sidebar-box' id='media-upload-box'>
12 12 <div id='media-upload-form'>
13 13 <%= form_tag({ :action => 'media_upload' }, :multipart => true) do %>
... ... @@ -48,4 +48,4 @@
48 48 </script>
49 49  
50 50 <%= render :partial => 'media_new_folder', :locals => {:default_folder => default_folder} %>
51   -<%= javascript_include_tag 'jquery.fileupload.js', 'tmpl.js', 'media-panel.js' %>
  51 +<%= javascript_include_tag 'vendor/jquery.fileupload.js', 'vendor/tmpl.js', 'media-panel.js' %>
... ...
app/views/cms/_textile_article.html.erb
... ... @@ -1,10 +0,0 @@
1   -<%= required_fields_message %>
2   -
3   -<%# TODO add Textile help here %>
4   -
5   -<%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '72', :maxlength => 150)) %>
6   -
7   -<%= render :partial => 'text_fields' %>
8   -<%= render :partial => 'general_fields' %>
9   -<%= render :partial => 'translatable' %>
10   -<%= render :partial => 'shared/lead_and_body' %>
app/views/cms/_tiny_mce_article.html.erb
... ... @@ -1,12 +0,0 @@
1   -<%= required_fields_message %>
2   -
3   -<%= render :file => 'shared/tiny_mce' %>
4   -
5   -<div>
6   - <%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64', :maxlength => 150)) %>
7   -
8   - <%= render :partial => 'text_fields' %>
9   - <%= render :partial => 'general_fields' %>
10   - <%= render :partial => 'translatable' %>
11   - <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true} %>
12   -</div>
app/views/cms/edit.html.erb
... ... @@ -34,7 +34,7 @@
34 34  
35 35 <br />
36 36  
37   - <%= f.text_field('tag_list', :size => 64) %>
  37 + <%= f.text_field 'tag_list', size: 64, value: @article.tag_list.join(',') %>
38 38 <%= content_tag( 'small', _('Separate tags with commas') ) %>
39 39  
40 40 <script>
... ...
app/views/cms/suggest_an_article.html.erb
... ... @@ -2,8 +2,6 @@
2 2  
3 3 <%= required_fields_message %>
4 4  
5   -<%= render :file => 'shared/tiny_mce' %>
6   -
7 5 <%= labelled_form_for 'task' do |f| %>
8 6  
9 7 <%= required labelled_form_field(_('Title'), text_field('task[article]', 'name', :size => 50)) %>
... ... @@ -17,7 +15,7 @@
17 15 <%= required labelled_form_field(_('Email'), text_field(:task, 'email')) %>
18 16 <% end %>
19 17  
20   - <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true, :object => 'task[article]'} %>
  18 + <%= render :partial => 'shared/lead_and_body', :locals => {:object => 'task[article]'} %>
21 19  
22 20 <%= hidden_field_tag('back_to', @back_to) %>
23 21  
... ...
app/views/contact/new.html.erb
... ... @@ -25,8 +25,7 @@
25 25  
26 26 <%= required f.text_field(:subject) %>
27 27  
28   - <%= render :file => 'shared/tiny_mce' %>
29   - <%= required f.text_area(:message, :class => 'mceEditor') %>
  28 + <%= required f.text_area(:message, :class => current_editor) %>
30 29  
31 30 <%= labelled_form_field check_box(:contact, :receive_a_copy) + _('I want to receive a copy of the message in my e-mail.'), '' %>
32 31  
... ...
app/views/email_templates/_form.html.erb
... ... @@ -19,8 +19,7 @@
19 19 <%= @template_params_allowed %>
20 20 </div>
21 21 </div>
22   - <%= render :file => 'shared/tiny_mce' %>
23   - <%= labelled_form_field(_('Body:'), f.text_area(:body, :class => 'mceEditor')) %>
  22 + <%= labelled_form_field(_('Body:'), f.text_area(:body, :class => current_editor)) %>
24 23 </div>
25 24  
26 25 <div class="actions">
... ...
app/views/file_presenter/_image.html.erb
1 1 <% if image.gallery? && options[:gallery_view] %>
2 2 <%
3 3 images = image.parent.images
4   - current_index = images.index(image.encapsulated_file)
  4 + current_index = images.index(image.encapsulated_instance)
5 5 total_of_images = images.count
6 6 link_to_previous = if current_index >= 1
7 7 link_to(_('&laquo; Previous').html_safe, images[current_index - 1].view_url, :class => 'previous')
... ... @@ -30,7 +30,10 @@
30 30  
31 31 <img src="<%= [Noosfero.root, image.public_filename(:display)].join %>" class="<%=image.css_class_name%>">
32 32  
  33 +<% if image.parent.is_a?(Gallery) && image.parent.allow_download %>
  34 + <%= link_to _('Download image'), [Noosfero.root, image.public_filename(:display)].join, download: image.filename, id: 'download-image-id', class: "button with-text icon-save" %>
  35 +<% end %>
  36 +
33 37 <div class="uploaded-file-description <%= 'empty' if image.abstract.blank? %>">
34 38 <%= image.abstract %>
35 39 </div>
36   -
... ...
app/views/home/terms.html.erb
1 1 <h1><%= _('Terms of use - %s') % environment.name %></h1>
2   -<%= environment.terms_of_use %>
  2 +<%= environment.terms_of_use.html_safe %>
... ...
app/views/layouts/application-ng.html.erb
... ... @@ -35,6 +35,9 @@
35 35 noosfero.profile = <%= (@profile.identifier if @profile).to_json.html_safe %>
36 36 </script>
37 37  
  38 + <% if current_editor_is?(Article::Editor::TINY_MCE) %>
  39 + <%= render :file => 'shared/tiny_mce' %>
  40 + <% end %>
38 41 </head>
39 42 <body class="<%= h body_classes %>">
40 43 <a href="#content" id="link-go-content"><span><%= _("Go to the content") %></span></a>
... ... @@ -72,7 +75,7 @@
72 75 </div><!-- end id="content" -->
73 76 </div><!-- end id="wrap-2" -->
74 77 </div><!-- end id="wrap-1" -->
75   - <%= render_environment_features(:logged_in) if logged_in? %>
  78 + <%= render_environment_features(:logged_in).html_safe if logged_in? %>
76 79 <div id="footer">
77 80 <div id="theme-footer">
78 81 <%= theme_footer %>
... ... @@ -86,9 +89,9 @@
86 89 <%=
87 90 str = (@plugins.dispatch(:body_ending).map do |content|
88 91 if content.respond_to?(:call) then
89   - instance_exec(&content)
  92 + instance_exec(&content).html_safe
90 93 else
91   - content
  94 + content.html_safe
92 95 end
93 96 end)
94 97 safe_join(str, "\n")
... ...
app/views/layouts/slideshow.html.erb
... ... @@ -11,7 +11,7 @@
11 11 %></title>
12 12  
13 13 <%= stylesheet_import "slideshow" %>
14   - <%= javascript_include_tag 'jquery-2.1.1.min', 'sliderjs', 'pikachoose' %>
  14 + <%= javascript_include_tag 'jquery-2.1.1.min', 'vendor/sliderjs', 'vendor/pikachoose' %>
15 15 <script type="text/javascript">
16 16 $(document).ready(function (){
17 17 $("#slideshow").PikaChoose({
... ...
app/views/profile/_default_activity.html.erb
... ... @@ -2,7 +2,7 @@
2 2 <%= link_to(profile_image(activity.user, :minor), activity.user.url) %>
3 3 </div>
4 4 <div class='profile-activity-description'>
5   - <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p>
  5 + <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe(activity).html_safe %></p>
6 6 <p class='profile-activity-time'><%= time_ago_in_words(activity.created_at) %></p>
7 7 <div class='profile-wall-actions'>
8 8 <%= link_to s_('profile|Comment'), '#', { :class => 'focus-on-comment'} %>
... ...
app/views/profile/_favorite_enterprise.html.erb
... ... @@ -3,7 +3,7 @@
3 3 </div>
4 4 <div class='profile-activity-description'>
5 5 <p class='profile-activity-text'>
6   - <%= link_to activity.user.short_name(nil), activity.user.url %> <%= describe activity %>
  6 + <%= link_to activity.user.short_name(nil), activity.user.url %> <%= describe(activity).html_safe %>
7 7 </p>
8 8 <p class='profile-activity-time'><%= time_ago_in_words activity.created_at %></p>
9 9  
... ...
app/views/profile/_leave_scrap.html.erb
... ... @@ -2,7 +2,7 @@
2 2 <%= link_to(profile_image(activity.user, :minor), activity.user.url) %>
3 3 </div>
4 4 <div class='profile-activity-description'>
5   - <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p>
  5 + <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe(activity).html_safe %></p>
6 6 <p class='profile-activity-time'><%= time_ago_in_words(activity.created_at) %></p>
7 7 <div class='profile-wall-actions'>
8 8 <%= link_to_function(_('Remove'), 'remove_item_wall(this, \'%s\', \'%s\', \'%s\'); return false ;' % [".profile-activity-item", url_for(:profile => params[:profile], :action => :remove_activity, :activity_id => activity.id, :view => params[:view]), _('Are you sure you want to remove this activity and all its replies?')]) if logged_in? && current_person == @profile %>
... ...
app/views/profile/_profile_activities_list.html.erb
... ... @@ -12,6 +12,6 @@
12 12  
13 13 <% if activities.current_page < activities.total_pages %>
14 14 <div id='profile_activities_page_<%= activities.current_page %>'>
15   - <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_activities', :page => (activities.current_page + 1)}, :update => "profile_activities_page_#{activities.current_page}" %>
  15 + <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_activities', :page => (activities.current_page + 1), :offsets => @offsets, :kind => 'wall'}, :update => "profile_activities_page_#{activities.current_page}" %>
16 16 </div>
17 17 <% end %>
... ...
app/views/profile/_profile_network.html.erb
1 1 <h3><%= _("%s's network activity") % @profile.name %></h3>
2 2 <ul id='network-activities' class='profile-activities'>
3   - <%= render :partial => 'profile_network_activities', :locals => {:network_activities => @network_activities} %>
  3 + <%= render :partial => 'profile_network_activities', :locals => {:activities => @network_activities} %>
4 4 </ul>
... ...
app/views/profile/_profile_network_activities.html.erb
1   -<% network_activities.each do |activity| %>
  1 +<% activities.each do |activity| %>
2 2 <%= render :partial => 'profile_activity', :locals => { :activity => activity, :tab_action => 'network' } if activity.visible? %>
3 3 <% end %>
4   -<% if network_activities.current_page < network_activities.total_pages %>
5   - <div id='profile_network_activities_page_<%= network_activities.current_page %>'>
6   - <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_network_activities', :page => (network_activities.current_page + 1)}, :update => "profile_network_activities_page_#{network_activities.current_page}" %>
  4 +<% if activities.current_page < activities.total_pages %>
  5 + <div id='profile_network_activities_page_<%= activities.current_page %>'>
  6 + <%= button_to_remote :add, _('View more'), :url => {:action => 'view_more_activities', :page => (activities.current_page + 1), :offsets => @offsets, :kind => 'network'}, :update => "profile_network_activities_page_#{activities.current_page}" %>
7 7 </div>
8 8 <% end %>
... ...
app/views/profile/_profile_wall.html.erb
1 1 <h3><%= _("%s's wall") % @profile.name %></h3>
2 2 <div id='leave_scrap'>
3 3 <%= flash[:error] %>
4   - <%= form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_activities', :success => "jQuery('#leave_scrap_content').val('')", :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, :class => 'autogrow' %>
  4 + <%= form_remote_tag :url => {:controller => 'profile', :action => 'leave_scrap', :tab_action => 'wall' }, :update => 'profile_activities', :success => "jQuery('#leave_scrap_content').val(''); jQuery('#filter-followed').tokenInput('clear')", :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', :rows => 2, :class => 'autogrow' %>
  6 + <% if profile == user %>
  7 + <%= token_input_field_tag(:filter_followed, 'filter-followed', {:action => 'search_followed'}, {:theme => 'facebook', :placeholder => _('Filter followed, friends or group of friends to send them a private scrap...')}) %>
  8 + <% end %>
6 9 <%= submit_button :new, _('Share') %>
7 10 <% end %>
8 11 </div>
... ...
app/views/profile/send_mail.html.erb
... ... @@ -22,8 +22,7 @@
22 22  
23 23 <%= labelled_form_field(_('Subject:'), f.text_field(:subject)) %>
24 24  
25   - <%= render :file => 'shared/tiny_mce' %>
26   - <%= labelled_form_field(_('Body:'), f.text_area(:body, :class => 'mceEditor')) %>
  25 + <%= labelled_form_field(_('Body:'), f.text_area(:body, :class => 'body ' + current_editor)) %>
27 26  
28 27 <%= submit_button(:send, _('Send')) %>
29 28 <%= button :cancel, _('Cancel e-mail'), :back %>
... ...
app/views/profile_editor/_pending_tasks.html.erb
... ... @@ -2,9 +2,9 @@
2 2  
3 3 <% unless @pending_tasks.empty? %>
4 4 <div class='pending-tasks'>
5   - <h2><%= _('You have pending requests') %></h2>
  5 + <h2><%= _('You have %s pending requests' % @pending_tasks.count) %></h2>
6 6 <ul>
7   - <%= safe_join(@pending_tasks.map {|task| content_tag('li', task_information(task).html_safe)}) %>
  7 + <%= safe_join(@pending_tasks.limit(5).map {|task| content_tag('li', task_information(task, params).html_safe)}) %>
8 8 </ul>
9 9 <%= button(:todo, _('Process requests'), :controller => 'tasks', :action => 'index') %>
10 10 </div>
... ...
app/views/profile_editor/_person.html.erb
... ... @@ -16,6 +16,8 @@
16 16 </div>
17 17 </div>
18 18  
  19 + <%= select_editor(_('Editor'), 'profile_data', 'editor', {}) %>
  20 +
19 21 <%= safe_join(@plugins.dispatch(:profile_info_extra_contents).collect { |content| instance_exec(&content) }, "") %>
20 22  
21 23 <div class="formfieldline">
... ...
app/views/profile_editor/header_footer.html.erb
1   -<%= render :file => 'shared/tiny_mce' %>
2   -
3 1 <h1><%= _('Editing header and footer') %></h1>
4 2  
5 3 <%= form_tag do %>
... ... @@ -21,9 +19,9 @@
21 19 </div>
22 20 <% end %>
23 21 <h2><%= _('Content for header ') %></h2>
24   - <%= text_area_tag(:custom_header, @header, :style => 'width: 100%; height: 150px;', :class => 'mceEditor') %>
  22 + <%= text_area_tag(:custom_header, @header, :style => 'width: 100%; height: 150px;', :class => current_editor) %>
25 23 <h2><%= _('Content for footer') %></h2>
26   - <%= text_area_tag(:custom_footer, @footer, :style => 'width: 100%; height: 150px;', :class => 'mceEditor') %>
  24 + <%= text_area_tag(:custom_footer, @footer, :style => 'width: 100%; height: 150px;', :class => current_editor) %>
27 25 <%= button_bar do %>
28 26 <%= submit_button(:save, _('Save')) %>
29 27 <%= button(:cancel, _('Cancel'), :action => 'index') %>
... ...
app/views/profile_editor/welcome_page.html.erb
... ... @@ -8,7 +8,7 @@
8 8 <%= _('Your welcome page will only be displayed if this options is selected.') %>
9 9 </div>
10 10  
11   - <%= f.text_area(:body, :cols => 40, :style => 'width: 100%', :class => 'mceEditor') %>
  11 + <%= f.text_area(:body, :cols => 40, :style => 'width: 100%', :class => current_editor) %>
12 12 <div class='explanation'>
13 13 <%= _('This page will be displayed to the user after his signup with this template.') %>
14 14 </div>
... ... @@ -17,5 +17,3 @@
17 17 <%= submit_button('save', _('Save'), :cancel => @back_to) %>
18 18 <% end %>
19 19 <% end %>
20   -
21   -<%= render :file => 'shared/tiny_mce' %>
... ...
app/views/shared/_lead_and_body.html.erb
... ... @@ -3,7 +3,7 @@
3 3 <% abstract_method ||= :abstract %>
4 4 <% body_label ||= 'Text' %>
5 5 <% body_method ||= :body %>
6   -<% editor_type = defined?(tiny_mce) && tiny_mce ? 'mceEditor' : '' %>
  6 +<% editor_type = current_editor %>
7 7 <% lead_id ||= 0%>
8 8 <% f ||= false%>
9 9  
... ...
app/views/shared/_profile_connections.html.erb
... ... @@ -4,7 +4,7 @@
4 4 <ul class="profile-list">
5 5 <% profiles.each do |profile| %>
6 6 <li>
7   - <%= link_to_profile profile_image(profile) + '<br/>' + profile.short_name,
  7 + <%= link_to_profile profile_image(profile) + '<br/>'.html_safe + profile.short_name,
8 8 profile.identifier, :class => 'profile-link' %>
9 9 </li>
10 10 <% end %>
... ...
app/views/shared/logged_in/xmpp_chat.html.erb
1   - <%= javascript_include_tag 'strophejs-1.1.3/strophe.min', 'jquery.emoticon', 'designs/icons/pidgin/emoticons.js', 'ba-linkify', 'jquery.ba-hashchange', 'jquery.sound', 'chat', 'vendor/perfect-scrollbar.min.js', 'vendor/perfect-scrollbar.with-mousewheel.min.js', 'jquery.timeago.js' %>
  1 + <%= javascript_include_tag 'vendor/strophejs-1.1.3/strophe.min', 'vendor/jquery.emoticon', 'designs/icons/pidgin/emoticons.js', 'vendor/ba-linkify', 'vendor/jquery.ba-hashchange', 'vendor/jquery.sound', 'chat', 'vendor/perfect-scrollbar.min.js', 'vendor/perfect-scrollbar.with-mousewheel.min.js', 'vendor/jquery.timeago.js' %>
2 2 <%= stylesheet_link_tag 'vendor/perfect-scrollbar.min.css' %>
3 3  
4 4 <% extend ChatHelper %>
... ...
app/views/shared/not_found.html.erb
1 1 <div id='not-found'>
2   - <h1><%= _('There is no such page: %s') % (content_tag('tt', @path)) %></h1>
  2 + <h1><%= _('There is no such page: %s').html_safe % (content_tag('tt', @path)) %></h1>
3 3 <p>
4 4 <%= _('.You may have clicked an expired link or mistyped the address.') %>
5 5 <%= _('.This page does not exist. Would you like to: Create a new content or locate existing content.') %>
... ...
app/views/shared/tiny_mce.html.erb
... ... @@ -47,7 +47,9 @@ function tinymce_macros_setup(editor) {
47 47 tinymce.PluginManager.add('macrosPlugin', tinymce.plugins.MacrosPlugin);
48 48  
49 49 jQuery(document).ready(function () {
50   - <%= tinymce_init_js :mode => mode %>
  50 + <%= tinymce_init_js %>
  51 + <%= tinymce_init_js :mode => 'simple' %>
  52 + <%= tinymce_init_js :mode => 'restricted' %>
51 53 });
52 54 </script>
53 55  
... ...