Commit 2cdcd813d955c0e5f5d50d0e5acd907a3b087979
Exists in
theme-brasil-digital-from-staging
and in
9 other branches
Merge branch 'production' of gitlab.com:participa/noosfero into production
* 'production' of gitlab.com:participa/noosfero: (173 commits) update submodule update submodule add rubygems source fixed tasks_controller_test added random as a possible value for make_order_with_parameters added sql injection protection to method make_order_with_parameters HOTFIX: removing wrong comment HOTFIX: fix tests fix new content url in not found content submodule: updating submodule Adapting views and helpers to datime format Remove admins.map that generate a SQL query for each user causing performance problems. Translated using Weblate (Armenian) scheduler: scope into Noosfero change event start date and ende date to datetime search: realign search box and search button Avoid unecessary user save after update person scrap_test: destroy actiontracker to stabilize #last Remove letfover selectordie files Update proposals discussion plugin ...
Showing
185 changed files
with
64518 additions
and
62530 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 185 files displayed.
.gitlab-ci.yml
| 1 | before_script: | 1 | before_script: |
| 2 | - mkdir -p tmp/pids log | 2 | - mkdir -p tmp/pids log |
| 3 | - bundle check || bundle install | 3 | - bundle check || bundle install |
| 4 | +# workaround for plugins with Gemfile | ||
| 5 | + - perl -pi -e 's/--local //' script/noosfero-plugins | ||
| 4 | - script/noosfero-plugins disableall | 6 | - script/noosfero-plugins disableall |
| 5 | - bundle exec rake makemo &>/dev/null | 7 | - bundle exec rake makemo &>/dev/null |
| 6 | # database | 8 | # database |
.gitmodules
| @@ -28,3 +28,9 @@ | @@ -28,3 +28,9 @@ | ||
| 28 | [submodule "public/proposal-app"] | 28 | [submodule "public/proposal-app"] |
| 29 | path = public/proposal-app | 29 | path = public/proposal-app |
| 30 | url = https://gitlab.com/participa/proposal-app.git | 30 | url = https://gitlab.com/participa/proposal-app.git |
| 31 | +[submodule "plugins/gravatar-provider"] | ||
| 32 | + path = plugins/gravatar-provider | ||
| 33 | + url = https://gitlab.com/noosfero-plugins/gravatar-provider.git | ||
| 34 | +[submodule "plugins/juventude"] | ||
| 35 | + path = plugins/juventude | ||
| 36 | + url = https://gitlab.com/noosfero-plugins/juventude.git |
.travis.yml
| @@ -2,7 +2,6 @@ language: ruby | @@ -2,7 +2,6 @@ language: ruby | ||
| 2 | rvm: | 2 | rvm: |
| 3 | # for 2.2 support we need to upgrade the pg gem | 3 | # for 2.2 support we need to upgrade the pg gem |
| 4 | - 2.1.6 | 4 | - 2.1.6 |
| 5 | -cache: bundler | ||
| 6 | 5 | ||
| 7 | sudo: false | 6 | sudo: false |
| 8 | addons: | 7 | addons: |
| @@ -19,9 +18,20 @@ addons: | @@ -19,9 +18,20 @@ addons: | ||
| 19 | - libsqlite3-dev | 18 | - libsqlite3-dev |
| 20 | - libxslt1-dev | 19 | - libxslt1-dev |
| 21 | 20 | ||
| 21 | +before_install: | ||
| 22 | + - gem env | ||
| 23 | + | ||
| 24 | +# workaround for https://github.com/travis-ci/travis-ci/issues/4536 | ||
| 25 | +before_install: | ||
| 26 | + - export GEM_HOME=$PWD/vendor/bundle/ruby/2.1.0 | ||
| 27 | + - gem install bundler | ||
| 28 | +cache: bundler | ||
| 29 | + | ||
| 22 | before_script: | 30 | before_script: |
| 23 | - mkdir -p tmp/pids log | 31 | - mkdir -p tmp/pids log |
| 24 | - - bundle check || bundle install | 32 | +# workaround for plugins with Gemfile |
| 33 | + - perl -pi -e 's/cat .+ > \$gemfile/echo "source \\"https:\/\/rubygems.org\\"" > \$gemfile && cat \$source\/Gemfile >> \$gemfile/' script/noosfero-plugins | ||
| 34 | + - perl -pi -e 's/--local --quiet/install --jobs=3 --retry=3/' script/noosfero-plugins | ||
| 25 | - script/noosfero-plugins disableall | 35 | - script/noosfero-plugins disableall |
| 26 | - bundle exec rake makemo &>/dev/null | 36 | - bundle exec rake makemo &>/dev/null |
| 27 | # database | 37 | # database |
Gemfile
| @@ -28,6 +28,11 @@ gem 'grape-entity' | @@ -28,6 +28,11 @@ gem 'grape-entity' | ||
| 28 | gem 'grape_logging', :git => 'https://github.com/aceunreal/grape_logging.git', :ref => '100091b' | 28 | gem 'grape_logging', :git => 'https://github.com/aceunreal/grape_logging.git', :ref => '100091b' |
| 29 | gem 'rack-cors' | 29 | gem 'rack-cors' |
| 30 | gem 'rack-contrib' | 30 | gem 'rack-contrib' |
| 31 | +gem 'liquid', '~> 3.0.3' | ||
| 32 | +#gem 'grape-swagger-rails' | ||
| 33 | + | ||
| 34 | +# FIXME list here all actual dependencies (i.e. the ones in debian/control), | ||
| 35 | +# with their GEM names (not the Debian package names) | ||
| 31 | 36 | ||
| 32 | gem 'api-pagination', '~> 4.1.1' | 37 | gem 'api-pagination', '~> 4.1.1' |
| 33 | 38 |
app/controllers/admin/environment_email_templates_controller.rb
0 → 100644
| @@ -0,0 +1,15 @@ | @@ -0,0 +1,15 @@ | ||
| 1 | +class EnvironmentEmailTemplatesController < EmailTemplatesController | ||
| 2 | + | ||
| 3 | + protect 'manage_email_templates', :environment | ||
| 4 | + | ||
| 5 | + protected | ||
| 6 | + | ||
| 7 | + def owner | ||
| 8 | + environment | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + before_filter :only => :index do | ||
| 12 | + @back_to = url_for(:controller => :admin_panel) | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | +end |
| @@ -0,0 +1,62 @@ | @@ -0,0 +1,62 @@ | ||
| 1 | +class EmailTemplatesController < ApplicationController | ||
| 2 | + | ||
| 3 | + def index | ||
| 4 | + @email_templates = owner.email_templates | ||
| 5 | + end | ||
| 6 | + | ||
| 7 | + def show | ||
| 8 | + @email_template = owner.email_templates.find(params[:id]) | ||
| 9 | + | ||
| 10 | + respond_to do |format| | ||
| 11 | + format.html # show.html.erb | ||
| 12 | + format.json { render json: @email_template } | ||
| 13 | + end | ||
| 14 | + end | ||
| 15 | + | ||
| 16 | + def show_parsed | ||
| 17 | + @email_template = owner.email_templates.find(params[:id]) | ||
| 18 | + template_params = {:profile => owner, :environment => environment} | ||
| 19 | + render json: {:parsed_body => @email_template.parsed_body(template_params), :parsed_subject => @email_template.parsed_subject(template_params)} | ||
| 20 | + end | ||
| 21 | + | ||
| 22 | + def new | ||
| 23 | + @email_template = owner.email_templates.build(:owner => owner) | ||
| 24 | + end | ||
| 25 | + | ||
| 26 | + def edit | ||
| 27 | + @email_template = owner.email_templates.find(params[:id]) | ||
| 28 | + end | ||
| 29 | + | ||
| 30 | + def create | ||
| 31 | + @email_template = owner.email_templates.build(params[:email_template]) | ||
| 32 | + @email_template.owner = owner | ||
| 33 | + | ||
| 34 | + if @email_template.save | ||
| 35 | + session[:notice] = _('Email template was successfully created.') | ||
| 36 | + redirect_to url_for(:action => :index) | ||
| 37 | + else | ||
| 38 | + render action: "new" | ||
| 39 | + end | ||
| 40 | + end | ||
| 41 | + | ||
| 42 | + def update | ||
| 43 | + @email_template = owner.email_templates.find(params[:id]) | ||
| 44 | + | ||
| 45 | + if @email_template.update_attributes(params[:email_template]) | ||
| 46 | + session[:notice] = _('Email template was successfully updated.') | ||
| 47 | + redirect_to url_for(:action => :index) | ||
| 48 | + else | ||
| 49 | + render action: "edit" | ||
| 50 | + end | ||
| 51 | + end | ||
| 52 | + | ||
| 53 | + def destroy | ||
| 54 | + @email_template = owner.email_templates.find(params[:id]) | ||
| 55 | + @email_template.destroy | ||
| 56 | + | ||
| 57 | + respond_to do |format| | ||
| 58 | + format.html { redirect_to url_for(:action => :index)} | ||
| 59 | + format.json { head :no_content } | ||
| 60 | + end | ||
| 61 | + end | ||
| 62 | +end |
app/controllers/my_profile/manage_products_controller.rb
| @@ -206,7 +206,8 @@ class ManageProductsController < ApplicationController | @@ -206,7 +206,8 @@ class ManageProductsController < ApplicationController | ||
| 206 | end | 206 | end |
| 207 | 207 | ||
| 208 | def certifiers_for_selection | 208 | def certifiers_for_selection |
| 209 | - @qualifier = Qualifier.exists?(params[:id]) ? Qualifier.find(params[:id]) : nil | 209 | + # updated to use hash as argument to exists? to avoid sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) |
| 210 | + @qualifier = Qualifier.exists?(:id => params[:id]) ? Qualifier.find(params[:id]) : nil | ||
| 210 | render :update do |page| | 211 | render :update do |page| |
| 211 | page.replace_html params[:certifier_area], :partial => 'certifiers_for_selection' | 212 | page.replace_html params[:certifier_area], :partial => 'certifiers_for_selection' |
| 212 | end | 213 | end |
app/controllers/my_profile/profile_email_templates_controller.rb
0 → 100644
| @@ -0,0 +1,16 @@ | @@ -0,0 +1,16 @@ | ||
| 1 | +class ProfileEmailTemplatesController < EmailTemplatesController | ||
| 2 | + | ||
| 3 | + needs_profile | ||
| 4 | + protect 'manage_email_templates', :profile | ||
| 5 | + | ||
| 6 | + protected | ||
| 7 | + | ||
| 8 | + def owner | ||
| 9 | + profile | ||
| 10 | + end | ||
| 11 | + | ||
| 12 | + before_filter :only => :index do | ||
| 13 | + @back_to = url_for(:controller => :profile_editor) | ||
| 14 | + end | ||
| 15 | + | ||
| 16 | +end |
app/controllers/my_profile/tasks_controller.rb
| 1 | class TasksController < MyProfileController | 1 | class TasksController < MyProfileController |
| 2 | 2 | ||
| 3 | protect [:perform_task, :view_tasks], :profile, :only => [:index, :save_tags, :search_tags] | 3 | protect [:perform_task, :view_tasks], :profile, :only => [:index, :save_tags, :search_tags] |
| 4 | - protect :perform_task, :profile, :except => [:index, :save_tags, :search_tags] | 4 | + protect :perform_task, :profile, :only => [:processed, :change_responsible, :close, :new, :list_requested, :ticket_details, :search_tags] |
| 5 | 5 | ||
| 6 | def index | 6 | def index |
| 7 | + @rejection_email_templates = profile.email_templates.find_all_by_template_type(:task_rejection) | ||
| 8 | + @acceptance_email_templates = profile.email_templates.find_all_by_template_type(:task_acceptance) | ||
| 9 | + | ||
| 7 | @filter_type = params[:filter_type].presence | 10 | @filter_type = params[:filter_type].presence |
| 8 | @filter_text = params[:filter_text].presence | 11 | @filter_text = params[:filter_text].presence |
| 9 | @filter_responsible = params[:filter_responsible] | 12 | @filter_responsible = params[:filter_responsible] |
| @@ -19,13 +22,17 @@ class TasksController < MyProfileController | @@ -19,13 +22,17 @@ class TasksController < MyProfileController | ||
| 19 | 22 | ||
| 20 | @failed = params ? params[:failed] : {} | 23 | @failed = params ? params[:failed] : {} |
| 21 | 24 | ||
| 22 | - @responsible_candidates = profile.members.by_role(profile.roles.reject {|r| !r.has_permission?('perform_task')}) if profile.organization? | 25 | + @responsible_candidates = profile.members.by_role(profile.roles.reject {|r| !r.has_permission?('perform_task') && !r.has_permission?('view_tasks')}) if profile.organization? |
| 23 | 26 | ||
| 24 | @view_only = !current_person.has_permission?(:perform_task, profile) | 27 | @view_only = !current_person.has_permission?(:perform_task, profile) |
| 25 | end | 28 | end |
| 26 | 29 | ||
| 27 | def processed | 30 | def processed |
| 28 | - @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at) | 31 | + @tasks = Task.to(profile).without_spam.closed.order('tasks.created_at DESC') |
| 32 | + @filter = params[:filter] || {} | ||
| 33 | + @tasks = filter_tasks(@filter, @tasks) | ||
| 34 | + @tasks = @tasks.paginate(:per_page => Task.per_page, :page => params[:page]) | ||
| 35 | + @task_types = Task.closed_types_for(profile) | ||
| 29 | end | 36 | end |
| 30 | 37 | ||
| 31 | def change_responsible | 38 | def change_responsible |
| @@ -78,6 +85,7 @@ class TasksController < MyProfileController | @@ -78,6 +85,7 @@ class TasksController < MyProfileController | ||
| 78 | end | 85 | end |
| 79 | 86 | ||
| 80 | url = { :action => 'index' } | 87 | url = { :action => 'index' } |
| 88 | + | ||
| 81 | if failed.blank? | 89 | if failed.blank? |
| 82 | session[:notice] = _("All decisions were applied successfully.") | 90 | session[:notice] = _("All decisions were applied successfully.") |
| 83 | else | 91 | else |
| @@ -109,9 +117,9 @@ class TasksController < MyProfileController | @@ -109,9 +117,9 @@ class TasksController < MyProfileController | ||
| 109 | end | 117 | end |
| 110 | 118 | ||
| 111 | def search_tasks | 119 | def search_tasks |
| 112 | - | ||
| 113 | - params[:filter_type] = params[:filter_type].blank? ? nil : params[:filter_type] | ||
| 114 | - result = Task.pending_all(profile,params) | 120 | + filter_type = params[:filter_type].presence |
| 121 | + filter_text = params[:filter_text].presence | ||
| 122 | + result = Task.pending_all(profile,filter_type, filter_text) | ||
| 115 | 123 | ||
| 116 | render :json => result.map { |task| {:label => task.data[:name], :value => task.data[:name]} } | 124 | render :json => result.map { |task| {:label => task.data[:name], :value => task.data[:name]} } |
| 117 | end | 125 | end |
| @@ -125,10 +133,9 @@ class TasksController < MyProfileController | @@ -125,10 +133,9 @@ class TasksController < MyProfileController | ||
| 125 | 133 | ||
| 126 | ActsAsTaggableOn.remove_unused_tags = true | 134 | ActsAsTaggableOn.remove_unused_tags = true |
| 127 | 135 | ||
| 128 | - task = Task.to(profile).find_by_id params[:task_id] | ||
| 129 | - save = user.tag(task, with: params[:tag_list], on: :tags) | ||
| 130 | - | ||
| 131 | - if save | 136 | + task = profile.tasks.find_by_id(params[:task_id]) |
| 137 | + | ||
| 138 | + if task && task.update_attributes(:tag_list => params[:tag_list]) | ||
| 132 | result[:success] = true | 139 | result[:success] = true |
| 133 | end | 140 | end |
| 134 | end | 141 | end |
| @@ -149,7 +156,39 @@ class TasksController < MyProfileController | @@ -149,7 +156,39 @@ class TasksController < MyProfileController | ||
| 149 | result = ActsAsTaggableOn::Tag.find(:all, :conditions => ['LOWER(name) LIKE ?', "%#{arg}%"]) | 156 | result = ActsAsTaggableOn::Tag.find(:all, :conditions => ['LOWER(name) LIKE ?', "%#{arg}%"]) |
| 150 | 157 | ||
| 151 | render :text => prepare_to_token_input_by_label(result).to_json, :content_type => 'application/json' | 158 | render :text => prepare_to_token_input_by_label(result).to_json, :content_type => 'application/json' |
| 159 | + end | ||
| 160 | + | ||
| 161 | + protected | ||
| 162 | + | ||
| 163 | + def filter_by_closed_date(filter, tasks) | ||
| 164 | + filter[:closed_from] = Date.parse(filter[:closed_from]) unless filter[:closed_from].blank? | ||
| 165 | + filter[:closed_until] = Date.parse(filter[:closed_until]) unless filter[:closed_until].blank? | ||
| 166 | + | ||
| 167 | + tasks = tasks.where('tasks.end_date >= ?', filter[:closed_from].beginning_of_day) unless filter[:closed_from].blank? | ||
| 168 | + tasks = tasks.where('tasks.end_date <= ?', filter[:closed_until].end_of_day) unless filter[:closed_until].blank? | ||
| 169 | + tasks | ||
| 170 | + end | ||
| 171 | + | ||
| 172 | + def filter_by_creation_date(filter, tasks) | ||
| 173 | + filter[:created_from] = Date.parse(filter[:created_from]) unless filter[:created_from].blank? | ||
| 174 | + filter[:created_until] = Date.parse(filter[:created_until]) unless filter[:created_until].blank? | ||
| 175 | + | ||
| 176 | + tasks = tasks.where('tasks.created_at >= ?', filter[:created_from].beginning_of_day) unless filter[:created_from].blank? | ||
| 177 | + tasks = tasks.where('tasks.created_at <= ?', filter[:created_until].end_of_day) unless filter[:created_until].blank? | ||
| 178 | + tasks | ||
| 179 | + end | ||
| 152 | 180 | ||
| 181 | + def filter_tasks(filter, tasks) | ||
| 182 | + tasks = tasks.includes(:requestor, :closed_by) | ||
| 183 | + tasks = tasks.of(filter[:type].presence) | ||
| 184 | + tasks = tasks.where(:status => filter[:status]) unless filter[:status].blank? | ||
| 185 | + tasks = filter_by_creation_date(filter, tasks) | ||
| 186 | + tasks = filter_by_closed_date(filter, tasks) | ||
| 187 | + | ||
| 188 | + tasks = tasks.like('profiles.name', filter[:requestor]) unless filter[:requestor].blank? | ||
| 189 | + tasks = tasks.like('closed_bies_tasks.name', filter[:closed_by]) unless filter[:closed_by].blank? | ||
| 190 | + tasks = tasks.like('tasks.data', filter[:text]) unless filter[:text].blank? | ||
| 191 | + tasks | ||
| 153 | end | 192 | end |
| 154 | 193 | ||
| 155 | end | 194 | end |
app/controllers/public/contact_controller.rb
| @@ -6,8 +6,9 @@ class ContactController < PublicController | @@ -6,8 +6,9 @@ class ContactController < PublicController | ||
| 6 | def new | 6 | def new |
| 7 | @contact = build_contact | 7 | @contact = build_contact |
| 8 | if request.post? && params[:confirm] == 'true' | 8 | if request.post? && params[:confirm] == 'true' |
| 9 | - @contact.city = (!params[:city].blank? && City.exists?(params[:city])) ? City.find(params[:city]).name : nil | ||
| 10 | - @contact.state = (!params[:state].blank? && State.exists?(params[:state])) ? State.find(params[:state]).name : nil | 9 | + # updated to use hash as argument to exists? to avoid sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) |
| 10 | + @contact.city = (!params[:city].blank? && City.exists?(:id => params[:city])) ? City.find(params[:city]).name : nil | ||
| 11 | + @contact.state = (!params[:state].blank? && State.exists?(:id => params[:state])) ? State.find(params[:state]).name : nil | ||
| 11 | if @contact.deliver | 12 | if @contact.deliver |
| 12 | session[:notice] = _('Contact successfully sent') | 13 | session[:notice] = _('Contact successfully sent') |
| 13 | redirect_to :action => 'new' | 14 | redirect_to :action => 'new' |
app/controllers/public/profile_controller.rb
| @@ -356,6 +356,7 @@ class ProfileController < PublicController | @@ -356,6 +356,7 @@ class ProfileController < PublicController | ||
| 356 | 356 | ||
| 357 | def send_mail | 357 | def send_mail |
| 358 | @mailing = profile.mailings.build(params[:mailing]) | 358 | @mailing = profile.mailings.build(params[:mailing]) |
| 359 | + @email_templates = profile.email_templates.find_all_by_template_type(:organization_members) | ||
| 359 | if request.post? | 360 | if request.post? |
| 360 | @mailing.locale = locale | 361 | @mailing.locale = locale |
| 361 | @mailing.person = user | 362 | @mailing.person = user |
app/controllers/public/search_controller.rb
| @@ -95,10 +95,10 @@ class SearchController < PublicController | @@ -95,10 +95,10 @@ class SearchController < PublicController | ||
| 95 | 95 | ||
| 96 | def events | 96 | def events |
| 97 | if params[:year].blank? && params[:year].blank? && params[:day].blank? | 97 | if params[:year].blank? && params[:year].blank? && params[:day].blank? |
| 98 | - @date = Date.today | 98 | + @date = DateTime.now |
| 99 | else | 99 | else |
| 100 | - year = (params[:year] ? params[:year].to_i : Date.today.year) | ||
| 101 | - month = (params[:month] ? params[:month].to_i : Date.today.month) | 100 | + year = (params[:year] ? params[:year].to_i : DateTime.now.year) |
| 101 | + month = (params[:month] ? params[:month].to_i : DateTime.now.month) | ||
| 102 | day = (params[:day] ? params[:day].to_i : 1) | 102 | day = (params[:day] ? params[:day].to_i : 1) |
| 103 | @date = build_date(year, month, day) | 103 | @date = build_date(year, month, day) |
| 104 | end | 104 | end |
| @@ -109,9 +109,7 @@ class SearchController < PublicController | @@ -109,9 +109,7 @@ class SearchController < PublicController | ||
| 109 | @events = @category ? | 109 | @events = @category ? |
| 110 | environment.events.by_day(@date).in_category(Category.find(@category_id)).paginate(:per_page => per_page, :page => params[:page]) : | 110 | environment.events.by_day(@date).in_category(Category.find(@category_id)).paginate(:per_page => per_page, :page => params[:page]) : |
| 111 | environment.events.by_day(@date).paginate(:per_page => per_page, :page => params[:page]) | 111 | environment.events.by_day(@date).paginate(:per_page => per_page, :page => params[:page]) |
| 112 | - end | ||
| 113 | - | ||
| 114 | - if params[:year] || params[:month] | 112 | + elsif params[:year] || params[:month] |
| 115 | @events = @category ? | 113 | @events = @category ? |
| 116 | environment.events.by_month(@date).in_category(Category.find(@category_id)).paginate(:per_page => per_page, :page => params[:page]) : | 114 | environment.events.by_month(@date).in_category(Category.find(@category_id)).paginate(:per_page => per_page, :page => params[:page]) : |
| 117 | environment.events.by_month(@date).paginate(:per_page => per_page, :page => params[:page]) | 115 | environment.events.by_month(@date).paginate(:per_page => per_page, :page => params[:page]) |
app/helpers/application_helper.rb
| @@ -44,6 +44,8 @@ module ApplicationHelper | @@ -44,6 +44,8 @@ module ApplicationHelper | ||
| 44 | 44 | ||
| 45 | include PluginsHelper | 45 | include PluginsHelper |
| 46 | 46 | ||
| 47 | + include TaskHelper | ||
| 48 | + | ||
| 47 | def locale | 49 | def locale |
| 48 | (@page && !@page.language.blank?) ? @page.language : FastGettext.locale | 50 | (@page && !@page.language.blank?) ? @page.language : FastGettext.locale |
| 49 | end | 51 | end |
| @@ -606,24 +608,14 @@ module ApplicationHelper | @@ -606,24 +608,14 @@ module ApplicationHelper | ||
| 606 | trigger_class = 'enterprise-trigger' | 608 | trigger_class = 'enterprise-trigger' |
| 607 | end | 609 | end |
| 608 | end | 610 | end |
| 609 | - | ||
| 610 | - extra_info_tag = '' | ||
| 611 | - img_class = 'profile-image' | ||
| 612 | - | ||
| 613 | - if extra_info.is_a? Hash | ||
| 614 | - extra_info_tag = content_tag( 'span', extra_info[:value], :class => 'extra_info '+extra_info[:class]) | ||
| 615 | - img_class +=' '+extra_info[:class] | ||
| 616 | - else | ||
| 617 | - extra_info_tag = content_tag( 'span', extra_info, :class => 'extra_info' ) | ||
| 618 | - end | ||
| 619 | - #extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' ) | 611 | + extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' ) |
| 620 | links = links_for_balloon(profile) | 612 | links = links_for_balloon(profile) |
| 621 | content_tag('div', content_tag(tag, | 613 | content_tag('div', content_tag(tag, |
| 622 | (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? popover_menu(_('Profile links'),profile.short_name,links,{:class => trigger_class, :url => url}) : "") + | 614 | (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? popover_menu(_('Profile links'),profile.short_name,links,{:class => trigger_class, :url => url}) : "") + |
| 623 | link_to( | 615 | link_to( |
| 624 | - content_tag( 'span', profile_image( profile, size ), :class => img_class ) + | 616 | + content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + |
| 625 | content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) + | 617 | content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) + |
| 626 | - extra_info_tag + profile_sex_icon( profile ) + profile_cat_icons( profile ), | 618 | + extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ), |
| 627 | profile.url, | 619 | profile.url, |
| 628 | :class => 'profile_link url', | 620 | :class => 'profile_link url', |
| 629 | :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, | 621 | :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, |
app/helpers/content_viewer_helper.rb
| @@ -51,7 +51,7 @@ module ContentViewerHelper | @@ -51,7 +51,7 @@ module ContentViewerHelper | ||
| 51 | elsif date_format == 'past_time' | 51 | elsif date_format == 'past_time' |
| 52 | left_time = true | 52 | left_time = true |
| 53 | end | 53 | end |
| 54 | - content_tag('span', show_date(article.published_at, use_numbers , year, left_time), :class => 'date') | 54 | + content_tag('span', show_time(article.published_at, use_numbers , year, left_time), :class => 'date') |
| 55 | end | 55 | end |
| 56 | 56 | ||
| 57 | def link_to_comments(article, args = {}) | 57 | def link_to_comments(article, args = {}) |
app/helpers/dates_helper.rb
| @@ -43,9 +43,14 @@ module DatesHelper | @@ -43,9 +43,14 @@ module DatesHelper | ||
| 43 | end | 43 | end |
| 44 | 44 | ||
| 45 | # formats a datetime for displaying. | 45 | # formats a datetime for displaying. |
| 46 | - def show_time(time) | ||
| 47 | - if time | ||
| 48 | - _('%{day} %{month} %{year}, %{hour}:%{minutes}') % { :year => time.year, :month => month_name(time.month), :day => time.day, :hour => time.hour, :minutes => time.strftime("%M") } | 46 | + def show_time(time, use_numbers = false, year = true, left_time = false) |
| 47 | + if time && use_numbers | ||
| 48 | + _('%{month}/%{day}/%{year}, %{hour}:%{minutes}') % { :year => (year ? time.year : ''), :month => time.month, :day => time.day, :hour => time.hour, :minutes => time.strftime("%M") } | ||
| 49 | + elsif time && left_time | ||
| 50 | + date_format = time_ago_in_words(time) | ||
| 51 | + elsif time | ||
| 52 | + date_format = year ? _('%{month_name} %{day}, %{year} %{hour}:%{minutes}') : _('%{month_name} %{day} %{hour}:%{minutes}') | ||
| 53 | + date_format % { :day => time.day, :month_name => month_name(time.month), :year => time.year, :hour => time.hour, :minutes => time.strftime("%M") } | ||
| 49 | else | 54 | else |
| 50 | '' | 55 | '' |
| 51 | end | 56 | end |
| @@ -53,7 +58,7 @@ module DatesHelper | @@ -53,7 +58,7 @@ module DatesHelper | ||
| 53 | 58 | ||
| 54 | def show_period(date1, date2 = nil, use_numbers = false) | 59 | def show_period(date1, date2 = nil, use_numbers = false) |
| 55 | if (date1 == date2) || (date2.nil?) | 60 | if (date1 == date2) || (date2.nil?) |
| 56 | - show_date(date1, use_numbers) | 61 | + show_time(date1, use_numbers) |
| 57 | else | 62 | else |
| 58 | if date1.year == date2.year | 63 | if date1.year == date2.year |
| 59 | if date1.month == date2.month | 64 | if date1.month == date2.month |
| @@ -72,8 +77,8 @@ module DatesHelper | @@ -72,8 +77,8 @@ module DatesHelper | ||
| 72 | end | 77 | end |
| 73 | else | 78 | else |
| 74 | _('from %{date1} to %{date2}') % { | 79 | _('from %{date1} to %{date2}') % { |
| 75 | - :date1 => show_date(date1, use_numbers), | ||
| 76 | - :date2 => show_date(date2, use_numbers) | 80 | + :date1 => show_time(date1, use_numbers), |
| 81 | + :date2 => show_time(date2, use_numbers) | ||
| 77 | } | 82 | } |
| 78 | end | 83 | end |
| 79 | end | 84 | end |
| @@ -106,18 +111,18 @@ module DatesHelper | @@ -106,18 +111,18 @@ module DatesHelper | ||
| 106 | 111 | ||
| 107 | def build_date(year, month, day = 1) | 112 | def build_date(year, month, day = 1) |
| 108 | if year.blank? and month.blank? and day.blank? | 113 | if year.blank? and month.blank? and day.blank? |
| 109 | - Date.today | 114 | + DateTime.now |
| 110 | else | 115 | else |
| 111 | if year.blank? | 116 | if year.blank? |
| 112 | - year = Date.today.year | 117 | + year = DateTime.now.year |
| 113 | end | 118 | end |
| 114 | if month.blank? | 119 | if month.blank? |
| 115 | - month = Date.today.month | 120 | + month = DateTime.now.month |
| 116 | end | 121 | end |
| 117 | if day.blank? | 122 | if day.blank? |
| 118 | day = 1 | 123 | day = 1 |
| 119 | end | 124 | end |
| 120 | - Date.new(year.to_i, month.to_i, day.to_i) | 125 | + DateTime.new(year.to_i, month.to_i, day.to_i) |
| 121 | end | 126 | end |
| 122 | end | 127 | end |
| 123 | 128 |
| @@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
| 1 | +module EmailTemplateHelper | ||
| 2 | + | ||
| 3 | + def mail_with_template(params={}) | ||
| 4 | + if params[:email_template].present? | ||
| 5 | + params[:body] = params[:email_template].parsed_body(params[:template_params]) | ||
| 6 | + params[:subject] = params[:email_template].parsed_subject(params[:template_params]) | ||
| 7 | + params[:content_type] = "text/html" | ||
| 8 | + end | ||
| 9 | + mail(params.except(:email_template)) | ||
| 10 | + end | ||
| 11 | + | ||
| 12 | +end |
app/helpers/events_helper.rb
| @@ -16,7 +16,7 @@ module EventsHelper | @@ -16,7 +16,7 @@ module EventsHelper | ||
| 16 | 16 | ||
| 17 | content_tag( 'tr', | 17 | content_tag( 'tr', |
| 18 | content_tag('td', | 18 | content_tag('td', |
| 19 | - content_tag('div', show_date(article.start_date) + ( article.end_date.nil? ? '' : (_(" to ") + show_date(article.end_date))),:class => 'event-date' ) + | 19 | + content_tag('div', show_time(article.start_date) + ( article.end_date.nil? ? '' : (_(" to ") + show_time(article.end_date))),:class => 'event-date' ) + |
| 20 | content_tag('div',link_to(article.name,article.url),:class => 'event-title') + | 20 | content_tag('div',link_to(article.name,article.url),:class => 'event-title') + |
| 21 | content_tag('div',(article.address.nil? or article.address == '') ? '' : (_('Place: ') + article.address),:class => 'event-place') | 21 | content_tag('div',(article.address.nil? or article.address == '') ? '' : (_('Place: ') + article.address),:class => 'event-place') |
| 22 | ) | 22 | ) |
| @@ -30,7 +30,7 @@ module EventsHelper | @@ -30,7 +30,7 @@ module EventsHelper | ||
| 30 | # the day itself | 30 | # the day itself |
| 31 | date, | 31 | date, |
| 32 | # is there any events in this date? | 32 | # is there any events in this date? |
| 33 | - events.any? {|event| event.date_range.include?(date)}, | 33 | + events.any? {|event| event.date_range.cover?(date)}, |
| 34 | # is this date in the current month? | 34 | # is this date in the current month? |
| 35 | true | 35 | true |
| 36 | ] | 36 | ] |
app/helpers/forms_helper.rb
| @@ -151,7 +151,7 @@ module FormsHelper | @@ -151,7 +151,7 @@ module FormsHelper | ||
| 151 | datepicker_options[:close_text] ||= _('Done') | 151 | datepicker_options[:close_text] ||= _('Done') |
| 152 | datepicker_options[:constrain_input] ||= true | 152 | datepicker_options[:constrain_input] ||= true |
| 153 | datepicker_options[:current_text] ||= _('Today') | 153 | datepicker_options[:current_text] ||= _('Today') |
| 154 | - datepicker_options[:date_format] ||= 'mm/dd/yy' | 154 | + datepicker_options[:date_format] ||= 'yy/mm/dd' |
| 155 | datepicker_options[:day_names] ||= [_('Sunday'), _('Monday'), _('Tuesday'), _('Wednesday'), _('Thursday'), _('Friday'), _('Saturday')] | 155 | datepicker_options[:day_names] ||= [_('Sunday'), _('Monday'), _('Tuesday'), _('Wednesday'), _('Thursday'), _('Friday'), _('Saturday')] |
| 156 | datepicker_options[:day_names_min] ||= [_('Su'), _('Mo'), _('Tu'), _('We'), _('Th'), _('Fr'), _('Sa')] | 156 | datepicker_options[:day_names_min] ||= [_('Su'), _('Mo'), _('Tu'), _('We'), _('Th'), _('Fr'), _('Sa')] |
| 157 | datepicker_options[:day_names_short] ||= [_('Sun'), _('Mon'), _('Tue'), _('Wed'), _('Thu'), _('Fri'), _('Sat')] | 157 | datepicker_options[:day_names_short] ||= [_('Sun'), _('Mon'), _('Tue'), _('Wed'), _('Thu'), _('Fri'), _('Sat')] |
| @@ -236,7 +236,7 @@ module FormsHelper | @@ -236,7 +236,7 @@ module FormsHelper | ||
| 236 | weekHeader: #{datepicker_options[:week_header].to_json}, | 236 | weekHeader: #{datepicker_options[:week_header].to_json}, |
| 237 | yearRange: #{datepicker_options[:year_range].to_json}, | 237 | yearRange: #{datepicker_options[:year_range].to_json}, |
| 238 | yearSuffix: #{datepicker_options[:year_suffix].to_json} | 238 | yearSuffix: #{datepicker_options[:year_suffix].to_json} |
| 239 | - }) | 239 | + }).datepicker('setDate', new Date('#{value}')) |
| 240 | </script> | 240 | </script> |
| 241 | ".html_safe | 241 | ".html_safe |
| 242 | result | 242 | result |
| @@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
| 1 | +module TaskHelper | ||
| 2 | + | ||
| 3 | + def task_email_template(description, email_templates, task, include_blank=true) | ||
| 4 | + return '' unless email_templates.present? | ||
| 5 | + | ||
| 6 | + content_tag( | ||
| 7 | + :div, | ||
| 8 | + labelled_form_field(description, select_tag("tasks[#{task.id}][task][email_template_id]", options_from_collection_for_select(email_templates, :id, :name), :include_blank => include_blank, 'data-url' => url_for(:controller => 'profile_email_templates', :action => 'show_parsed', :profile => profile.identifier))), | ||
| 9 | + :class => 'template-selection' | ||
| 10 | + ) | ||
| 11 | + end | ||
| 12 | + | ||
| 13 | + def task_action action | ||
| 14 | + base_url = { action: action } | ||
| 15 | + url_for(base_url.merge(filter_params)) | ||
| 16 | + end | ||
| 17 | + | ||
| 18 | + def filter_params | ||
| 19 | + filter_fields = ['filter_type', 'filter_text', 'filter_responsible', 'filter_tags'] | ||
| 20 | + params.select {|filter| filter if filter_fields.include? filter } | ||
| 21 | + end | ||
| 22 | + | ||
| 23 | +end |
app/mailers/task_mailer.rb
| 1 | class TaskMailer < ActionMailer::Base | 1 | class TaskMailer < ActionMailer::Base |
| 2 | 2 | ||
| 3 | + include EmailTemplateHelper | ||
| 4 | + | ||
| 3 | def target_notification(task, message) | 5 | def target_notification(task, message) |
| 4 | @message = extract_message(message) | 6 | @message = extract_message(message) |
| 5 | @target = task.target.name | 7 | @target = task.target.name |
| @@ -34,10 +36,12 @@ class TaskMailer < ActionMailer::Base | @@ -34,10 +36,12 @@ class TaskMailer < ActionMailer::Base | ||
| 34 | @environment = task.requestor.environment.name | 36 | @environment = task.requestor.environment.name |
| 35 | @url = url_for(:host => task.requestor.environment.default_hostname, :controller => 'home') | 37 | @url = url_for(:host => task.requestor.environment.default_hostname, :controller => 'home') |
| 36 | 38 | ||
| 37 | - mail( | 39 | + mail_with_template( |
| 38 | to: task.requestor.notification_emails, | 40 | to: task.requestor.notification_emails, |
| 39 | from: self.class.generate_from(task), | 41 | from: self.class.generate_from(task), |
| 40 | - subject: '[%s] %s' % [task.requestor.environment.name, task.target_notification_description] | 42 | + subject: '[%s] %s' % [task.requestor.environment.name, task.target_notification_description], |
| 43 | + email_template: task.email_template, | ||
| 44 | + template_params: {:environment => task.requestor.environment, :task => task, :message => @message, :url => @url, :requestor => task.requestor} | ||
| 41 | ) | 45 | ) |
| 42 | end | 46 | end |
| 43 | 47 |
app/mailers/user_mailer.rb
| 1 | class UserMailer < ActionMailer::Base | 1 | class UserMailer < ActionMailer::Base |
| 2 | + | ||
| 3 | + include EmailTemplateHelper | ||
| 4 | + | ||
| 2 | def activation_email_notify(user) | 5 | def activation_email_notify(user) |
| 3 | user_email = "#{user.login}@#{user.email_domain}" | 6 | user_email = "#{user.login}@#{user.email_domain}" |
| 4 | @name = user.name | 7 | @name = user.name |
| @@ -22,10 +25,12 @@ class UserMailer < ActionMailer::Base | @@ -22,10 +25,12 @@ class UserMailer < ActionMailer::Base | ||
| 22 | @redirection = (true if user.return_to) | 25 | @redirection = (true if user.return_to) |
| 23 | @join = (user.community_to_join if user.community_to_join) | 26 | @join = (user.community_to_join if user.community_to_join) |
| 24 | 27 | ||
| 25 | - mail( | 28 | + mail_with_template( |
| 26 | from: "#{user.environment.name} <#{user.environment.contact_email}>", | 29 | from: "#{user.environment.name} <#{user.environment.contact_email}>", |
| 27 | to: user.email, | 30 | to: user.email, |
| 28 | subject: _("[%s] Activate your account") % [user.environment.name], | 31 | subject: _("[%s] Activate your account") % [user.environment.name], |
| 32 | + template_params: {:environment => user.environment, :activation_code => @activation_code, :redirection => @redirection, :join => @join}, | ||
| 33 | + email_template: user.environment.email_templates.find_by_template_type(:user_activation), | ||
| 29 | ) | 34 | ) |
| 30 | end | 35 | end |
| 31 | 36 |
app/models/article.rb
| @@ -645,6 +645,14 @@ class Article < ActiveRecord::Base | @@ -645,6 +645,14 @@ class Article < ActiveRecord::Base | ||
| 645 | can_display_hits? && display_hits | 645 | can_display_hits? && display_hits |
| 646 | end | 646 | end |
| 647 | 647 | ||
| 648 | + def display_media_panel? | ||
| 649 | + can_display_media_panel? && environment.enabled?('media_panel') | ||
| 650 | + end | ||
| 651 | + | ||
| 652 | + def can_display_media_panel? | ||
| 653 | + false | ||
| 654 | + end | ||
| 655 | + | ||
| 648 | def image? | 656 | def image? |
| 649 | false | 657 | false |
| 650 | end | 658 | end |
| @@ -813,6 +821,10 @@ class Article < ActiveRecord::Base | @@ -813,6 +821,10 @@ class Article < ActiveRecord::Base | ||
| 813 | "content_viewer/view_page" | 821 | "content_viewer/view_page" |
| 814 | end | 822 | end |
| 815 | 823 | ||
| 824 | + def to_liquid | ||
| 825 | + HashWithIndifferentAccess.new :name => name, :abstract => abstract, :body => body, :id => id, :parent_id => parent_id, :author => author | ||
| 826 | + end | ||
| 827 | + | ||
| 816 | private | 828 | private |
| 817 | 829 | ||
| 818 | def sanitize_tag_list | 830 | def sanitize_tag_list |
app/models/change_password.rb
| @@ -28,6 +28,13 @@ class ChangePassword < Task | @@ -28,6 +28,13 @@ class ChangePassword < Task | ||
| 28 | validates_presence_of :password_confirmation, :on => :update, :if => lambda { |change| change.status != Task::Status::CANCELLED } | 28 | validates_presence_of :password_confirmation, :on => :update, :if => lambda { |change| change.status != Task::Status::CANCELLED } |
| 29 | validates_confirmation_of :password, :if => lambda { |change| change.status != Task::Status::CANCELLED } | 29 | validates_confirmation_of :password, :if => lambda { |change| change.status != Task::Status::CANCELLED } |
| 30 | 30 | ||
| 31 | + before_save :set_email_template | ||
| 32 | + | ||
| 33 | + def set_email_template | ||
| 34 | + template = environment.email_templates.find_by_template_type(:user_change_password) | ||
| 35 | + data[:email_template_id] = template.id unless template.nil? | ||
| 36 | + end | ||
| 37 | + | ||
| 31 | def environment | 38 | def environment |
| 32 | requestor.environment unless requestor.nil? | 39 | requestor.environment unless requestor.nil? |
| 33 | end | 40 | end |
| @@ -0,0 +1,50 @@ | @@ -0,0 +1,50 @@ | ||
| 1 | +class EmailTemplate < ActiveRecord::Base | ||
| 2 | + | ||
| 3 | + belongs_to :owner, :polymorphic => true | ||
| 4 | + | ||
| 5 | + attr_accessible :template_type, :subject, :body, :owner, :name | ||
| 6 | + | ||
| 7 | + validates_presence_of :name | ||
| 8 | + | ||
| 9 | + validates :name, uniqueness: { scope: [:owner_type, :owner_id] } | ||
| 10 | + | ||
| 11 | + validates :template_type, uniqueness: { scope: [:owner_type, :owner_id] }, if: :unique_by_type? | ||
| 12 | + | ||
| 13 | + def parsed_body(params) | ||
| 14 | + @parsed_body ||= parse(body, params) | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | + def parsed_subject(params) | ||
| 18 | + @parsed_subject ||= parse(subject, params) | ||
| 19 | + end | ||
| 20 | + | ||
| 21 | + def self.available_types | ||
| 22 | + { | ||
| 23 | + :task_rejection => {:description => _('Task Rejection'), :owner_type => Profile}, | ||
| 24 | + :task_acceptance => {:description => _('Task Acceptance'), :owner_type => Profile}, | ||
| 25 | + :organization_members => {:description => _('Organization Members'), :owner_type => Profile}, | ||
| 26 | + :user_activation => {:description => _('User Activation'), :unique => true, :owner_type => Environment}, | ||
| 27 | + :user_change_password => {:description => _('Change User Password'), :unique => true, :owner_type => Environment} | ||
| 28 | + } | ||
| 29 | + end | ||
| 30 | + | ||
| 31 | + def available_types | ||
| 32 | + HashWithIndifferentAccess.new EmailTemplate.available_types.select {|k, v| owner.kind_of?(v[:owner_type])} | ||
| 33 | + end | ||
| 34 | + | ||
| 35 | + def type_description | ||
| 36 | + available_types.fetch(template_type, {})[:description] | ||
| 37 | + end | ||
| 38 | + | ||
| 39 | + def unique_by_type? | ||
| 40 | + available_types.fetch(template_type, {})[:unique] | ||
| 41 | + end | ||
| 42 | + | ||
| 43 | + protected | ||
| 44 | + | ||
| 45 | + def parse(source, params) | ||
| 46 | + template = Liquid::Template.parse(source) | ||
| 47 | + template.render(HashWithIndifferentAccess.new(params)) | ||
| 48 | + end | ||
| 49 | + | ||
| 50 | +end |
app/models/enterprise_homepage.rb
app/models/environment.rb
| @@ -21,6 +21,7 @@ class Environment < ActiveRecord::Base | @@ -21,6 +21,7 @@ class Environment < ActiveRecord::Base | ||
| 21 | 21 | ||
| 22 | has_many :tasks, :dependent => :destroy, :as => 'target' | 22 | has_many :tasks, :dependent => :destroy, :as => 'target' |
| 23 | has_many :search_terms, :as => :context | 23 | has_many :search_terms, :as => :context |
| 24 | + has_many :email_templates, :foreign_key => :owner_id | ||
| 24 | 25 | ||
| 25 | IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/ | 26 | IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/ |
| 26 | 27 | ||
| @@ -50,6 +51,7 @@ class Environment < ActiveRecord::Base | @@ -50,6 +51,7 @@ class Environment < ActiveRecord::Base | ||
| 50 | 'manage_environment_licenses' => N_('Manage environment licenses'), | 51 | 'manage_environment_licenses' => N_('Manage environment licenses'), |
| 51 | 'manage_environment_trusted_sites' => N_('Manage environment trusted sites'), | 52 | 'manage_environment_trusted_sites' => N_('Manage environment trusted sites'), |
| 52 | 'edit_appearance' => N_('Edit appearance'), | 53 | 'edit_appearance' => N_('Edit appearance'), |
| 54 | + 'manage_email_templates' => N_('Manage Email Templates'), | ||
| 53 | } | 55 | } |
| 54 | 56 | ||
| 55 | module Roles | 57 | module Roles |
| @@ -985,6 +987,10 @@ class Environment < ActiveRecord::Base | @@ -985,6 +987,10 @@ class Environment < ActiveRecord::Base | ||
| 985 | self.licenses.any? | 987 | self.licenses.any? |
| 986 | end | 988 | end |
| 987 | 989 | ||
| 990 | + def to_liquid | ||
| 991 | + HashWithIndifferentAccess.new :name => name | ||
| 992 | + end | ||
| 993 | + | ||
| 988 | private | 994 | private |
| 989 | 995 | ||
| 990 | def default_language_available | 996 | def default_language_available |
app/models/event.rb
| @@ -3,13 +3,14 @@ require 'builder' | @@ -3,13 +3,14 @@ require 'builder' | ||
| 3 | 3 | ||
| 4 | class Event < Article | 4 | class Event < Article |
| 5 | 5 | ||
| 6 | - attr_accessible :start_date, :end_date, :link, :address | 6 | + attr_accessible :start_date, :end_date, :link, :address, :presenter |
| 7 | 7 | ||
| 8 | def self.type_name | 8 | def self.type_name |
| 9 | _('Event') | 9 | _('Event') |
| 10 | end | 10 | end |
| 11 | 11 | ||
| 12 | settings_items :address, :type => :string | 12 | settings_items :address, :type => :string |
| 13 | + settings_items :presenter, :type => :string | ||
| 13 | 14 | ||
| 14 | def link=(value) | 15 | def link=(value) |
| 15 | self.setting[:link] = maybe_add_http(value) | 16 | self.setting[:link] = maybe_add_http(value) |
| @@ -23,7 +24,7 @@ class Event < Article | @@ -23,7 +24,7 @@ class Event < Article | ||
| 23 | 24 | ||
| 24 | def initialize(*args) | 25 | def initialize(*args) |
| 25 | super(*args) | 26 | super(*args) |
| 26 | - self.start_date ||= Date.today | 27 | + self.start_date ||= DateTime.now |
| 27 | end | 28 | end |
| 28 | 29 | ||
| 29 | validates_presence_of :title, :start_date | 30 | validates_presence_of :title, :start_date |
| @@ -35,7 +36,7 @@ class Event < Article | @@ -35,7 +36,7 @@ class Event < Article | ||
| 35 | end | 36 | end |
| 36 | 37 | ||
| 37 | scope :by_day, lambda { |date| | 38 | scope :by_day, lambda { |date| |
| 38 | - { :conditions => ['start_date = :date AND end_date IS NULL OR (start_date <= :date AND end_date >= :date)', {:date => date}], | 39 | + { :conditions => [' start_date >= :start_date AND start_date <= :end_date AND end_date IS NULL OR (start_date <= :end_date AND end_date >= :start_date)', {:start_date => date.beginning_of_day, :end_date => date.end_of_day}], |
| 39 | :order => 'start_date ASC' | 40 | :order => 'start_date ASC' |
| 40 | } | 41 | } |
| 41 | } | 42 | } |
| @@ -80,7 +81,7 @@ class Event < Article | @@ -80,7 +81,7 @@ class Event < Article | ||
| 80 | 81 | ||
| 81 | def self.date_range(year, month) | 82 | def self.date_range(year, month) |
| 82 | if year.nil? || month.nil? | 83 | if year.nil? || month.nil? |
| 83 | - today = Date.today | 84 | + today = DateTime.now |
| 84 | year = today.year | 85 | year = today.year |
| 85 | month = today.month | 86 | month = today.month |
| 86 | else | 87 | else |
| @@ -88,7 +89,7 @@ class Event < Article | @@ -88,7 +89,7 @@ class Event < Article | ||
| 88 | month = month.to_i | 89 | month = month.to_i |
| 89 | end | 90 | end |
| 90 | 91 | ||
| 91 | - first_day = Date.new(year, month, 1) | 92 | + first_day = DateTime.new(year, month, 1) |
| 92 | last_day = first_day + 1.month - 1.day | 93 | last_day = first_day + 1.month - 1.day |
| 93 | 94 | ||
| 94 | first_day..last_day | 95 | first_day..last_day |
| @@ -134,6 +135,10 @@ class Event < Article | @@ -134,6 +135,10 @@ class Event < Article | ||
| 134 | true | 135 | true |
| 135 | end | 136 | end |
| 136 | 137 | ||
| 138 | + def can_display_media_panel? | ||
| 139 | + true | ||
| 140 | + end | ||
| 141 | + | ||
| 137 | include Noosfero::TranslatableContent | 142 | include Noosfero::TranslatableContent |
| 138 | include MaybeAddHttp | 143 | include MaybeAddHttp |
| 139 | 144 |
app/models/organization.rb
| @@ -178,7 +178,11 @@ class Organization < Profile | @@ -178,7 +178,11 @@ class Organization < Profile | ||
| 178 | end | 178 | end |
| 179 | 179 | ||
| 180 | def notification_emails | 180 | def notification_emails |
| 181 | + # TODO: Add performance improvement here! | ||
| 182 | + #emails = [contact_email].select(&:present?) + admins([:user]).pluck(:email) | ||
| 183 | + # Revert change to make the tests pass | ||
| 181 | emails = [contact_email].select(&:present?) + admins.map(&:email) | 184 | emails = [contact_email].select(&:present?) + admins.map(&:email) |
| 185 | + | ||
| 182 | if emails.empty? | 186 | if emails.empty? |
| 183 | emails << environment.contact_email | 187 | emails << environment.contact_email |
| 184 | end | 188 | end |
app/models/person.rb
| @@ -19,12 +19,13 @@ class Person < Profile | @@ -19,12 +19,13 @@ class Person < Profile | ||
| 19 | acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)} | 19 | acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)} |
| 20 | acts_as_accessor | 20 | acts_as_accessor |
| 21 | 21 | ||
| 22 | - acts_as_tagger | ||
| 23 | 22 | ||
| 24 | - scope :members_of, lambda { |resources| | 23 | + scope :members_of, lambda { |resources, extra_joins = nil| |
| 25 | resources = [resources] if !resources.kind_of?(Array) | 24 | resources = [resources] if !resources.kind_of?(Array) |
| 26 | conditions = resources.map {|resource| "role_assignments.resource_type = '#{resource.class.base_class.name}' AND role_assignments.resource_id = #{resource.id || -1}"}.join(' OR ') | 25 | conditions = resources.map {|resource| "role_assignments.resource_type = '#{resource.class.base_class.name}' AND role_assignments.resource_id = #{resource.id || -1}"}.join(' OR ') |
| 27 | - { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => [conditions] } | 26 | + joins = [:role_assignments] |
| 27 | + joins += extra_joins if extra_joins.is_a? Array | ||
| 28 | + { :select => 'DISTINCT profiles.*', :joins => joins, :conditions => [conditions] } | ||
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | scope :not_members_of, lambda { |resources| | 31 | scope :not_members_of, lambda { |resources| |
| @@ -33,10 +34,11 @@ class Person < Profile | @@ -33,10 +34,11 @@ class Person < Profile | ||
| 33 | { :select => 'DISTINCT profiles.*', :conditions => ['"profiles"."id" NOT IN (SELECT DISTINCT profiles.id FROM "profiles" INNER JOIN "role_assignments" ON "role_assignments"."accessor_id" = "profiles"."id" AND "role_assignments"."accessor_type" = (\'Profile\') WHERE "profiles"."type" IN (\'Person\') AND (%s))' % conditions] } | 34 | { :select => 'DISTINCT profiles.*', :conditions => ['"profiles"."id" NOT IN (SELECT DISTINCT profiles.id FROM "profiles" INNER JOIN "role_assignments" ON "role_assignments"."accessor_id" = "profiles"."id" AND "role_assignments"."accessor_type" = (\'Profile\') WHERE "profiles"."type" IN (\'Person\') AND (%s))' % conditions] } |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | - scope :by_role, lambda { |roles| | 37 | + scope :by_role, lambda { |roles, extra_joins = nil| |
| 37 | roles = [roles] unless roles.kind_of?(Array) | 38 | roles = [roles] unless roles.kind_of?(Array) |
| 38 | - { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.role_id IN (?)', | ||
| 39 | -roles] } | 39 | + joins = [:role_assignments] |
| 40 | + joins += extra_joins if extra_joins.is_a? Array | ||
| 41 | + { :select => 'DISTINCT profiles.*', :joins => joins, :conditions => ['role_assignments.role_id IN (?)',roles] } | ||
| 40 | } | 42 | } |
| 41 | 43 | ||
| 42 | scope :not_friends_of, lambda { |resources| | 44 | scope :not_friends_of, lambda { |resources| |
| @@ -216,7 +218,7 @@ roles] } | @@ -216,7 +218,7 @@ roles] } | ||
| 216 | contact_informatioin | 218 | contact_informatioin |
| 217 | ] | 219 | ] |
| 218 | 220 | ||
| 219 | - xss_terminate :only => [ :custom_footer, :custom_header, :description, :preferred_domain, :nickname, :sex, :nationality, :country, :state, :city, :district, :zip_code, :address, :address_reference, :cell_phone, :comercial_phone, :personal_website, :jabber_id, :schooling, :formation, :custom_formation, :area_of_study, :custom_area_of_study, :professional_activity, :organization, :organization_website, :contact_phone, :contact_information ], :with => 'white_list' | 221 | + xss_terminate :only => [ :custom_footer, :custom_header, :description, :nickname, :sex, :nationality, :country, :state, :city, :district, :zip_code, :address, :address_reference, :cell_phone, :comercial_phone, :personal_website, :jabber_id, :schooling, :formation, :custom_formation, :area_of_study, :custom_area_of_study, :professional_activity, :organization, :organization_website, :contact_phone, :contact_information ], :with => 'white_list' |
| 220 | 222 | ||
| 221 | validates_multiparameter_assignments | 223 | validates_multiparameter_assignments |
| 222 | 224 | ||
| @@ -310,7 +312,7 @@ roles] } | @@ -310,7 +312,7 @@ roles] } | ||
| 310 | end | 312 | end |
| 311 | 313 | ||
| 312 | after_update do |person| | 314 | after_update do |person| |
| 313 | - person.user.save! | 315 | + person.user.save! unless person.user.changes.blank? |
| 314 | end | 316 | end |
| 315 | 317 | ||
| 316 | def is_admin?(environment = nil) | 318 | def is_admin?(environment = nil) |
app/models/product_category.rb
| @@ -13,8 +13,11 @@ class ProductCategory < Category | @@ -13,8 +13,11 @@ class ProductCategory < Category | ||
| 13 | scope :by_environment, lambda { |environment| { | 13 | scope :by_environment, lambda { |environment| { |
| 14 | :conditions => ['environment_id = ?', environment.id] | 14 | :conditions => ['environment_id = ?', environment.id] |
| 15 | }} | 15 | }} |
| 16 | + | ||
| 17 | + # updated scope method to avoid sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) | ||
| 18 | + # explicited to_i on level argument | ||
| 16 | scope :unique_by_level, lambda { |level| { | 19 | scope :unique_by_level, lambda { |level| { |
| 17 | - :select => "DISTINCT ON (filtered_category) split_part(path, '/', #{level}) AS filtered_category, categories.*" | 20 | + :select => "DISTINCT ON (filtered_category) split_part(path, '/', #{level.to_i}) AS filtered_category, categories.*" |
| 18 | }} | 21 | }} |
| 19 | 22 | ||
| 20 | def all_products | 23 | def all_products |
app/models/profile.rb
| @@ -3,7 +3,7 @@ | @@ -3,7 +3,7 @@ | ||
| 3 | # which by default is the one returned by Environment:default. | 3 | # which by default is the one returned by Environment:default. |
| 4 | class Profile < ActiveRecord::Base | 4 | class Profile < ActiveRecord::Base |
| 5 | 5 | ||
| 6 | - attr_accessible :name, :identifier, :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :environment, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time, :redirection_after_login, :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :custom_fields | 6 | + attr_accessible :name, :identifier, :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :environment, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time, :redirection_after_login, :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :custom_fields, :administrator_mail_notification, :region |
| 7 | 7 | ||
| 8 | # use for internationalizable human type names in search facets | 8 | # use for internationalizable human type names in search facets |
| 9 | # reimplement on subclasses | 9 | # reimplement on subclasses |
| @@ -107,6 +107,7 @@ class Profile < ActiveRecord::Base | @@ -107,6 +107,7 @@ class Profile < ActiveRecord::Base | ||
| 107 | 'invite_members' => N_('Invite members'), | 107 | 'invite_members' => N_('Invite members'), |
| 108 | 'send_mail_to_members' => N_('Send e-Mail to members'), | 108 | 'send_mail_to_members' => N_('Send e-Mail to members'), |
| 109 | 'manage_custom_roles' => N_('Manage custom roles'), | 109 | 'manage_custom_roles' => N_('Manage custom roles'), |
| 110 | + 'manage_email_templates' => N_('Manage Email Templates'), | ||
| 110 | } | 111 | } |
| 111 | 112 | ||
| 112 | acts_as_accessible | 113 | acts_as_accessible |
| @@ -191,8 +192,8 @@ class Profile < ActiveRecord::Base | @@ -191,8 +192,8 @@ class Profile < ActiveRecord::Base | ||
| 191 | alias_method_chain :count, :distinct | 192 | alias_method_chain :count, :distinct |
| 192 | end | 193 | end |
| 193 | 194 | ||
| 194 | - def members_by_role(roles) | ||
| 195 | - Person.members_of(self).by_role(roles) | 195 | + def members_by_role(roles, with_entities = nil) |
| 196 | + Person.members_of(self, with_entities).by_role(roles, with_entities) | ||
| 196 | end | 197 | end |
| 197 | 198 | ||
| 198 | acts_as_having_boxes | 199 | acts_as_having_boxes |
| @@ -223,6 +224,8 @@ class Profile < ActiveRecord::Base | @@ -223,6 +224,8 @@ class Profile < ActiveRecord::Base | ||
| 223 | 224 | ||
| 224 | has_many :comments_received, :class_name => 'Comment', :through => :articles, :source => :comments | 225 | has_many :comments_received, :class_name => 'Comment', :through => :articles, :source => :comments |
| 225 | 226 | ||
| 227 | + has_many :email_templates, :foreign_key => :owner_id | ||
| 228 | + | ||
| 226 | # Although this should be a has_one relation, there are no non-silly names for | 229 | # Although this should be a has_one relation, there are no non-silly names for |
| 227 | # a foreign key on article to reference the template to which it is | 230 | # a foreign key on article to reference the template to which it is |
| 228 | # welcome_page... =P | 231 | # welcome_page... =P |
| @@ -250,6 +253,7 @@ class Profile < ActiveRecord::Base | @@ -250,6 +253,7 @@ class Profile < ActiveRecord::Base | ||
| 250 | settings_items :description | 253 | settings_items :description |
| 251 | settings_items :fields_privacy, :type => :hash, :default => {} | 254 | settings_items :fields_privacy, :type => :hash, :default => {} |
| 252 | settings_items :email_suggestions, :type => :boolean, :default => false | 255 | settings_items :email_suggestions, :type => :boolean, :default => false |
| 256 | + settings_items :administrator_mail_notification, :type => :boolean, :default => true | ||
| 253 | 257 | ||
| 254 | validates_length_of :description, :maximum => 550, :allow_nil => true | 258 | validates_length_of :description, :maximum => 550, :allow_nil => true |
| 255 | 259 | ||
| @@ -543,6 +547,10 @@ class Profile < ActiveRecord::Base | @@ -543,6 +547,10 @@ class Profile < ActiveRecord::Base | ||
| 543 | self.articles.find(:all, options) | 547 | self.articles.find(:all, options) |
| 544 | end | 548 | end |
| 545 | 549 | ||
| 550 | + def to_liquid | ||
| 551 | + HashWithIndifferentAccess.new :name => name, :identifier => identifier | ||
| 552 | + end | ||
| 553 | + | ||
| 546 | class << self | 554 | class << self |
| 547 | 555 | ||
| 548 | # finds a profile by its identifier. This method is a shortcut to | 556 | # finds a profile by its identifier. This method is a shortcut to |
| @@ -685,15 +693,15 @@ private :generate_url, :url_options | @@ -685,15 +693,15 @@ private :generate_url, :url_options | ||
| 685 | after_create :insert_default_article_set | 693 | after_create :insert_default_article_set |
| 686 | def insert_default_article_set | 694 | def insert_default_article_set |
| 687 | if template | 695 | if template |
| 688 | - copy_articles_from template | 696 | + self.save! if copy_articles_from template |
| 689 | else | 697 | else |
| 690 | default_set_of_articles.each do |article| | 698 | default_set_of_articles.each do |article| |
| 691 | article.profile = self | 699 | article.profile = self |
| 692 | article.advertise = false | 700 | article.advertise = false |
| 693 | article.save! | 701 | article.save! |
| 694 | end | 702 | end |
| 703 | + self.save! | ||
| 695 | end | 704 | end |
| 696 | - self.save! | ||
| 697 | end | 705 | end |
| 698 | 706 | ||
| 699 | # Override this method in subclasses of Profile to create a default article | 707 | # Override this method in subclasses of Profile to create a default article |
| @@ -714,10 +722,12 @@ private :generate_url, :url_options | @@ -714,10 +722,12 @@ private :generate_url, :url_options | ||
| 714 | end | 722 | end |
| 715 | 723 | ||
| 716 | def copy_articles_from other | 724 | def copy_articles_from other |
| 725 | + return false if other.top_level_articles.empty? | ||
| 717 | other.top_level_articles.each do |a| | 726 | other.top_level_articles.each do |a| |
| 718 | copy_article_tree a | 727 | copy_article_tree a |
| 719 | end | 728 | end |
| 720 | self.articles.reload | 729 | self.articles.reload |
| 730 | + true | ||
| 721 | end | 731 | end |
| 722 | 732 | ||
| 723 | def copy_article_tree(article, parent=nil) | 733 | def copy_article_tree(article, parent=nil) |
| @@ -762,6 +772,29 @@ private :generate_url, :url_options | @@ -762,6 +772,29 @@ private :generate_url, :url_options | ||
| 762 | end | 772 | end |
| 763 | end | 773 | end |
| 764 | 774 | ||
| 775 | + # Adds many people to profile by id's | ||
| 776 | + def add_members_by_id(people_ids) | ||
| 777 | + | ||
| 778 | + unless people_ids.nil? && people_ids.empty? | ||
| 779 | + | ||
| 780 | + people = Person.where(id: people_ids) | ||
| 781 | + people.each do |person| | ||
| 782 | + | ||
| 783 | + add_member(person) unless person.is_member_of?(self) | ||
| 784 | + end | ||
| 785 | + end | ||
| 786 | + end | ||
| 787 | + | ||
| 788 | + # Adds many people to profile by email's | ||
| 789 | + def add_members_by_email(people_emails) | ||
| 790 | + | ||
| 791 | + people = User.where(email: people_emails) | ||
| 792 | + people.each do |user| | ||
| 793 | + | ||
| 794 | + add_member(user.person) unless user.person.is_member_of?(self) | ||
| 795 | + end | ||
| 796 | + end | ||
| 797 | + | ||
| 765 | def remove_member(person) | 798 | def remove_member(person) |
| 766 | self.disaffiliate(person, Profile::Roles.all_roles(environment.id)) | 799 | self.disaffiliate(person, Profile::Roles.all_roles(environment.id)) |
| 767 | end | 800 | end |
| @@ -893,11 +926,11 @@ private :generate_url, :url_options | @@ -893,11 +926,11 @@ private :generate_url, :url_options | ||
| 893 | self.forums.count.nonzero? | 926 | self.forums.count.nonzero? |
| 894 | end | 927 | end |
| 895 | 928 | ||
| 896 | - def admins | 929 | + def admins(with_entities = nil) |
| 897 | return [] if environment.blank? | 930 | return [] if environment.blank? |
| 898 | admin_role = Profile::Roles.admin(environment.id) | 931 | admin_role = Profile::Roles.admin(environment.id) |
| 899 | return [] if admin_role.blank? | 932 | return [] if admin_role.blank? |
| 900 | - self.members_by_role(admin_role) | 933 | + self.members_by_role(admin_role, with_entities) |
| 901 | end | 934 | end |
| 902 | 935 | ||
| 903 | def enable_contact? | 936 | def enable_contact? |
app/models/task.rb
| @@ -42,6 +42,8 @@ class Task < ActiveRecord::Base | @@ -42,6 +42,8 @@ class Task < ActiveRecord::Base | ||
| 42 | 42 | ||
| 43 | attr_protected :status | 43 | attr_protected :status |
| 44 | 44 | ||
| 45 | + settings_items :email_template_id, :type => :integer | ||
| 46 | + | ||
| 45 | def initialize(*args) | 47 | def initialize(*args) |
| 46 | super | 48 | super |
| 47 | self.status = (args.first ? args.first[:status] : nil) || Task::Status::ACTIVE | 49 | self.status = (args.first ? args.first[:status] : nil) || Task::Status::ACTIVE |
| @@ -68,14 +70,28 @@ class Task < ActiveRecord::Base | @@ -68,14 +70,28 @@ class Task < ActiveRecord::Base | ||
| 68 | begin | 70 | begin |
| 69 | target_msg = task.target_notification_message | 71 | target_msg = task.target_notification_message |
| 70 | if target_msg && task.target && !task.target.notification_emails.empty? | 72 | if target_msg && task.target && !task.target.notification_emails.empty? |
| 71 | - TaskMailer.target_notification(task, target_msg).deliver | 73 | + if target_profile_accepts_notification?(task) |
| 74 | + TaskMailer.target_notification(task, target_msg).deliver | ||
| 75 | + end | ||
| 72 | end | 76 | end |
| 73 | - rescue NotImplementedError => ex | 77 | + rescue Exception => ex |
| 74 | Rails.logger.info ex.to_s | 78 | Rails.logger.info ex.to_s |
| 75 | end | 79 | end |
| 76 | end | 80 | end |
| 77 | end | 81 | end |
| 78 | 82 | ||
| 83 | + def target_profile_accepts_notification?(task) | ||
| 84 | + if target_is_profile?(task) | ||
| 85 | + return task.target.administrator_mail_notification | ||
| 86 | + else | ||
| 87 | + true | ||
| 88 | + end | ||
| 89 | + end | ||
| 90 | + | ||
| 91 | + def target_is_profile?(task) | ||
| 92 | + task.target.kind_of? Profile | ||
| 93 | + end | ||
| 94 | + | ||
| 79 | # this method finished the task. It calls #perform, which must be overriden | 95 | # this method finished the task. It calls #perform, which must be overriden |
| 80 | # by subclasses. At the end a message (as returned by #finish_message) is | 96 | # by subclasses. At the end a message (as returned by #finish_message) is |
| 81 | # sent to the requestor with #notify_requestor. | 97 | # sent to the requestor with #notify_requestor. |
| @@ -282,15 +298,56 @@ class Task < ActiveRecord::Base | @@ -282,15 +298,56 @@ class Task < ActiveRecord::Base | ||
| 282 | end | 298 | end |
| 283 | end | 299 | end |
| 284 | 300 | ||
| 301 | + def email_template | ||
| 302 | + @email_template ||= email_template_id.present? ? EmailTemplate.find_by_id(email_template_id) : nil | ||
| 303 | + end | ||
| 304 | + | ||
| 305 | + def to_liquid | ||
| 306 | + HashWithIndifferentAccess.new({ | ||
| 307 | + :requestor => requestor, | ||
| 308 | + :reject_explanation => reject_explanation, | ||
| 309 | + :code => code | ||
| 310 | + }) | ||
| 311 | + end | ||
| 312 | + | ||
| 285 | scope :pending, :conditions => { :status => Task::Status::ACTIVE } | 313 | scope :pending, :conditions => { :status => Task::Status::ACTIVE } |
| 286 | scope :hidden, :conditions => { :status => Task::Status::HIDDEN } | 314 | scope :hidden, :conditions => { :status => Task::Status::HIDDEN } |
| 287 | scope :finished, :conditions => { :status => Task::Status::FINISHED } | 315 | scope :finished, :conditions => { :status => Task::Status::FINISHED } |
| 288 | scope :canceled, :conditions => { :status => Task::Status::CANCELLED } | 316 | scope :canceled, :conditions => { :status => Task::Status::CANCELLED } |
| 289 | scope :closed, :conditions => { :status => [Task::Status::CANCELLED, Task::Status::FINISHED] } | 317 | scope :closed, :conditions => { :status => [Task::Status::CANCELLED, Task::Status::FINISHED] } |
| 290 | scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] } | 318 | scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] } |
| 291 | - scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } | 319 | + |
| 320 | + # # updated scope method to avoid sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) | ||
| 321 | + # def self.of type | ||
| 322 | + # if type | ||
| 323 | + # where "type LIKE ?", type | ||
| 324 | + # else | ||
| 325 | + # all | ||
| 326 | + # end | ||
| 327 | + # end | ||
| 328 | + # | ||
| 329 | + # # updated scope method to avoid sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) | ||
| 330 | + # def self.order_by attribute_name, sort_order | ||
| 331 | + # if Task.column_names.include? attribute_name | ||
| 332 | + # # TODO future versions of rails accepts a hash as param to order method | ||
| 333 | + # # which helps to prevent sql injection in an shorter way | ||
| 334 | + # sort_order_filtered = ("ASC".eql? "#{sort_order}".upcase) ? 'asc' : 'desc' | ||
| 335 | + # sort_expression = Task.column_names.collect {|column_name| "#{column_name} #{sort_order_filtered}" if column_name.eql? attribute_name} | ||
| 336 | + # order(sort_expression.join) unless sort_expression.join.empty? | ||
| 337 | + # end | ||
| 338 | + # end | ||
| 339 | + # | ||
| 340 | + # # updated scope method to avoid sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) | ||
| 341 | + # def self.like field, value | ||
| 342 | + # if value and Tasks.column_names.include? field | ||
| 343 | + # where("LOWER(?) LIKE ?", "#{field}", "%#{value.downcase}%") | ||
| 344 | + # end | ||
| 345 | + # end | ||
| 346 | + | ||
| 347 | + scope :of, lambda { |type| conditions = type ? "tasks.type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } | ||
| 292 | scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } | 348 | scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } |
| 293 | scope :like, lambda { |field, value| where("LOWER(#{field}) LIKE ?", "%#{value.downcase}%") if value} | 349 | scope :like, lambda { |field, value| where("LOWER(#{field}) LIKE ?", "%#{value.downcase}%") if value} |
| 350 | + | ||
| 294 | scope :pending_all, lambda { |profile, filter_type, filter_text| | 351 | scope :pending_all, lambda { |profile, filter_type, filter_text| |
| 295 | self.to(profile).without_spam.pending.of(filter_type).like('data', filter_text) | 352 | self.to(profile).without_spam.pending.of(filter_type).like('data', filter_text) |
| 296 | } | 353 | } |
| @@ -310,6 +367,10 @@ class Task < ActiveRecord::Base | @@ -310,6 +367,10 @@ class Task < ActiveRecord::Base | ||
| 310 | Task.to(profile).pending.select('distinct type').map { |t| [t.class.name, t.title] } | 367 | Task.to(profile).pending.select('distinct type').map { |t| [t.class.name, t.title] } |
| 311 | end | 368 | end |
| 312 | 369 | ||
| 370 | + def self.closed_types_for(profile) | ||
| 371 | + Task.to(profile).closed.select('distinct type').map { |t| [t.class.name, t.title] } | ||
| 372 | + end | ||
| 373 | + | ||
| 313 | def opened? | 374 | def opened? |
| 314 | status == Task::Status::ACTIVE || status == Task::Status::HIDDEN | 375 | status == Task::Status::ACTIVE || status == Task::Status::HIDDEN |
| 315 | end | 376 | end |
app/models/textile_article.rb
| @@ -24,6 +24,10 @@ class TextileArticle < TextArticle | @@ -24,6 +24,10 @@ class TextileArticle < TextArticle | ||
| 24 | true | 24 | true |
| 25 | end | 25 | end |
| 26 | 26 | ||
| 27 | + def can_display_media_panel? | ||
| 28 | + true | ||
| 29 | + end | ||
| 30 | + | ||
| 27 | protected | 31 | protected |
| 28 | 32 | ||
| 29 | def convert_to_html(textile) | 33 | def convert_to_html(textile) |
app/models/tiny_mce_article.rb
app/models/user.rb
| @@ -102,18 +102,20 @@ class User < ActiveRecord::Base | @@ -102,18 +102,20 @@ class User < ActiveRecord::Base | ||
| 102 | # Virtual attribute for the unencrypted password | 102 | # Virtual attribute for the unencrypted password |
| 103 | attr_accessor :password, :name | 103 | attr_accessor :password, :name |
| 104 | 104 | ||
| 105 | - validates_presence_of :login, :email | ||
| 106 | - validates_format_of :login, :with => Profile::IDENTIFIER_FORMAT, :if => (lambda {|user| !user.login.blank?}) | 105 | + validates_presence_of :login |
| 106 | + validates_presence_of :email | ||
| 107 | + validates_format_of :login, :message => _('incorrect format'), :with => Profile::IDENTIFIER_FORMAT, :if => (lambda {|user| !user.login.blank?}) | ||
| 107 | validates_presence_of :password, :if => :password_required? | 108 | validates_presence_of :password, :if => :password_required? |
| 108 | validates_presence_of :password_confirmation, :if => :password_required? | 109 | validates_presence_of :password_confirmation, :if => :password_required? |
| 109 | - validates_length_of :password, :within => 4..40, :if => :password_required? | 110 | + validates_length_of :password, :message => _('length must be within 4 to 40 characters'), :within => 4..40, :if => :password_required? |
| 110 | validates_confirmation_of :password, :if => :password_required? | 111 | validates_confirmation_of :password, :if => :password_required? |
| 111 | - validates_length_of :login, :within => 2..40, :if => (lambda {|user| !user.login.blank?}) | ||
| 112 | - validates_length_of :email, :within => 3..100, :if => (lambda {|user| !user.email.blank?}) | ||
| 113 | - validates_uniqueness_of :login, :email, :case_sensitive => false, :scope => :environment_id | 112 | + validates_length_of :login, :message => _('length must be within 2 to 40 characters'), :within => 2..40, :if => (lambda {|user| !user.login.blank?}) |
| 113 | + validates_length_of :email, :message => _('length must be within 3 to 100 characters'),:within => 3..100, :if => (lambda {|user| !user.email.blank?}) | ||
| 114 | + validates_uniqueness_of :login, :case_sensitive => false, :scope => :environment_id | ||
| 115 | + validates_uniqueness_of :email, :case_sensitive => false, :scope => :environment_id | ||
| 114 | before_save :encrypt_password | 116 | before_save :encrypt_password |
| 115 | before_save :normalize_email, if: proc{ |u| u.email.present? } | 117 | before_save :normalize_email, if: proc{ |u| u.email.present? } |
| 116 | - validates_format_of :email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda {|user| !user.email.blank?}) | 118 | + validates_format_of :email, :message => _('incorrect format'), :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda {|user| !user.email.blank?}) |
| 117 | 119 | ||
| 118 | validates_inclusion_of :terms_accepted, :in => [ '1' ], :if => lambda { |u| ! u.terms_of_use.blank? }, :message => N_('{fn} must be checked in order to signup.').fix_i18n | 120 | validates_inclusion_of :terms_accepted, :in => [ '1' ], :if => lambda { |u| ! u.terms_of_use.blank? }, :message => N_('{fn} must be checked in order to signup.').fix_i18n |
| 119 | 121 | ||
| @@ -392,7 +394,7 @@ class User < ActiveRecord::Base | @@ -392,7 +394,7 @@ class User < ActiveRecord::Base | ||
| 392 | 394 | ||
| 393 | def deliver_activation_code | 395 | def deliver_activation_code |
| 394 | return if person.is_template? | 396 | return if person.is_template? |
| 395 | - UserMailer.activation_code(self).deliver unless self.activation_code.blank? | 397 | + Delayed::Job.enqueue(UserMailer::Job.new(self, :activation_code)) unless self.activation_code.blank? |
| 396 | end | 398 | end |
| 397 | 399 | ||
| 398 | def delay_activation_check | 400 | def delay_activation_check |
app/views/admin_panel/index.html.erb
| @@ -12,6 +12,7 @@ | @@ -12,6 +12,7 @@ | ||
| 12 | <tr><td><%= link_to _('Licenses'), :controller =>'licenses' %></td></tr> | 12 | <tr><td><%= link_to _('Licenses'), :controller =>'licenses' %></td></tr> |
| 13 | <tr><td><%= link_to _('Trusted sites'), :controller =>'trusted_sites' %></td></tr> | 13 | <tr><td><%= link_to _('Trusted sites'), :controller =>'trusted_sites' %></td></tr> |
| 14 | <tr><td><%= link_to _('Blocks'), :controller => 'features', :action => 'manage_blocks' %></td></tr> | 14 | <tr><td><%= link_to _('Blocks'), :controller => 'features', :action => 'manage_blocks' %></td></tr> |
| 15 | + <tr><td><%= link_to _('Email templates'), :controller =>'environment_email_templates' %></td></tr> | ||
| 15 | </table> | 16 | </table> |
| 16 | 17 | ||
| 17 | <h2><%= _('Profiles') %></h2> | 18 | <h2><%= _('Profiles') %></h2> |
app/views/cms/_event.html.erb
| @@ -8,9 +8,9 @@ | @@ -8,9 +8,9 @@ | ||
| 8 | <%= render :partial => 'general_fields' %> | 8 | <%= render :partial => 'general_fields' %> |
| 9 | <%= render :partial => 'translatable' %> | 9 | <%= render :partial => 'translatable' %> |
| 10 | 10 | ||
| 11 | -<%= labelled_form_field(_('Start date'), pick_date(:article, :start_date)) %> | 11 | +<%= date_range_field('article[start_date]', 'article[end_date]', @article.start_date, @article.end_date, _('%Y-%m-%d %H:%M'), {:time => true}, {:id => 'article_start_date'} ) %> |
| 12 | 12 | ||
| 13 | -<%= labelled_form_field(_('End date'), pick_date(:article, :end_date)) %> | 13 | +<%= labelled_form_field(_('Presenter:'), text_field(:article, :presenter)) %> |
| 14 | 14 | ||
| 15 | <%= labelled_form_field(_('Event website:'), text_field(:article, :link)) %> | 15 | <%= labelled_form_field(_('Event website:'), text_field(:article, :link)) %> |
| 16 | 16 |
app/views/cms/edit.html.erb
| 1 | <%= error_messages_for 'article' %> | 1 | <%= error_messages_for 'article' %> |
| 2 | 2 | ||
| 3 | -<% show_media_panel = environment.enabled?('media_panel') && [TinyMceArticle, TextileArticle, Event, EnterpriseHomepage, ProposalsDiscussionPlugin::Topic, ProposalsDiscussionPlugin::Discussion].any?{|klass| @article.kind_of?(klass)} %> | ||
| 4 | - | ||
| 5 | -<div class='<%= (show_media_panel ? 'with_media_panel' : 'no_media_panel') %>'> | 3 | +<div class='<%= (@article.display_media_panel? ? 'with_media_panel' : 'no_media_panel') %>'> |
| 6 | <%= labelled_form_for 'article', :html => { :multipart => true, :class => @type } do |f| %> | 4 | <%= labelled_form_for 'article', :html => { :multipart => true, :class => @type } do |f| %> |
| 7 | 5 | ||
| 8 | <%= hidden_field_tag("type", @type) if @type %> | 6 | <%= hidden_field_tag("type", @type) if @type %> |
| @@ -68,7 +66,7 @@ | @@ -68,7 +66,7 @@ | ||
| 68 | <% end %> | 66 | <% end %> |
| 69 | </div> | 67 | </div> |
| 70 | 68 | ||
| 71 | -<% if show_media_panel %> | 69 | +<% if @article.display_media_panel? %> |
| 72 | <%= render :partial => 'text_editor_sidebar' %> | 70 | <%= render :partial => 'text_editor_sidebar' %> |
| 73 | <% end %> | 71 | <% end %> |
| 74 | 72 |
app/views/content_viewer/_publishing_info.html.erb
| 1 | <span class="publishing-info"> | 1 | <span class="publishing-info"> |
| 2 | <span class="date"> | 2 | <span class="date"> |
| 3 | - <%= show_date(@page.published_at) %> | 3 | + <%= show_time(@page.published_at) %> |
| 4 | </span> | 4 | </span> |
| 5 | <span class="author"> | 5 | <span class="author"> |
| 6 | <%= _(", by %s") % (@page.author ? link_to(@page.author_name, @page.author_url) : @page.author_name) %> | 6 | <%= _(", by %s") % (@page.author ? link_to(@page.author_name, @page.author_url) : @page.author_name) %> |
| @@ -0,0 +1,31 @@ | @@ -0,0 +1,31 @@ | ||
| 1 | +<%= form_for(@email_template, :url => {:action => @email_template.persisted? ? :update : :create, :id => @email_template.id}) do |f| %> | ||
| 2 | + | ||
| 3 | + <%= error_messages_for :email_template if @email_template.errors.any? %> | ||
| 4 | + | ||
| 5 | + <div class="template-fields"> | ||
| 6 | + <div class="header-fields"> | ||
| 7 | + <%= labelled_form_field(_('Template Name:'), f.text_field(:name)) %> | ||
| 8 | + <%= labelled_form_field(_('Template Type:'), f.select(:template_type, @email_template.available_types.map {|k,v| [v[:description], k]}, :include_blank => true)) %> | ||
| 9 | + <%= labelled_form_field(_('Subject:'), f.text_field(:subject)) %> | ||
| 10 | + </div> | ||
| 11 | + <div class="available-params"> | ||
| 12 | + <div class="reference"> | ||
| 13 | + <a target="_blank" href="https://github.com/Shopify/liquid/wiki/Liquid-for-Designers"><%= _('Template language reference') %></a> | ||
| 14 | + </div> | ||
| 15 | + <div class="label"> | ||
| 16 | + <%= _('The following parameters may be used in subject and body:') %> | ||
| 17 | + </div> | ||
| 18 | + <div class="values"> | ||
| 19 | + {{profile.name}}, {{profile.identifier}}, {{environment.name}} | ||
| 20 | + </div> | ||
| 21 | + </div> | ||
| 22 | + <%= render :file => 'shared/tiny_mce' %> | ||
| 23 | + <%= labelled_form_field(_('Body:'), f.text_area(:body, :class => 'mceEditor')) %> | ||
| 24 | + </div> | ||
| 25 | + | ||
| 26 | + <div class="actions"> | ||
| 27 | + <%= submit_button(:save, _('Save')) %> | ||
| 28 | + <%= button(:back, _('Back'), :action => :index) %> | ||
| 29 | + </div> | ||
| 30 | + | ||
| 31 | +<% end %> |
| @@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
| 1 | +<div class="email-templates"> | ||
| 2 | + <h1><%= _('Email Templates') %></h1> | ||
| 3 | + | ||
| 4 | + <table> | ||
| 5 | + <tr> | ||
| 6 | + <th><%= _('Name') %></th> | ||
| 7 | + <th><%= _('Type') %></th> | ||
| 8 | + <th><%= _('Actions') %></th> | ||
| 9 | + </tr> | ||
| 10 | + | ||
| 11 | + <% @email_templates.each do |email_template| %> | ||
| 12 | + <tr> | ||
| 13 | + <td><%= email_template.name %></td> | ||
| 14 | + <td><%= email_template.type_description %></td> | ||
| 15 | + <td> | ||
| 16 | + <%= button_without_text(:edit, _('Edit'), {:action => :edit, :id => email_template.id}) %> | ||
| 17 | + <%= button_without_text(:remove, _('Remove'), {:action => :destroy, :id => email_template.id}, method: :delete, data: { confirm: 'Are you sure?' }) %> | ||
| 18 | + </td> | ||
| 19 | + </tr> | ||
| 20 | + <% end %> | ||
| 21 | + </table> | ||
| 22 | + | ||
| 23 | + <br /> | ||
| 24 | + | ||
| 25 | + <%= button(:new, _('New template'), :action => :new) %> | ||
| 26 | + <%= button(:back, _('Back'), @back_to) %> | ||
| 27 | +</div> |
app/views/profile/send_mail.html.erb
| @@ -4,6 +4,12 @@ | @@ -4,6 +4,12 @@ | ||
| 4 | 4 | ||
| 5 | <%= error_messages_for :mailing %> | 5 | <%= error_messages_for :mailing %> |
| 6 | 6 | ||
| 7 | +<% if @email_templates.present? %> | ||
| 8 | + <div class="template-selection"> | ||
| 9 | + <%= labelled_form_field(_('Select a template:'), select_tag(:template, options_from_collection_for_select(@email_templates, :id, :name), :include_blank => true, 'data-url' => url_for(:controller => 'profile_email_templates', :action => 'show_parsed'))) %> | ||
| 10 | + </div> | ||
| 11 | +<% end %> | ||
| 12 | + | ||
| 7 | <%= form_for :mailing, :url => {:action => 'send_mail'}, :html => {:id => 'mailing-form'} do |f| %> | 13 | <%= form_for :mailing, :url => {:action => 'send_mail'}, :html => {:id => 'mailing-form'} do |f| %> |
| 8 | 14 | ||
| 9 | <%= labelled_form_field(_('Subject:'), f.text_field(:subject)) %> | 15 | <%= labelled_form_field(_('Subject:'), f.text_field(:subject)) %> |
app/views/profile_editor/_moderation.html.erb
| 1 | <h2><%= _('Moderation options') %></h2> | 1 | <h2><%= _('Moderation options') %></h2> |
| 2 | <% if profile.community? %> | 2 | <% if profile.community? %> |
| 3 | <div style='margin-bottom: 1em'> | 3 | <div style='margin-bottom: 1em'> |
| 4 | + <h4><%= _('Email Configuration:')%></h4> | ||
| 5 | + </div> | ||
| 6 | + <div style='margin-bottom: 0.5em'> | ||
| 7 | + <%= check_box(:profile_data, :administrator_mail_notification, :style => 'float: left') %> | ||
| 8 | + <div style='margin-left: 30px'> | ||
| 9 | + <%= _('Send administrator Email for every task (Default: yes)') %> | ||
| 10 | + </div> | ||
| 11 | + </div> | ||
| 12 | + | ||
| 13 | + <div style='margin-bottom: 1em'> | ||
| 4 | <h4><%= _('Invitation moderation:')%></h4> | 14 | <h4><%= _('Invitation moderation:')%></h4> |
| 5 | </div> | 15 | </div> |
| 6 | <div style='margin-bottom: 0.5em'> | 16 | <div style='margin-bottom: 0.5em'> |
app/views/profile_editor/index.html.erb
| @@ -72,6 +72,8 @@ | @@ -72,6 +72,8 @@ | ||
| 72 | 72 | ||
| 73 | <%= control_panel_button(_('Edit welcome page'), 'welcome-page', :action => 'welcome_page') if has_welcome_page %> | 73 | <%= control_panel_button(_('Edit welcome page'), 'welcome-page', :action => 'welcome_page') if has_welcome_page %> |
| 74 | 74 | ||
| 75 | + <%= control_panel_button(_('Email Templates'), 'email-templates', :controller => :profile_email_templates) if profile.organization? %> | ||
| 76 | + | ||
| 75 | <% @plugins.dispatch(:control_panel_buttons).each do |button| %> | 77 | <% @plugins.dispatch(:control_panel_buttons).each do |button| %> |
| 76 | <%= control_panel_button(button[:title], button[:icon], button[:url], button[:html_options]) %> | 78 | <%= control_panel_button(button[:title], button[:icon], button[:url], button[:html_options]) %> |
| 77 | <% end %> | 79 | <% end %> |
app/views/profile_members/_members_list.html.erb
| @@ -20,6 +20,7 @@ | @@ -20,6 +20,7 @@ | ||
| 20 | :loading => "jQuery('#members-list').addClass('loading')", | 20 | :loading => "jQuery('#members-list').addClass('loading')", |
| 21 | :success => "jQuery('#tr-#{m.identifier}').show()", | 21 | :success => "jQuery('#tr-#{m.identifier}').show()", |
| 22 | :complete => "jQuery('#members-list').removeClass('loading')", | 22 | :complete => "jQuery('#members-list').removeClass('loading')", |
| 23 | + :confirm => _('Are you sure you want to remove this user?'), | ||
| 23 | :url => { :id => m }.merge(remove_action)) if m != user %> | 24 | :url => { :id => m }.merge(remove_action)) if m != user %> |
| 24 | </div> | 25 | </div> |
| 25 | </td> | 26 | </td> |
app/views/shared/not_found.html.erb
| 1 | <div id='not-found'> | 1 | <div id='not-found'> |
| 2 | - <h1><%= _('Nonexistent content: %s') % (content_tag('tt', @path)) %></h1> | 2 | + <h1><%= _('There is no such page: %s') % (content_tag('tt', @path)) %></h1> |
| 3 | <p> | 3 | <p> |
| 4 | <%= _('.You may have clicked an expired link or mistyped the address.') %> | 4 | <%= _('.You may have clicked an expired link or mistyped the address.') %> |
| 5 | - <%= _('.This page does not exist. Would you like to: Create a new conteúdoou Locate existing content.') %> | 5 | + <%= _('.This page does not exist. Would you like to: Create a new content or locate existing content.') %> |
| 6 | <!-- <%= _('If you clicked a link that was in another site, or was given to you by someone else, it would be nice if you tell them that their link is not valid anymore.') %> --> | 6 | <!-- <%= _('If you clicked a link that was in another site, or was given to you by someone else, it would be nice if you tell them that their link is not valid anymore.') %> --> |
| 7 | </p> | 7 | </p> |
| 8 | 8 | ||
| @@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
| 11 | <%= button :home, _('Go to the home page'), '/' %> | 11 | <%= button :home, _('Go to the home page'), '/' %> |
| 12 | <% if logged_in? %> | 12 | <% if logged_in? %> |
| 13 | <% parent_id = ((@article && @article.allow_children?) ? @article : nil) %> | 13 | <% parent_id = ((@article && @article.allow_children?) ? @article : nil) %> |
| 14 | - <%= modal_button('new', _('New content'), :controller => 'cms', :action => 'new', :parent_id => parent_id, :cms => true) %> | 14 | + <%= modal_button('new', _('New content'), myprofile_path(:profile => current_person.identifier, :controller => 'cms', :action => 'new', :parent_id => parent_id)) %> |
| 15 | <% end %> | 15 | <% end %> |
| 16 | <% end %> | 16 | <% end %> |
| 17 | <form action="/search"> | 17 | <form action="/search"> |
app/views/tasks/_approve_article_accept_details.html.erb
| 1 | +<%= task_email_template(_('Select an acceptance email template:'), @acceptance_email_templates, task) %> | ||
| 2 | + | ||
| 1 | <%= render :file => 'shared/tiny_mce' %> | 3 | <%= render :file => 'shared/tiny_mce' %> |
| 2 | 4 | ||
| 3 | <%= labelled_form_field(_('Create a link'), f.check_box(:create_link)) %> | 5 | <%= labelled_form_field(_('Create a link'), f.check_box(:create_link)) %> |
| @@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
| 1 | +<div class="title"> | ||
| 2 | + <%= task_information(task) %> | ||
| 3 | +</div> | ||
| 4 | +<div class="status"> | ||
| 5 | + <%= _(Task::Status.names[task.status]) %> | ||
| 6 | +</div> | ||
| 7 | +<div class="dates"> | ||
| 8 | + <span class="created"> | ||
| 9 | + <span class="label"><%= _('Created:') %></span> | ||
| 10 | + <span class="value"><%= show_date(task.created_at) %></span> | ||
| 11 | + </span> | ||
| 12 | + — | ||
| 13 | + <span class="processed"> | ||
| 14 | + <span class="label"><%= _('Processed:') %></span> | ||
| 15 | + <span class="value"><%= show_date(task.end_date) %></span> | ||
| 16 | + </span> | ||
| 17 | +</div> | ||
| 18 | +<% if task.closed_by.present? %> | ||
| 19 | + <div class="closed-by"> | ||
| 20 | + <span class="label"><%= _('Closed by:') %></span> | ||
| 21 | + <span class="value"><%= link_to(task.closed_by.name, task.closed_by.url) %></span> | ||
| 22 | + </div> | ||
| 23 | +<% end %> |
app/views/tasks/_task_reject_details.html.erb
| 1 | +<%= task_email_template(_('Select a rejection email template:'), @rejection_email_templates, task) %> | ||
| 2 | + | ||
| 1 | <%= labelled_form_field(_('Rejection explanation'), f.text_area(:reject_explanation, :rows => 5)) %> | 3 | <%= labelled_form_field(_('Rejection explanation'), f.text_area(:reject_explanation, :rows => 5)) %> |
app/views/tasks/index.html.erb
| @@ -50,7 +50,7 @@ | @@ -50,7 +50,7 @@ | ||
| 50 | <em><%= _('No pending tasks for %s') % profile.name %></em> | 50 | <em><%= _('No pending tasks for %s') % profile.name %></em> |
| 51 | </p> | 51 | </p> |
| 52 | <% else %> | 52 | <% else %> |
| 53 | - <%= form_tag :action => 'close' do%> | 53 | + <%= form_tag task_action('close') do%> |
| 54 | <% button_bar(:class => 'task-actions') do %> | 54 | <% button_bar(:class => 'task-actions') do %> |
| 55 | <%# FiXME button(:edit, _('View my requests'), :action => 'list_requested') %> | 55 | <%# FiXME button(:edit, _('View my requests'), :action => 'list_requested') %> |
| 56 | <%# FIXME button('menu-mail', _('Send request'), :action => 'new') %> | 56 | <%# FIXME button('menu-mail', _('Send request'), :action => 'new') %> |
| @@ -67,7 +67,9 @@ | @@ -67,7 +67,9 @@ | ||
| 67 | <% end %> | 67 | <% end %> |
| 68 | 68 | ||
| 69 | <div class="task_boxes"> | 69 | <div class="task_boxes"> |
| 70 | - <%= render :partial => 'task', :collection => @tasks %> | 70 | + <% @tasks.each do |task| %> |
| 71 | + <%= render :partial => partial_for_class(task.class, nil, nil), :locals => {:task => task} %> | ||
| 72 | + <% end %> | ||
| 71 | </div> | 73 | </div> |
| 72 | 74 | ||
| 73 | <% unless @view_only %> | 75 | <% unless @view_only %> |
app/views/tasks/processed.html.erb
| 1 | +<%= stylesheet_link_tag 'tasks' %> | ||
| 2 | + | ||
| 3 | +<div class="task-processed"> | ||
| 1 | <h1><%= _("%s's processed tasks") % profile.name %></h1> | 4 | <h1><%= _("%s's processed tasks") % profile.name %></h1> |
| 2 | 5 | ||
| 6 | +<div class="task-processed-filter"> | ||
| 7 | +<% | ||
| 8 | + type_collection = [[nil, _('All')]] + @task_types | ||
| 9 | +%> | ||
| 10 | + <%= form_tag '#', :method => 'get' do %> | ||
| 11 | + <%= field_set_tag _('Filter'), :class => 'filter_fields' do %> | ||
| 12 | + <div> | ||
| 13 | + <%= labelled_select(_('Type of task')+': ', 'filter[type]', :first, :last, @filter[:type], type_collection, {:id => 'filter-type'}) %> | ||
| 14 | + <%= labelled_select(_('Status:'), 'filter[status]', :last, :first, @filter[:status], [[_('Any'), nil], [_(Task::Status.names[Task::Status::CANCELLED]), 2], [_(Task::Status.names[Task::Status::FINISHED]), 3] ]) %> | ||
| 15 | + </div> | ||
| 16 | + | ||
| 17 | + <div> | ||
| 18 | + <%= labelled_text_field(_('Text Filter:'), 'filter[text]', @filter[:text]) %> | ||
| 19 | + </div> | ||
| 20 | + | ||
| 21 | + <div> | ||
| 22 | + <%= labelled_text_field(_('Requestor:'), 'filter[requestor]', @filter[:requestor]) %> | ||
| 23 | + <%= labelled_text_field(_('Closed by:'), 'filter[closed_by]', @filter[:closed_by]) %> | ||
| 24 | + </div> | ||
| 25 | + | ||
| 26 | + <%= labelled_form_field(_('Creation date'), date_range_field('filter[created_from]', 'filter[created_until]', @filter[:created_from], @filter[:created_until], '%Y-%m-%d', { :change_month => true, :change_year => true, :date_format => 'yy-mm-dd' }, { :size => 14, :from_id => 'filter_created_from', :to_id => 'filter_created_until' })) %> | ||
| 27 | + <%= labelled_form_field(_('Processed date'), date_range_field('filter[closed_from]', 'filter[closed_until]', @filter[:closed_from], @filter[:closed_until], '%Y-%m-%d', { :change_month => true, :change_year => true, :date_format => 'yy-mm-dd' }, { :size => 14, :from_id => 'filter_closed_from', :to_id => 'filter_closed_until' })) %> | ||
| 28 | + | ||
| 29 | + <div class="actions"> | ||
| 30 | + <%= submit_button(:search, _('Search')) %> | ||
| 31 | + </div> | ||
| 32 | + <% end %> | ||
| 33 | + <% end %> | ||
| 34 | +</div> | ||
| 35 | + | ||
| 3 | <p> | 36 | <p> |
| 4 | <% if @tasks.empty? %> | 37 | <% if @tasks.empty? %> |
| 5 | <em><%= _('No processed tasks.') %></em> | 38 | <em><%= _('No processed tasks.') %></em> |
| 6 | <% else %> | 39 | <% else %> |
| 7 | - <ul> | 40 | + <ul class="task-list"> |
| 8 | <% @tasks.each do |item| %> | 41 | <% @tasks.each do |item| %> |
| 9 | - <li> | ||
| 10 | - <strong><%= task_information(item) %></strong> <br/> | ||
| 11 | - <small> | ||
| 12 | - <%= _('Created:') +' '+ show_date(item.created_at) %> | ||
| 13 | - — | ||
| 14 | - <%= _('Processed:') +' '+ show_date(item.end_date) %> | ||
| 15 | - </small> | 42 | + <li class="task status-<%= item.status%>"> |
| 43 | + <%= render :partial => partial_for_class(item.class, nil, 'processed'), :locals => {:task => item} %> | ||
| 16 | </li> | 44 | </li> |
| 17 | <% end %> | 45 | <% end %> |
| 18 | </ul> | 46 | </ul> |
| 47 | + <%= pagination_links(@tasks)%> | ||
| 19 | <% end %> | 48 | <% end %> |
| 20 | </p> | 49 | </p> |
| 21 | 50 | ||
| 22 | <% button_bar do %> | 51 | <% button_bar do %> |
| 23 | <%= button(:back, _('Back'), :action => 'index') %> | 52 | <%= button(:back, _('Back'), :action => 'index') %> |
| 24 | <% end %> | 53 | <% end %> |
| 54 | + | ||
| 55 | +</div> |
config/application.rb
| @@ -62,7 +62,7 @@ module Noosfero | @@ -62,7 +62,7 @@ module Noosfero | ||
| 62 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. | 62 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. |
| 63 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. | 63 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. |
| 64 | # config.time_zone = 'Central Time (US & Canada)' | 64 | # config.time_zone = 'Central Time (US & Canada)' |
| 65 | - config.time_zone = 'Brasilia' | 65 | + #config.time_zone = 'Brasilia' |
| 66 | 66 | ||
| 67 | 67 | ||
| 68 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. | 68 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. |
config/initializers/eager_load.rb
| @@ -0,0 +1,14 @@ | @@ -0,0 +1,14 @@ | ||
| 1 | +class CreateEmailTemplate < ActiveRecord::Migration | ||
| 2 | + | ||
| 3 | + def change | ||
| 4 | + create_table :email_templates do |t| | ||
| 5 | + t.string :name | ||
| 6 | + t.string :template_type | ||
| 7 | + t.string :subject | ||
| 8 | + t.text :body | ||
| 9 | + t.references :owner, :polymorphic => true | ||
| 10 | + t.timestamps | ||
| 11 | + end | ||
| 12 | + end | ||
| 13 | + | ||
| 14 | +end |
db/migrate/20150617222204_event_period_with_support_to_datetime.rb
0 → 100644
| @@ -0,0 +1,37 @@ | @@ -0,0 +1,37 @@ | ||
| 1 | +class EventPeriodWithSupportToDatetime < ActiveRecord::Migration | ||
| 2 | + def up | ||
| 3 | + change_table :articles do |t| | ||
| 4 | + t.change :start_date, :datetime | ||
| 5 | + end | ||
| 6 | + | ||
| 7 | + change_table :articles do |t| | ||
| 8 | + t.change :end_date, :datetime | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + change_table :article_versions do |t| | ||
| 12 | + t.change :start_date, :datetime | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | + change_table :article_versions do |t| | ||
| 16 | + t.change :end_date, :datetime | ||
| 17 | + end | ||
| 18 | + end | ||
| 19 | + | ||
| 20 | + def down | ||
| 21 | + change_table :articles do |t| | ||
| 22 | + t.change :start_date, :date | ||
| 23 | + end | ||
| 24 | + | ||
| 25 | + change_table :articles do |t| | ||
| 26 | + t.change :end_date, :date | ||
| 27 | + end | ||
| 28 | + | ||
| 29 | + change_table :article_versions do |t| | ||
| 30 | + t.change :start_date, :date | ||
| 31 | + end | ||
| 32 | + | ||
| 33 | + change_table :article_versions do |t| | ||
| 34 | + t.change :end_date, :date | ||
| 35 | + end | ||
| 36 | + end | ||
| 37 | +end | ||
| 0 | \ No newline at end of file | 38 | \ No newline at end of file |
db/migrate/20150712194411_change_task_end_date_from_date_to_date_time.rb
0 → 100644
db/migrate/20150722042714_change_article_date_to_datetime.rb
0 → 100644
| @@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
| 1 | +class ChangeArticleDateToDatetime < ActiveRecord::Migration | ||
| 2 | + | ||
| 3 | + def up | ||
| 4 | + change_table :articles do |t| | ||
| 5 | + t.change :start_date, :datetime | ||
| 6 | + t.change :end_date, :datetime | ||
| 7 | + end | ||
| 8 | + | ||
| 9 | + change_table :article_versions do |t| | ||
| 10 | + t.change :start_date, :datetime | ||
| 11 | + t.change :end_date, :datetime | ||
| 12 | + end | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | + def down | ||
| 16 | + change_table :articles do |t| | ||
| 17 | + t.change :start_date, :date | ||
| 18 | + t.change :end_date, :date | ||
| 19 | + end | ||
| 20 | + | ||
| 21 | + change_table :article_versions do |t| | ||
| 22 | + t.change :start_date, :date | ||
| 23 | + t.change :end_date, :date | ||
| 24 | + end | ||
| 25 | + end | ||
| 26 | + | ||
| 27 | +end |
features/comment.feature
| @@ -37,7 +37,7 @@ Feature: comment | @@ -37,7 +37,7 @@ Feature: comment | ||
| 37 | And I fill in "Title" with "Hey ho, let's go!" | 37 | And I fill in "Title" with "Hey ho, let's go!" |
| 38 | And I fill in "Enter your comment" with "Hey ho, let's go!" | 38 | And I fill in "Enter your comment" with "Hey ho, let's go!" |
| 39 | When I press "Post comment" | 39 | When I press "Post comment" |
| 40 | - Then I should see "Hey ho, let's go" | 40 | + Then I should see "Hey ho, let" |
| 41 | 41 | ||
| 42 | @selenium-fixme | 42 | @selenium-fixme |
| 43 | Scenario: redirect to right place after comment a picture | 43 | Scenario: redirect to right place after comment a picture |
lib/activities_counter_cache_job.rb
| 1 | class ActivitiesCounterCacheJob | 1 | class ActivitiesCounterCacheJob |
| 2 | + | ||
| 3 | + # Changed to prevent sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) | ||
| 2 | def perform | 4 | def perform |
| 3 | - person_activities_counts = ActiveRecord::Base.connection.execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.user_id WHERE (action_tracker.created_at >= '#{ActionTracker::Record::RECENT_DELAY.days.ago.to_s(:db)}') AND ( (profiles.type = 'Person' ) ) GROUP BY profiles.id;") | ||
| 4 | - organization_activities_counts = ActiveRecord::Base.connection.execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.target_id WHERE (action_tracker.created_at >= '#{ActionTracker::Record::RECENT_DELAY.days.ago.to_s(:db)}') AND ( (profiles.type = 'Community' OR profiles.type = 'Enterprise' OR profiles.type = 'Organization' ) ) GROUP BY profiles.id;") | 5 | + person_activities_counts = ActiveRecord::Base.connection.execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.user_id WHERE (action_tracker.created_at >= #{ActiveRecord::Base.connection.quote(ActionTracker::Record::RECENT_DELAY.days.ago.to_s(:db))}) AND ( (profiles.type = 'Person' ) ) GROUP BY profiles.id;") |
| 6 | + organization_activities_counts = ActiveRecord::Base.connection.execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.target_id WHERE (action_tracker.created_at >= #{ActiveRecord::Base.connection.quote(ActionTracker::Record::RECENT_DELAY.days.ago.to_s(:db))}) AND ( (profiles.type = 'Community' OR profiles.type = 'Enterprise' OR profiles.type = 'Organization' ) ) GROUP BY profiles.id;") | ||
| 5 | activities_counts = person_activities_counts.entries + organization_activities_counts.entries | 7 | activities_counts = person_activities_counts.entries + organization_activities_counts.entries |
| 6 | activities_counts.each do |count| | 8 | activities_counts.each do |count| |
| 7 | - ActiveRecord::Base.connection.execute("UPDATE profiles SET activities_count=#{count['count'].to_i} WHERE profiles.id=#{count['id']};") | 9 | + update_sql = ActiveRecord::Base.__send__(:sanitize_sql, ["UPDATE profiles SET activities_count=? WHERE profiles.id=?;", count['count'].to_i, count['id'] ], '') |
| 10 | + ActiveRecord::Base.connection.execute(update_sql) | ||
| 8 | end | 11 | end |
| 9 | Delayed::Job.enqueue(ActivitiesCounterCacheJob.new, {:priority => -3, :run_at => 1.day.from_now}) | 12 | Delayed::Job.enqueue(ActivitiesCounterCacheJob.new, {:priority => -3, :run_at => 1.day.from_now}) |
| 10 | end | 13 | end |
| 14 | + | ||
| 11 | end | 15 | end |
lib/add_members_job.rb
| @@ -4,7 +4,12 @@ class AddMembersJob < Struct.new(:people_ids, :profile_id, :locale) | @@ -4,7 +4,12 @@ class AddMembersJob < Struct.new(:people_ids, :profile_id, :locale) | ||
| 4 | Noosfero.with_locale(locale) do | 4 | Noosfero.with_locale(locale) do |
| 5 | 5 | ||
| 6 | profile = Profile.find(profile_id) | 6 | profile = Profile.find(profile_id) |
| 7 | - profile.add_members people_ids | 7 | + |
| 8 | + if people_ids.first =~ /\@/ | ||
| 9 | + profile.add_members_by_email people_ids | ||
| 10 | + else | ||
| 11 | + profile.add_members_by_id people_ids | ||
| 12 | + end | ||
| 8 | 13 | ||
| 9 | end | 14 | end |
| 10 | end | 15 | end |
lib/noosfero/api/api.rb
| @@ -12,6 +12,8 @@ module Noosfero | @@ -12,6 +12,8 @@ module Noosfero | ||
| 12 | use GrapeLogging::Middleware::RequestLogger, { logger: logger } | 12 | use GrapeLogging::Middleware::RequestLogger, { logger: logger } |
| 13 | 13 | ||
| 14 | rescue_from :all do |e| | 14 | rescue_from :all do |e| |
| 15 | + puts e.inspect | ||
| 16 | + puts e.backtrace.inspect | ||
| 15 | logger.error e | 17 | logger.error e |
| 16 | end | 18 | end |
| 17 | 19 |
lib/noosfero/api/helpers.rb
| @@ -160,8 +160,27 @@ require 'grape' | @@ -160,8 +160,27 @@ require 'grape' | ||
| 160 | conditions | 160 | conditions |
| 161 | end | 161 | end |
| 162 | 162 | ||
| 163 | - def make_order_with_parameters(params) | ||
| 164 | - params[:order] || "created_at DESC" | 163 | + # changing make_order_with_parameters to avoid sql injection |
| 164 | + def make_order_with_parameters(object, method, params) | ||
| 165 | + order = "created_at DESC" | ||
| 166 | + unless params[:order].blank? | ||
| 167 | + if params[:order].include? '\'' or params[:order].include? '"' | ||
| 168 | + order = "created_at DESC" | ||
| 169 | + elsif ['RANDOM()', 'RANDOM'].include? params[:order].upcase | ||
| 170 | + order = 'RANDOM()' | ||
| 171 | + else | ||
| 172 | + field_name, direction = params[:order].split(' ') | ||
| 173 | + assoc = object.class.reflect_on_association(method.to_sym) | ||
| 174 | + if !field_name.blank? and assoc | ||
| 175 | + if assoc.klass.attribute_names.include? field_name | ||
| 176 | + if direction.present? and ['ASC','DESC'].include? direction.upcase | ||
| 177 | + order = "#{field_name} #{direction.upcase}" | ||
| 178 | + end | ||
| 179 | + end | ||
| 180 | + end | ||
| 181 | + end | ||
| 182 | + end | ||
| 183 | + return order | ||
| 165 | end | 184 | end |
| 166 | 185 | ||
| 167 | def by_reference(scope, params) | 186 | def by_reference(scope, params) |
| @@ -176,7 +195,7 @@ require 'grape' | @@ -176,7 +195,7 @@ require 'grape' | ||
| 176 | 195 | ||
| 177 | def select_filtered_collection_of(object, method, params) | 196 | def select_filtered_collection_of(object, method, params) |
| 178 | conditions = make_conditions_with_parameter(params) | 197 | conditions = make_conditions_with_parameter(params) |
| 179 | - order = make_order_with_parameters(params) | 198 | + order = make_order_with_parameters(object,method,params) |
| 180 | 199 | ||
| 181 | objects = object.send(method) | 200 | objects = object.send(method) |
| 182 | objects = by_reference(objects, params) | 201 | objects = by_reference(objects, params) |
lib/noosfero/api/session.rb
| @@ -32,11 +32,10 @@ module Noosfero | @@ -32,11 +32,10 @@ module Noosfero | ||
| 32 | params do | 32 | params do |
| 33 | requires :email, type: String, desc: _("Email") | 33 | requires :email, type: String, desc: _("Email") |
| 34 | requires :login, type: String, desc: _("Login") | 34 | requires :login, type: String, desc: _("Login") |
| 35 | - requires :password, type: String, desc: _("Password") | ||
| 36 | - requires :password_confirmation, type: String, desc: _("Password confirmation") | 35 | + #requires :password, type: String, desc: _("Password") |
| 36 | + #requires :password_confirmation, type: String, desc: _("Password confirmation") | ||
| 37 | end | 37 | end |
| 38 | post "/register" do | 38 | post "/register" do |
| 39 | - unique_attributes! User, [:email, :login] | ||
| 40 | attrs = attributes_for_keys [:email, :login, :password, :password_confirmation] + environment.signup_person_fields | 39 | attrs = attributes_for_keys [:email, :login, :password, :password_confirmation] + environment.signup_person_fields |
| 41 | remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR']) | 40 | remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR']) |
| 42 | # test_captcha will render_api_error! and exit in case of any problem | 41 | # test_captcha will render_api_error! and exit in case of any problem |
lib/noosfero/plugin.rb
| @@ -138,11 +138,12 @@ class Noosfero::Plugin | @@ -138,11 +138,12 @@ class Noosfero::Plugin | ||
| 138 | filters = [filters] | 138 | filters = [filters] |
| 139 | end | 139 | end |
| 140 | filters.each do |plugin_filter| | 140 | filters.each do |plugin_filter| |
| 141 | + plugin_filter[:options] ||= {} | ||
| 142 | + plugin_filter[:options][:if] = -> { environment.plugin_enabled? plugin.module_name } | ||
| 143 | + | ||
| 141 | filter_method = "#{plugin.identifier}_#{plugin_filter[:method_name]}".to_sym | 144 | filter_method = "#{plugin.identifier}_#{plugin_filter[:method_name]}".to_sym |
| 142 | - controller_class.send(plugin_filter[:type], filter_method, (plugin_filter[:options] || {})) | ||
| 143 | - controller_class.send(:define_method, filter_method) do | ||
| 144 | - instance_exec(&plugin_filter[:block]) if environment.plugin_enabled?(plugin) | ||
| 145 | - end | 145 | + controller_class.send plugin_filter[:type], filter_method, plugin_filter[:options] |
| 146 | + controller_class.send :define_method, filter_method, &plugin_filter[:block] | ||
| 146 | end | 147 | end |
| 147 | end | 148 | end |
| 148 | 149 |
lib/noosfero/plugin/parent_methods.rb
| @@ -11,6 +11,10 @@ class Noosfero::Plugin | @@ -11,6 +11,10 @@ class Noosfero::Plugin | ||
| 11 | @identifier ||= (if self.parents.first.instance_of? Module then self.parents.first else self end).name.underscore | 11 | @identifier ||= (if self.parents.first.instance_of? Module then self.parents.first else self end).name.underscore |
| 12 | end | 12 | end |
| 13 | 13 | ||
| 14 | + def module_name | ||
| 15 | + @name ||= (if self.parents.first != Object then self.parents.first else self end).to_s | ||
| 16 | + end | ||
| 17 | + | ||
| 14 | def public_name | 18 | def public_name |
| 15 | @public_name ||= self.identifier.gsub '_plugin', '' | 19 | @public_name ||= self.identifier.gsub '_plugin', '' |
| 16 | end | 20 | end |
| @@ -0,0 +1,97 @@ | @@ -0,0 +1,97 @@ | ||
| 1 | +# based on https://github.com/discourse/discourse/blob/master/lib/scheduler/defer.rb | ||
| 2 | + | ||
| 3 | +module Noosfero | ||
| 4 | + module Scheduler | ||
| 5 | + module Deferrable | ||
| 6 | + def initialize | ||
| 7 | + # FIXME: do some other way when not using Unicorn | ||
| 8 | + @async = (not Rails.env.test?) and defined? Unicorn | ||
| 9 | + @queue = Queue.new | ||
| 10 | + @mutex = Mutex.new | ||
| 11 | + @paused = false | ||
| 12 | + @thread = nil | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | + def pause | ||
| 16 | + stop! | ||
| 17 | + @paused = true | ||
| 18 | + end | ||
| 19 | + | ||
| 20 | + def resume | ||
| 21 | + @paused = false | ||
| 22 | + end | ||
| 23 | + | ||
| 24 | + # for test | ||
| 25 | + def async= val | ||
| 26 | + @async = val | ||
| 27 | + end | ||
| 28 | + | ||
| 29 | + def later desc = nil, &blk | ||
| 30 | + if @async | ||
| 31 | + start_thread unless (@thread && @thread.alive?) || @paused | ||
| 32 | + @queue << [blk, desc] | ||
| 33 | + else | ||
| 34 | + blk.call | ||
| 35 | + end | ||
| 36 | + end | ||
| 37 | + | ||
| 38 | + def stop! | ||
| 39 | + @thread.kill if @thread and @thread.alive? | ||
| 40 | + @thread = nil | ||
| 41 | + end | ||
| 42 | + | ||
| 43 | + # test only | ||
| 44 | + def stopped? | ||
| 45 | + !(@thread and @thread.alive?) | ||
| 46 | + end | ||
| 47 | + | ||
| 48 | + def do_all_work | ||
| 49 | + while !@queue.empty? | ||
| 50 | + do_work _non_block=true | ||
| 51 | + end | ||
| 52 | + end | ||
| 53 | + | ||
| 54 | + private | ||
| 55 | + | ||
| 56 | + def start_thread | ||
| 57 | + @mutex.synchronize do | ||
| 58 | + return if @thread && @thread.alive? | ||
| 59 | + @thread = Thread.new do | ||
| 60 | + while true | ||
| 61 | + do_work | ||
| 62 | + end | ||
| 63 | + end | ||
| 64 | + @thread.priority = -2 | ||
| 65 | + end | ||
| 66 | + end | ||
| 67 | + | ||
| 68 | + # using non_block to match Ruby #deq | ||
| 69 | + def do_work non_block=false | ||
| 70 | + job, desc = @queue.deq non_block | ||
| 71 | + begin | ||
| 72 | + job.call | ||
| 73 | + rescue => ex | ||
| 74 | + ExceptionNotifier.notify_exception ex, message: "Running deferred code '#{desc}'" | ||
| 75 | + end | ||
| 76 | + rescue => ex | ||
| 77 | + ExceptionNotifier.notify_exception ex, message: "Processing deferred code queue" | ||
| 78 | + end | ||
| 79 | + end | ||
| 80 | + | ||
| 81 | + class Defer | ||
| 82 | + | ||
| 83 | + module Unicorn | ||
| 84 | + def process_client client | ||
| 85 | + ::Noosfero::Scheduler::Defer.pause | ||
| 86 | + super client | ||
| 87 | + ::Noosfero::Scheduler::Defer.do_all_work | ||
| 88 | + ::Noosfero::Scheduler::Defer.resume | ||
| 89 | + end | ||
| 90 | + end | ||
| 91 | + | ||
| 92 | + extend Deferrable | ||
| 93 | + initialize | ||
| 94 | + end | ||
| 95 | + | ||
| 96 | + end | ||
| 97 | +end |
plugins/analytics/controllers/profile/analytics_plugin/time_on_page_controller.rb
0 → 100644
| @@ -0,0 +1,30 @@ | @@ -0,0 +1,30 @@ | ||
| 1 | +class AnalyticsPlugin::TimeOnPageController < ProfileController | ||
| 2 | + | ||
| 3 | + before_filter :skip_page_view | ||
| 4 | + | ||
| 5 | + def page_load | ||
| 6 | + # to avoid concurrency problems with the original deferred request, also defer this | ||
| 7 | + Noosfero::Scheduler::Defer.later do | ||
| 8 | + page_view = profile.page_views.where(request_id: params[:id]).first | ||
| 9 | + page_view.request = request | ||
| 10 | + page_view.page_load! | ||
| 11 | + end | ||
| 12 | + | ||
| 13 | + render nothing: true | ||
| 14 | + end | ||
| 15 | + | ||
| 16 | + def report | ||
| 17 | + page_view = profile.page_views.where(request_id: params[:id]).first | ||
| 18 | + page_view.request = request | ||
| 19 | + page_view.increase_time_on_page! | ||
| 20 | + | ||
| 21 | + render nothing: true | ||
| 22 | + end | ||
| 23 | + | ||
| 24 | + protected | ||
| 25 | + | ||
| 26 | + def skip_page_view | ||
| 27 | + @analytics_skip_page_view = true | ||
| 28 | + end | ||
| 29 | + | ||
| 30 | +end |
plugins/analytics/db/migrate/20150715001149_init_analytics_plugin.rb
0 → 100644
| @@ -0,0 +1,47 @@ | @@ -0,0 +1,47 @@ | ||
| 1 | +class InitAnalyticsPlugin < ActiveRecord::Migration | ||
| 2 | + | ||
| 3 | + def up | ||
| 4 | + create_table :analytics_plugin_visits do |t| | ||
| 5 | + t.integer :profile_id | ||
| 6 | + end | ||
| 7 | + | ||
| 8 | + create_table :analytics_plugin_page_views do |t| | ||
| 9 | + t.string :type | ||
| 10 | + t.integer :visit_id | ||
| 11 | + t.integer :track_id | ||
| 12 | + t.integer :referer_page_view_id | ||
| 13 | + t.string :request_id | ||
| 14 | + | ||
| 15 | + t.integer :user_id | ||
| 16 | + t.integer :session_id | ||
| 17 | + t.integer :profile_id | ||
| 18 | + | ||
| 19 | + t.text :url | ||
| 20 | + t.text :referer_url | ||
| 21 | + | ||
| 22 | + t.text :user_agent | ||
| 23 | + t.string :remote_ip | ||
| 24 | + | ||
| 25 | + t.datetime :request_started_at | ||
| 26 | + t.datetime :request_finished_at | ||
| 27 | + t.datetime :page_loaded_at | ||
| 28 | + t.integer :time_on_page, default: 0 | ||
| 29 | + | ||
| 30 | + t.text :data, default: {}.to_yaml | ||
| 31 | + end | ||
| 32 | + add_index :analytics_plugin_page_views, :request_id | ||
| 33 | + add_index :analytics_plugin_page_views, :referer_page_view_id | ||
| 34 | + | ||
| 35 | + add_index :analytics_plugin_page_views, :user_id | ||
| 36 | + add_index :analytics_plugin_page_views, :session_id | ||
| 37 | + add_index :analytics_plugin_page_views, :profile_id | ||
| 38 | + add_index :analytics_plugin_page_views, :url | ||
| 39 | + add_index :analytics_plugin_page_views, [:user_id, :session_id, :profile_id, :url], name: :analytics_plugin_referer_find | ||
| 40 | + end | ||
| 41 | + | ||
| 42 | + def down | ||
| 43 | + drop_table :analytics_plugin_visits | ||
| 44 | + drop_table :analytics_plugin_page_views | ||
| 45 | + end | ||
| 46 | + | ||
| 47 | +end |
| @@ -0,0 +1,15 @@ | @@ -0,0 +1,15 @@ | ||
| 1 | +module AnalyticsPlugin | ||
| 2 | + | ||
| 3 | + TimeOnPageUpdateInterval = 2.minutes * 1000 | ||
| 4 | + | ||
| 5 | + extend Noosfero::Plugin::ParentMethods | ||
| 6 | + | ||
| 7 | + def self.plugin_name | ||
| 8 | + I18n.t'analytics_plugin.lib.plugin.name' | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + def self.plugin_description | ||
| 12 | + I18n.t'analytics_plugin.lib.plugin.description' | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | +end |
| @@ -0,0 +1,43 @@ | @@ -0,0 +1,43 @@ | ||
| 1 | + | ||
| 2 | +class AnalyticsPlugin::Base < Noosfero::Plugin | ||
| 3 | + | ||
| 4 | + def body_ending | ||
| 5 | + return unless profile and profile.analytics_enabled? | ||
| 6 | + lambda do | ||
| 7 | + render 'analytics_plugin/body_ending' | ||
| 8 | + end | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + def js_files | ||
| 12 | + ['analytics'].map{ |j| "javascripts/#{j}" } | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | + def application_controller_filters | ||
| 16 | + [{ | ||
| 17 | + type: 'around_filter', options: {}, block: -> &block do | ||
| 18 | + request_started_at = Time.now | ||
| 19 | + block.call | ||
| 20 | + request_finished_at = Time.now | ||
| 21 | + | ||
| 22 | + return if @analytics_skip_page_view | ||
| 23 | + return unless profile and profile.analytics_enabled? | ||
| 24 | + | ||
| 25 | + Noosfero::Scheduler::Defer.later 'analytics: register page view' do | ||
| 26 | + page_view = profile.page_views.build request: request, profile_id: profile, | ||
| 27 | + request_started_at: request_started_at, request_finished_at: request_finished_at | ||
| 28 | + | ||
| 29 | + unless profile.analytics_anonymous? | ||
| 30 | + # FIXME: use session.id in Rails 4 | ||
| 31 | + session_id = Marshal.load(Base64.decode64 request['_session_id'])['session_id'] rescue nil | ||
| 32 | + #session_id = request.session_options[:id] | ||
| 33 | + page_view.user = user | ||
| 34 | + page_view.session_id = session_id | ||
| 35 | + end | ||
| 36 | + | ||
| 37 | + page_view.save! | ||
| 38 | + end | ||
| 39 | + end, | ||
| 40 | + }] | ||
| 41 | + end | ||
| 42 | + | ||
| 43 | +end |
| @@ -0,0 +1,30 @@ | @@ -0,0 +1,30 @@ | ||
| 1 | +require_dependency 'profile' | ||
| 2 | +require_dependency 'community' | ||
| 3 | + | ||
| 4 | +([Profile] + Profile.descendants).each do |subclass| | ||
| 5 | +subclass.class_eval do | ||
| 6 | + | ||
| 7 | + has_many :visits, foreign_key: :profile_id, class_name: 'AnalyticsPlugin::Visit' | ||
| 8 | + has_many :page_views, foreign_key: :profile_id, class_name: 'AnalyticsPlugin::PageView' | ||
| 9 | + | ||
| 10 | +end | ||
| 11 | +end | ||
| 12 | + | ||
| 13 | +class Profile | ||
| 14 | + | ||
| 15 | + def analytics_settings attrs = {} | ||
| 16 | + @analytics_settings ||= Noosfero::Plugin::Settings.new self, AnalyticsPlugin, attrs | ||
| 17 | + attrs.each{ |a, v| @analytics_settings.send "#{a}=", v } | ||
| 18 | + @analytics_settings | ||
| 19 | + end | ||
| 20 | + alias_method :analytics_settings=, :analytics_settings | ||
| 21 | + | ||
| 22 | + def analytics_enabled? | ||
| 23 | + self.analytics_settings.enabled | ||
| 24 | + end | ||
| 25 | + | ||
| 26 | + def analytics_anonymous? | ||
| 27 | + self.analytics_settings.anonymous | ||
| 28 | + end | ||
| 29 | + | ||
| 30 | +end |
| @@ -0,0 +1,67 @@ | @@ -0,0 +1,67 @@ | ||
| 1 | +class AnalyticsPlugin::PageView < ActiveRecord::Base | ||
| 2 | + | ||
| 3 | + serialize :data | ||
| 4 | + | ||
| 5 | + attr_accessible *self.column_names | ||
| 6 | + attr_accessible :user, :profile | ||
| 7 | + | ||
| 8 | + attr_accessor :request | ||
| 9 | + attr_accessible :request | ||
| 10 | + | ||
| 11 | + acts_as_having_settings field: :options | ||
| 12 | + | ||
| 13 | + belongs_to :visit, class_name: 'AnalyticsPlugin::Visit' | ||
| 14 | + belongs_to :referer_page_view, class_name: 'AnalyticsPlugin::PageView' | ||
| 15 | + | ||
| 16 | + belongs_to :user, class_name: 'Person' | ||
| 17 | + belongs_to :session, primary_key: :session_id, foreign_key: :session_id, class_name: 'Session' | ||
| 18 | + belongs_to :profile | ||
| 19 | + | ||
| 20 | + validates_presence_of :visit | ||
| 21 | + validates_presence_of :request, on: :create | ||
| 22 | + validates_presence_of :url | ||
| 23 | + | ||
| 24 | + before_validation :extract_request_data, on: :create | ||
| 25 | + before_validation :fill_referer_page_view, on: :create | ||
| 26 | + before_validation :fill_visit, on: :create | ||
| 27 | + | ||
| 28 | + def request_duration | ||
| 29 | + self.request_finished_at - self.request_started_at | ||
| 30 | + end | ||
| 31 | + | ||
| 32 | + def page_load! | ||
| 33 | + self.page_loaded_at = Time.now | ||
| 34 | + self.update_column :page_loaded_at, self.page_loaded_at | ||
| 35 | + end | ||
| 36 | + | ||
| 37 | + def increase_time_on_page! | ||
| 38 | + now = Time.now | ||
| 39 | + initial_time = self.page_loaded_at || self.request_finished_at | ||
| 40 | + return unless now > initial_time | ||
| 41 | + | ||
| 42 | + self.time_on_page = now - initial_time | ||
| 43 | + self.update_column :time_on_page, self.time_on_page | ||
| 44 | + end | ||
| 45 | + | ||
| 46 | + protected | ||
| 47 | + | ||
| 48 | + def extract_request_data | ||
| 49 | + self.url = self.request.url.sub /\/+$/, '' | ||
| 50 | + self.referer_url = self.request.referer | ||
| 51 | + self.user_agent = self.request.headers['User-Agent'] | ||
| 52 | + self.request_id = self.request.env['action_dispatch.request_id'] | ||
| 53 | + self.remote_ip = self.request.remote_ip | ||
| 54 | + end | ||
| 55 | + | ||
| 56 | + def fill_referer_page_view | ||
| 57 | + self.referer_page_view = AnalyticsPlugin::PageView.order('request_started_at DESC'). | ||
| 58 | + where(url: self.referer_url, session_id: self.session_id, user_id: self.user_id, profile_id: self.profile_id).first if self.referer_url.present? | ||
| 59 | + end | ||
| 60 | + | ||
| 61 | + def fill_visit | ||
| 62 | + self.visit = self.referer_page_view.visit if self.referer_page_view | ||
| 63 | + self.visit ||= AnalyticsPlugin::Visit.new profile: profile | ||
| 64 | + end | ||
| 65 | + | ||
| 66 | +end | ||
| 67 | + |
| @@ -0,0 +1,11 @@ | @@ -0,0 +1,11 @@ | ||
| 1 | +class AnalyticsPlugin::Visit < ActiveRecord::Base | ||
| 2 | + | ||
| 3 | + attr_accessible *self.column_names | ||
| 4 | + attr_accessible :profile | ||
| 5 | + | ||
| 6 | + default_scope -> { includes :page_views } | ||
| 7 | + | ||
| 8 | + belongs_to :profile | ||
| 9 | + has_many :page_views, class_name: 'AnalyticsPlugin::PageView', dependent: :destroy | ||
| 10 | + | ||
| 11 | +end |
| @@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
| 1 | +# translation of analytic.po to portuguese | ||
| 2 | +# Krishnamurti Lelis Lima Vieira Nunes <krishna@colivre.coop.br>, 2007. | ||
| 3 | +# noosfero - Brazilian Portuguese translation | ||
| 4 | +# Copyright (C) 2007, | ||
| 5 | +# Forum Brasileiro de Economia Solidaria <http://www.fbes.org.br/> | ||
| 6 | +# Copyright (C) 2007, | ||
| 7 | +# Ynternet.org Foundation <http://www.ynternet.org/> | ||
| 8 | +# This file is distributed under the same license as noosfero itself. | ||
| 9 | +# Joenio Costa <joenio@colivre.coop.br>, 2008. | ||
| 10 | +# | ||
| 11 | +# | ||
| 12 | +msgid "" | ||
| 13 | +msgstr "" | ||
| 14 | +"Project-Id-Version: 1.0-690-gcb6e853\n" | ||
| 15 | +"POT-Creation-Date: 2015-03-05 12:10-0300\n" | ||
| 16 | +"PO-Revision-Date: 2015-07-21 09:23-0300\n" | ||
| 17 | +"Last-Translator: Michal Čihař <michal@cihar.com>\n" | ||
| 18 | +"Language-Team: Portuguese <https://hosted.weblate.org/projects/noosfero" | ||
| 19 | +"/plugin-solr/pt/>\n" | ||
| 20 | +"Language: pt\n" | ||
| 21 | +"MIME-Version: 1.0\n" | ||
| 22 | +"Content-Type: text/plain; charset=UTF-8\n" | ||
| 23 | +"Content-Transfer-Encoding: 8bit\n" | ||
| 24 | +"Plural-Forms: nplurals=2; plural=n != 1;\n" | ||
| 25 | +"X-Generator: Weblate 2.3-dev\n" | ||
| 26 | + | ||
| 27 | +msgid "Select the set of communities and users to track" | ||
| 28 | +msgstr "Seleciona o conjunto de comunidades e usuários para rastrear" | ||
| 29 | + |
| @@ -0,0 +1,39 @@ | @@ -0,0 +1,39 @@ | ||
| 1 | +analytics = { | ||
| 2 | + requestId: '', | ||
| 3 | + | ||
| 4 | + timeOnPage: { | ||
| 5 | + updateInterval: 0, | ||
| 6 | + baseUrl: '', | ||
| 7 | + | ||
| 8 | + report: function() { | ||
| 9 | + $.ajax(analytics.timeOnPage.baseUrl+'/report', { | ||
| 10 | + type: 'POST', data: {id: analytics.requestId}, | ||
| 11 | + success: function(data) { | ||
| 12 | + | ||
| 13 | + analytics.timeOnPage.poll() | ||
| 14 | + }, | ||
| 15 | + }) | ||
| 16 | + }, | ||
| 17 | + | ||
| 18 | + poll: function() { | ||
| 19 | + if (analytics.timeOnPage.updateInterval) | ||
| 20 | + setTimeout(analytics.timeOnPage.report, analytics.timeOnPage.updateInterval) | ||
| 21 | + }, | ||
| 22 | + }, | ||
| 23 | + | ||
| 24 | + init: function() { | ||
| 25 | + analytics.timeOnPage.poll() | ||
| 26 | + }, | ||
| 27 | + | ||
| 28 | + pageLoad: function() { | ||
| 29 | + $.ajax(analytics.timeOnPage.baseUrl+'/page_load', { | ||
| 30 | + type: 'POST', data: {id: analytics.requestId}, | ||
| 31 | + success: function(data) { | ||
| 32 | + }, | ||
| 33 | + }); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | +}; | ||
| 37 | + | ||
| 38 | +$(document).ready(analytics.pageLoad) | ||
| 39 | + |
plugins/analytics/test/functional/content_viewer_controller_test.rb
0 → 100644
| @@ -0,0 +1,48 @@ | @@ -0,0 +1,48 @@ | ||
| 1 | +require 'test_helper' | ||
| 2 | +require 'content_viewer_controller' | ||
| 3 | + | ||
| 4 | +class ContentViewerControllerTest < ActionController::TestCase | ||
| 5 | + | ||
| 6 | + def setup | ||
| 7 | + @controller = ContentViewerController.new | ||
| 8 | + @request = ActionController::TestRequest.new | ||
| 9 | + @response = ActionController::TestResponse.new | ||
| 10 | + | ||
| 11 | + @environment = Environment.default | ||
| 12 | + @environment.enabled_plugins += ['AnalyticsPlugin'] | ||
| 13 | + @environment.save! | ||
| 14 | + | ||
| 15 | + @user = create_user('testinguser').person | ||
| 16 | + login_as @user.identifier | ||
| 17 | + | ||
| 18 | + @community = build Community, identifier: 'testcomm', name: 'test' | ||
| 19 | + @community.analytics_settings.enabled = true | ||
| 20 | + @community.analytics_settings.anonymous = false | ||
| 21 | + @community.save! | ||
| 22 | + @community.add_member @user | ||
| 23 | + end | ||
| 24 | + | ||
| 25 | + should 'register page view correctly' do | ||
| 26 | + @request.env['HTTP_REFERER'] = 'http://google.com' | ||
| 27 | + first_url = 'http://test.host' | ||
| 28 | + get :view_page, profile: @community.identifier, page: [] | ||
| 29 | + assert_equal 1, @community.page_views.count | ||
| 30 | + assert_equal 1, @community.visits.count | ||
| 31 | + | ||
| 32 | + first_page_view = @community.page_views.order(:id).first | ||
| 33 | + assert_equal @request.referer, first_page_view.referer_url | ||
| 34 | + | ||
| 35 | + @request.env['HTTP_REFERER'] = first_url | ||
| 36 | + get :view_page, profile: @community.identifier, page: @community.articles.last.path.split('/') | ||
| 37 | + assert_equal 2, @community.page_views.count | ||
| 38 | + assert_equal 1, @community.visits.count | ||
| 39 | + | ||
| 40 | + second_page_view = @community.page_views.order(:id).last | ||
| 41 | + assert_equal first_page_view, second_page_view.referer_page_view | ||
| 42 | + | ||
| 43 | + assert_equal @user, second_page_view.user | ||
| 44 | + | ||
| 45 | + assert second_page_view.request_duration > 0 and second_page_view.request_duration < 1 | ||
| 46 | + end | ||
| 47 | + | ||
| 48 | +end |
plugins/analytics/views/analytics_plugin/_body_ending.html.slim
0 → 100644
| @@ -0,0 +1,6 @@ | @@ -0,0 +1,6 @@ | ||
| 1 | +javascript: | ||
| 2 | + analytics.timeOnPage.baseUrl = #{url_for(controller: 'analytics_plugin/time_on_page').to_json} | ||
| 3 | + analytics.timeOnPage.updateInterval = #{AnalyticsPlugin::TimeOnPageUpdateInterval.to_json} | ||
| 4 | + analytics.requestId = #{request.env['action_dispatch.request_id'].to_json} | ||
| 5 | + analytics.init() | ||
| 6 | + |
plugins/comment_classification/po/fr/comment_classification.po
| @@ -7,8 +7,8 @@ msgstr "" | @@ -7,8 +7,8 @@ msgstr "" | ||
| 7 | "Project-Id-Version: 1.1-166-gaf47713\n" | 7 | "Project-Id-Version: 1.1-166-gaf47713\n" |
| 8 | "Report-Msgid-Bugs-To: \n" | 8 | "Report-Msgid-Bugs-To: \n" |
| 9 | "POT-Creation-Date: 2015-06-01 17:26-0300\n" | 9 | "POT-Creation-Date: 2015-06-01 17:26-0300\n" |
| 10 | -"PO-Revision-Date: 2015-07-03 00:34+0200\n" | ||
| 11 | -"Last-Translator: Christophe DANIEL <papaeng@gmail.com>\n" | 10 | +"PO-Revision-Date: 2015-03-07 12:26+0200\n" |
| 11 | +"Last-Translator: Tuux <tuxa@galaxie.eu.org>\n" | ||
| 12 | "Language-Team: French <https://hosted.weblate.org/projects/noosfero/plugin-" | 12 | "Language-Team: French <https://hosted.weblate.org/projects/noosfero/plugin-" |
| 13 | "comment-classification/fr/>\n" | 13 | "comment-classification/fr/>\n" |
| 14 | "Language: fr\n" | 14 | "Language: fr\n" |
| @@ -16,7 +16,7 @@ msgstr "" | @@ -16,7 +16,7 @@ msgstr "" | ||
| 16 | "Content-Type: text/plain; charset=UTF-8\n" | 16 | "Content-Type: text/plain; charset=UTF-8\n" |
| 17 | "Content-Transfer-Encoding: 8bit\n" | 17 | "Content-Transfer-Encoding: 8bit\n" |
| 18 | "Plural-Forms: nplurals=2; plural=n > 1;\n" | 18 | "Plural-Forms: nplurals=2; plural=n > 1;\n" |
| 19 | -"X-Generator: Weblate 2.4-dev\n" | 19 | +"X-Generator: Weblate 2.3-dev\n" |
| 20 | 20 | ||
| 21 | #: plugins/comment_classification/lib/comment_classification_plugin.rb:11 | 21 | #: plugins/comment_classification/lib/comment_classification_plugin.rb:11 |
| 22 | msgid "A plugin that allow classification of comments." | 22 | msgid "A plugin that allow classification of comments." |
| @@ -80,7 +80,7 @@ msgstr "Statuts" | @@ -80,7 +80,7 @@ msgstr "Statuts" | ||
| 80 | 80 | ||
| 81 | #: plugins/comment_classification/views/comment_classification_plugin_myprofile/_status_form.html.erb:7 | 81 | #: plugins/comment_classification/views/comment_classification_plugin_myprofile/_status_form.html.erb:7 |
| 82 | msgid "Reason:" | 82 | msgid "Reason:" |
| 83 | -msgstr "Raison :" | 83 | +msgstr "Raison:" |
| 84 | 84 | ||
| 85 | #: plugins/comment_classification/views/comment_classification_plugin_myprofile/add_status.html.erb:1 | 85 | #: plugins/comment_classification/views/comment_classification_plugin_myprofile/add_status.html.erb:1 |
| 86 | msgid "Status for comment" | 86 | msgid "Status for comment" |
| @@ -121,7 +121,7 @@ msgstr "Couleur" | @@ -121,7 +121,7 @@ msgstr "Couleur" | ||
| 121 | 121 | ||
| 122 | #: plugins/comment_classification/views/comment_classification_plugin_labels/_form.html.erb:8 | 122 | #: plugins/comment_classification/views/comment_classification_plugin_labels/_form.html.erb:8 |
| 123 | msgid "Enable this label?" | 123 | msgid "Enable this label?" |
| 124 | -msgstr "Activer ce label ?" | 124 | +msgstr "Activer ce label?" |
| 125 | 125 | ||
| 126 | #: plugins/comment_classification/views/comment_classification_plugin_labels/edit.html.erb:1 | 126 | #: plugins/comment_classification/views/comment_classification_plugin_labels/edit.html.erb:1 |
| 127 | msgid "Editing label %s" | 127 | msgid "Editing label %s" |
| @@ -149,7 +149,7 @@ msgstr "Label" | @@ -149,7 +149,7 @@ msgstr "Label" | ||
| 149 | #: plugins/comment_classification/views/comment_classification_plugin_labels/index.html.erb:11 | 149 | #: plugins/comment_classification/views/comment_classification_plugin_labels/index.html.erb:11 |
| 150 | #: plugins/comment_classification/views/comment_classification_plugin_status/index.html.erb:10 | 150 | #: plugins/comment_classification/views/comment_classification_plugin_status/index.html.erb:10 |
| 151 | msgid "Enabled" | 151 | msgid "Enabled" |
| 152 | -msgstr "Activé(e)" | 152 | +msgstr "Activé" |
| 153 | 153 | ||
| 154 | #: plugins/comment_classification/views/comment_classification_plugin_labels/index.html.erb:12 | 154 | #: plugins/comment_classification/views/comment_classification_plugin_labels/index.html.erb:12 |
| 155 | #: plugins/comment_classification/views/comment_classification_plugin_status/index.html.erb:12 | 155 | #: plugins/comment_classification/views/comment_classification_plugin_status/index.html.erb:12 |
| @@ -162,7 +162,7 @@ msgstr "Êtes-vous sûr(e) que vous voulez retirer ce label ?" | @@ -162,7 +162,7 @@ msgstr "Êtes-vous sûr(e) que vous voulez retirer ce label ?" | ||
| 162 | 162 | ||
| 163 | #: plugins/comment_classification/views/comment_classification_plugin_status/_form.html.erb:7 | 163 | #: plugins/comment_classification/views/comment_classification_plugin_status/_form.html.erb:7 |
| 164 | msgid "Enable this status?" | 164 | msgid "Enable this status?" |
| 165 | -msgstr "Activer ce statut ?" | 165 | +msgstr "Activer ce statut?" |
| 166 | 166 | ||
| 167 | #: plugins/comment_classification/views/comment_classification_plugin_status/edit.html.erb:1 | 167 | #: plugins/comment_classification/views/comment_classification_plugin_status/edit.html.erb:1 |
| 168 | msgid "Editing status %s" | 168 | msgid "Editing status %s" |
| @@ -177,12 +177,13 @@ msgid "(no status registered yet)" | @@ -177,12 +177,13 @@ msgid "(no status registered yet)" | ||
| 177 | msgstr "(il n'y a pas de statut enregistré pour le moment)" | 177 | msgstr "(il n'y a pas de statut enregistré pour le moment)" |
| 178 | 178 | ||
| 179 | #: plugins/comment_classification/views/comment_classification_plugin_status/index.html.erb:11 | 179 | #: plugins/comment_classification/views/comment_classification_plugin_status/index.html.erb:11 |
| 180 | +#, fuzzy | ||
| 180 | msgid "Reason enabled?" | 181 | msgid "Reason enabled?" |
| 181 | -msgstr "Raison activée ?" | 182 | +msgstr "%s n'était pas activé(e)" |
| 182 | 183 | ||
| 183 | #: plugins/comment_classification/views/comment_classification_plugin_status/index.html.erb:21 | 184 | #: plugins/comment_classification/views/comment_classification_plugin_status/index.html.erb:21 |
| 184 | msgid "Are you sure you want to remove this status?" | 185 | msgid "Are you sure you want to remove this status?" |
| 185 | -msgstr "Êtes-vous sûr(e) de vouloir supprimer ce statut ?" | 186 | +msgstr "Êtes-vous sûr(e) de vouloir supprimer ce statut?" |
| 186 | 187 | ||
| 187 | #: plugins/comment_classification/views/comment/comments_labels_select.html.erb:3 | 188 | #: plugins/comment_classification/views/comment/comments_labels_select.html.erb:3 |
| 188 | msgid "[Select ...]" | 189 | msgid "[Select ...]" |
plugins/community_hub/Gemfile
plugins/community_track/lib/community_track_plugin/step.rb
| @@ -76,20 +76,20 @@ class CommunityTrackPlugin::Step < Folder | @@ -76,20 +76,20 @@ class CommunityTrackPlugin::Step < Folder | ||
| 76 | end | 76 | end |
| 77 | 77 | ||
| 78 | def active? | 78 | def active? |
| 79 | - (start_date..end_date).include?(Date.today) | 79 | + (start_date.to_date..end_date.to_date).include?(Date.today) |
| 80 | end | 80 | end |
| 81 | 81 | ||
| 82 | def finished? | 82 | def finished? |
| 83 | - Date.today > end_date | 83 | + Date.today > end_date.to_date |
| 84 | end | 84 | end |
| 85 | 85 | ||
| 86 | def waiting? | 86 | def waiting? |
| 87 | - Date.today < start_date | 87 | + Date.today < start_date.to_date |
| 88 | end | 88 | end |
| 89 | 89 | ||
| 90 | def schedule_activation | 90 | def schedule_activation |
| 91 | return if !changes['start_date'] && !changes['end_date'] | 91 | return if !changes['start_date'] && !changes['end_date'] |
| 92 | - if Date.today <= end_date || accept_comments | 92 | + if Date.today <= end_date.to_date || accept_comments |
| 93 | schedule_date = !accept_comments ? start_date : end_date + 1.day | 93 | schedule_date = !accept_comments ? start_date : end_date + 1.day |
| 94 | CommunityTrackPlugin::ActivationJob.find(id).destroy_all | 94 | CommunityTrackPlugin::ActivationJob.find(id).destroy_all |
| 95 | Delayed::Job.enqueue(CommunityTrackPlugin::ActivationJob.new(self.id), :run_at => schedule_date) | 95 | Delayed::Job.enqueue(CommunityTrackPlugin::ActivationJob.new(self.id), :run_at => schedule_date) |
plugins/custom_forms/po/fr/custom_forms.po
| @@ -7,7 +7,7 @@ msgstr "" | @@ -7,7 +7,7 @@ msgstr "" | ||
| 7 | "Project-Id-Version: 1.1-166-gaf47713\n" | 7 | "Project-Id-Version: 1.1-166-gaf47713\n" |
| 8 | "Report-Msgid-Bugs-To: \n" | 8 | "Report-Msgid-Bugs-To: \n" |
| 9 | "POT-Creation-Date: 2015-06-01 17:26-0300\n" | 9 | "POT-Creation-Date: 2015-06-01 17:26-0300\n" |
| 10 | -"PO-Revision-Date: 2015-07-03 00:36+0200\n" | 10 | +"PO-Revision-Date: 2015-06-29 14:17+0200\n" |
| 11 | "Last-Translator: Christophe DANIEL <papaeng@gmail.com>\n" | 11 | "Last-Translator: Christophe DANIEL <papaeng@gmail.com>\n" |
| 12 | "Language-Team: French <https://hosted.weblate.org/projects/noosfero/plugin-" | 12 | "Language-Team: French <https://hosted.weblate.org/projects/noosfero/plugin-" |
| 13 | "custom-forms/fr/>\n" | 13 | "custom-forms/fr/>\n" |
| @@ -20,12 +20,13 @@ msgstr "" | @@ -20,12 +20,13 @@ msgstr "" | ||
| 20 | 20 | ||
| 21 | #: plugins/custom_forms/lib/custom_forms_plugin/form.rb:67 | 21 | #: plugins/custom_forms/lib/custom_forms_plugin/form.rb:67 |
| 22 | msgid "Invalid string format of access." | 22 | msgid "Invalid string format of access." |
| 23 | -msgstr "Format de chaîne d'accès non valide." | 23 | +msgstr "" |
| 24 | 24 | ||
| 25 | #: plugins/custom_forms/lib/custom_forms_plugin/form.rb:71 | 25 | #: plugins/custom_forms/lib/custom_forms_plugin/form.rb:71 |
| 26 | #: plugins/custom_forms/lib/custom_forms_plugin/form.rb:76 | 26 | #: plugins/custom_forms/lib/custom_forms_plugin/form.rb:76 |
| 27 | +#, fuzzy | ||
| 27 | msgid "There is no profile with the provided id." | 28 | msgid "There is no profile with the provided id." |
| 28 | -msgstr "Il n'y a pas le profil avec l'ID fourni." | 29 | +msgstr "Il y a un problème avec les lignes suivantes : " |
| 29 | 30 | ||
| 30 | #: plugins/custom_forms/lib/custom_forms_plugin/form.rb:81 | 31 | #: plugins/custom_forms/lib/custom_forms_plugin/form.rb:81 |
| 31 | msgid "Invalid type format of access." | 32 | msgid "Invalid type format of access." |
plugins/display_content/po/fr/display_content.po
| @@ -7,8 +7,8 @@ msgstr "" | @@ -7,8 +7,8 @@ msgstr "" | ||
| 7 | "Project-Id-Version: 1.1-166-gaf47713\n" | 7 | "Project-Id-Version: 1.1-166-gaf47713\n" |
| 8 | "Report-Msgid-Bugs-To: \n" | 8 | "Report-Msgid-Bugs-To: \n" |
| 9 | "POT-Creation-Date: 2015-06-01 17:26-0300\n" | 9 | "POT-Creation-Date: 2015-06-01 17:26-0300\n" |
| 10 | -"PO-Revision-Date: 2015-07-03 00:31+0200\n" | ||
| 11 | -"Last-Translator: Christophe DANIEL <papaeng@gmail.com>\n" | 10 | +"PO-Revision-Date: 2015-03-07 02:11+0200\n" |
| 11 | +"Last-Translator: Tuux <tuxa@galaxie.eu.org>\n" | ||
| 12 | "Language-Team: French <https://hosted.weblate.org/projects/noosfero/plugin-" | 12 | "Language-Team: French <https://hosted.weblate.org/projects/noosfero/plugin-" |
| 13 | "display-content/fr/>\n" | 13 | "display-content/fr/>\n" |
| 14 | "Language: fr\n" | 14 | "Language: fr\n" |
| @@ -16,14 +16,14 @@ msgstr "" | @@ -16,14 +16,14 @@ msgstr "" | ||
| 16 | "Content-Type: text/plain; charset=UTF-8\n" | 16 | "Content-Type: text/plain; charset=UTF-8\n" |
| 17 | "Content-Transfer-Encoding: 8bit\n" | 17 | "Content-Transfer-Encoding: 8bit\n" |
| 18 | "Plural-Forms: nplurals=2; plural=n > 1;\n" | 18 | "Plural-Forms: nplurals=2; plural=n > 1;\n" |
| 19 | -"X-Generator: Weblate 2.4-dev\n" | 19 | +"X-Generator: Weblate 2.3-dev\n" |
| 20 | 20 | ||
| 21 | #: plugins/display_content/lib/display_content_plugin.rb:10 | 21 | #: plugins/display_content/lib/display_content_plugin.rb:10 |
| 22 | msgid "" | 22 | msgid "" |
| 23 | "A plugin that adds a block where you could choose any of your content and " | 23 | "A plugin that adds a block where you could choose any of your content and " |
| 24 | "display it." | 24 | "display it." |
| 25 | msgstr "" | 25 | msgstr "" |
| 26 | -"Un plugin qui permet d'ajouter une zone ou vous pourrez y afficher le " | 26 | +"Un greffon qui permet d'ajouter une zone ou vous pourrez y afficher le " |
| 27 | "contenue de votre choix." | 27 | "contenue de votre choix." |
| 28 | 28 | ||
| 29 | #: plugins/display_content/lib/display_content_block.rb:34 | 29 | #: plugins/display_content/lib/display_content_block.rb:34 |
| @@ -48,33 +48,33 @@ msgstr "Résumé" | @@ -48,33 +48,33 @@ msgstr "Résumé" | ||
| 48 | 48 | ||
| 49 | #: plugins/display_content/lib/display_content_block.rb:151 | 49 | #: plugins/display_content/lib/display_content_block.rb:151 |
| 50 | msgid "Read more" | 50 | msgid "Read more" |
| 51 | -msgstr "Lire plus" | 51 | +msgstr "En lire plus" |
| 52 | 52 | ||
| 53 | #: plugins/display_content/lib/display_content_block.rb:194 | 53 | #: plugins/display_content/lib/display_content_block.rb:194 |
| 54 | msgid "%{month}/%{day}/%{year}" | 54 | msgid "%{month}/%{day}/%{year}" |
| 55 | -msgstr "%{day}/%{month}/%{year}" | 55 | +msgstr "%{mois}/%{jour}/%{année}" |
| 56 | 56 | ||
| 57 | #: plugins/display_content/lib/display_content_block.rb:194 | 57 | #: plugins/display_content/lib/display_content_block.rb:194 |
| 58 | msgid "%{month}/%{day}" | 58 | msgid "%{month}/%{day}" |
| 59 | -msgstr "%{day}/%{month}" | 59 | +msgstr "%{mois}/%{jour}" |
| 60 | 60 | ||
| 61 | #: plugins/display_content/lib/display_content_block.rb:197 | 61 | #: plugins/display_content/lib/display_content_block.rb:197 |
| 62 | msgid "%{month_name} %{day}, %{year}" | 62 | msgid "%{month_name} %{day}, %{year}" |
| 63 | -msgstr "%{day} %{month_name} %{year}" | 63 | +msgstr "%{nom_du_mois} %{jour}, %{année}" |
| 64 | 64 | ||
| 65 | #: plugins/display_content/lib/display_content_block.rb:197 | 65 | #: plugins/display_content/lib/display_content_block.rb:197 |
| 66 | msgid "%{month_name} %{day}" | 66 | msgid "%{month_name} %{day}" |
| 67 | -msgstr "%{day} %{month_name}" | 67 | +msgstr "%{nom_du_mois} %{jour}" |
| 68 | 68 | ||
| 69 | #: plugins/display_content/views/box_organizer/_display_content_block.html.erb:5 | 69 | #: plugins/display_content/views/box_organizer/_display_content_block.html.erb:5 |
| 70 | msgid "Choose which attributes should be displayed and drag to reorder them:" | 70 | msgid "Choose which attributes should be displayed and drag to reorder them:" |
| 71 | msgstr "" | 71 | msgstr "" |
| 72 | "Choisissez quels attribues doivent êtres afficher et glisser/déposer pour " | 72 | "Choisissez quels attribues doivent êtres afficher et glisser/déposer pour " |
| 73 | -"les réorganiser :" | 73 | +"les réorganiser:" |
| 74 | 74 | ||
| 75 | #: plugins/display_content/views/box_organizer/_display_content_block.html.erb:21 | 75 | #: plugins/display_content/views/box_organizer/_display_content_block.html.erb:21 |
| 76 | msgid "Choose which content should be displayed:" | 76 | msgid "Choose which content should be displayed:" |
| 77 | -msgstr "Choisissez le contenu à afficher :" | 77 | +msgstr "Choisissez le contenu à afficher:" |
| 78 | 78 | ||
| 79 | #: plugins/display_content/views/box_organizer/_display_content_block.html.erb:23 | 79 | #: plugins/display_content/views/box_organizer/_display_content_block.html.erb:23 |
| 80 | msgid "Choose directly" | 80 | msgid "Choose directly" |
| @@ -86,7 +86,7 @@ msgstr "Choisir par type de contenu" | @@ -86,7 +86,7 @@ msgstr "Choisir par type de contenu" | ||
| 86 | 86 | ||
| 87 | #: plugins/display_content/views/box_organizer/_display_content_block.html.erb:28 | 87 | #: plugins/display_content/views/box_organizer/_display_content_block.html.erb:28 |
| 88 | msgid "Order by:" | 88 | msgid "Order by:" |
| 89 | -msgstr "Trier par :" | 89 | +msgstr "Trier par:" |
| 90 | 90 | ||
| 91 | #: plugins/display_content/views/box_organizer/_display_content_block.html.erb:29 | 91 | #: plugins/display_content/views/box_organizer/_display_content_block.html.erb:29 |
| 92 | msgid "Most recent" | 92 | msgid "Most recent" |
| @@ -98,7 +98,7 @@ msgstr "Le plus ancien" | @@ -98,7 +98,7 @@ msgstr "Le plus ancien" | ||
| 98 | 98 | ||
| 99 | #: plugins/display_content/views/box_organizer/_choose_by_content_type.html.erb:1 | 99 | #: plugins/display_content/views/box_organizer/_choose_by_content_type.html.erb:1 |
| 100 | msgid "Display content types:" | 100 | msgid "Display content types:" |
| 101 | -msgstr "Afficher le type de contenu :" | 101 | +msgstr "Afficher le types de contenue:" |
| 102 | 102 | ||
| 103 | #: plugins/display_content/views/box_organizer/_choose_by_content_type.html.erb:7 | 103 | #: plugins/display_content/views/box_organizer/_choose_by_content_type.html.erb:7 |
| 104 | msgid "more" | 104 | msgid "more" |
| @@ -106,8 +106,9 @@ msgstr "plus" | @@ -106,8 +106,9 @@ msgstr "plus" | ||
| 106 | 106 | ||
| 107 | #: plugins/display_content/views/box_organizer/_choose_directly.html.erb:5 | 107 | #: plugins/display_content/views/box_organizer/_choose_directly.html.erb:5 |
| 108 | msgid "Dinamically load children of selected folders" | 108 | msgid "Dinamically load children of selected folders" |
| 109 | -msgstr "charger dynamiquement les sous répertoires des répertoires sélectionnés" | 109 | +msgstr "" |
| 110 | +"Dynamiquement charger les sous répertoires des répertoires sélectionnés" | ||
| 110 | 111 | ||
| 111 | #: plugins/display_content/views/box_organizer/_choose_directly.html.erb:9 | 112 | #: plugins/display_content/views/box_organizer/_choose_directly.html.erb:9 |
| 112 | msgid "Limit:" | 113 | msgid "Limit:" |
| 113 | -msgstr "Limite :" | 114 | +msgstr "Limite:" |
plugins/gamification
| @@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
| 1 | +Subproject commit 7d1cef867e1325f623638366faeb5387a9261b0a |
| @@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
| 1 | +Subproject commit 8fb44a478abb5ccd345b8ffaf1f37817d273ad30 |
plugins/oauth_client/Gemfile
plugins/oauth_client/controllers/public/oauth_client_plugin_public_controller.rb
| @@ -22,7 +22,7 @@ class OauthClientPluginPublicController < PublicController | @@ -22,7 +22,7 @@ class OauthClientPluginPublicController < PublicController | ||
| 22 | if session.delete(:oauth_client_popup) || params[:oauth_client_popup] | 22 | if session.delete(:oauth_client_popup) || params[:oauth_client_popup] |
| 23 | current_user.private_token_expired? if current_user.present? | 23 | current_user.private_token_expired? if current_user.present? |
| 24 | private_token = current_user.present? ? current_user.private_token : '' | 24 | private_token = current_user.present? ? current_user.private_token : '' |
| 25 | - render 'oauth_client_plugin_public/finish', :locals => {:private_token => private_token, :user => params[:user]}, :layout => false | 25 | + render 'oauth_client_plugin_public/finish', :locals => {:private_token => private_token}, :layout => false |
| 26 | else | 26 | else |
| 27 | redirect_to :controller => :home | 27 | redirect_to :controller => :home |
| 28 | end | 28 | end |
| @@ -42,22 +42,47 @@ class OauthClientPluginPublicController < PublicController | @@ -42,22 +42,47 @@ class OauthClientPluginPublicController < PublicController | ||
| 42 | else | 42 | else |
| 43 | session[:notice] = _("Can't login with #{provider.name}") | 43 | session[:notice] = _("Can't login with #{provider.name}") |
| 44 | end | 44 | end |
| 45 | - session[:oauth_client_popup] = true if request.env["omniauth.params"]['oauth_client_popup'] | ||
| 46 | - session[:return_to] = url_for(:controller => :oauth_client_plugin_public, :action => :finish) | 45 | + session[:oauth_client_popup] = true if request.env.fetch("omniauth.params", {})['oauth_client_popup'] |
| 46 | + session[:return_to] = url_for( | ||
| 47 | + :controller => :oauth_client_plugin_public, | ||
| 48 | + :action => :finish, | ||
| 49 | + :user => { | ||
| 50 | + :login => current_user.login, | ||
| 51 | + :person => {:identifier => current_user.person.identifier, :name => current_user.person.name} | ||
| 52 | + } , | ||
| 53 | + :profile_data => {:name => current_user.person.name}, | ||
| 54 | + :oauth_client_popup => session[:oauth_client_popup] | ||
| 55 | + ) | ||
| 47 | 56 | ||
| 48 | redirect_to :controller => :account, :action => :login | 57 | redirect_to :controller => :account, :action => :login |
| 49 | end | 58 | end |
| 50 | 59 | ||
| 51 | def signup(auth) | 60 | def signup(auth) |
| 52 | login = auth.info.email.split('@').first | 61 | login = auth.info.email.split('@').first |
| 62 | + | ||
| 63 | + # reading provider from session and writing to cache to read when | ||
| 64 | + # api calls register to confirm signup | ||
| 65 | + auth_cach_hash = auth.to_hash | ||
| 66 | + auth_cach_hash[:provider_id] = session[:provider_id] | ||
| 67 | + signup_token = OauthClientPlugin::SignupDataStore.store_oauth_data(auth.info.email, auth_cach_hash) | ||
| 68 | + | ||
| 53 | session[:oauth_data] = auth | 69 | session[:oauth_data] = auth |
| 54 | - session[:oauth_client_popup] = true if request.env["omniauth.params"]['oauth_client_popup'] | 70 | + session[:oauth_client_popup] = true if request.env.fetch("omniauth.params", {})['oauth_client_popup'] |
| 55 | session[:return_to] = url_for(:controller => :oauth_client_plugin_public, :action => :finish) | 71 | session[:return_to] = url_for(:controller => :oauth_client_plugin_public, :action => :finish) |
| 56 | name = auth.info.name | 72 | name = auth.info.name |
| 57 | name ||= auth.extra && auth.extra.raw_info ? auth.extra.raw_info.name : '' | 73 | name ||= auth.extra && auth.extra.raw_info ? auth.extra.raw_info.name : '' |
| 58 | 74 | ||
| 59 | if session[:oauth_client_popup] | 75 | if session[:oauth_client_popup] |
| 60 | - redirect_to :controller => :oauth_client_plugin_public, :action => :finish, :user => {:login => login, :email => auth.info.email, :oauth_providers => [session[:provider_id]]}, :profile_data => {:name => name}, :oauth_client_popup => session[:oauth_client_popup] | 76 | + redirect_to :controller => :oauth_client_plugin_public, |
| 77 | + :action => :finish, | ||
| 78 | + :user => { | ||
| 79 | + :signup_token => signup_token, | ||
| 80 | + :login => login, | ||
| 81 | + :email => auth.info.email, | ||
| 82 | + :oauth_providers => [session[:provider_id]] | ||
| 83 | + }, | ||
| 84 | + :profile_data => {:name => name}, | ||
| 85 | + :oauth_client_popup => session[:oauth_client_popup] | ||
| 61 | else | 86 | else |
| 62 | redirect_to :controller => :account, :action => :signup, :user => {:login => login, :email => auth.info.email}, :profile_data => {:name => name} | 87 | redirect_to :controller => :account, :action => :signup, :user => {:login => login, :email => auth.info.email}, :profile_data => {:name => name} |
| 63 | end | 88 | end |
plugins/oauth_client/db/migrate/20150714200000_add_oauth_auth_fields_to_user_provider.rb
0 → 100644
| @@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
| 1 | +class AddOauthAuthFieldsToUserProvider < ActiveRecord::Migration | ||
| 2 | + | ||
| 3 | + def self.up | ||
| 4 | + change_table :oauth_client_plugin_user_providers do |t| | ||
| 5 | + t.text :oauth_data | ||
| 6 | + end | ||
| 7 | + end | ||
| 8 | + | ||
| 9 | + def self.down | ||
| 10 | + remove_column :oauth_client_plugin_user_providers, :oauth_data | ||
| 11 | + end | ||
| 12 | +end |
plugins/oauth_client/lib/ext/environment.rb
| @@ -4,4 +4,10 @@ class Environment | @@ -4,4 +4,10 @@ class Environment | ||
| 4 | 4 | ||
| 5 | has_many :oauth_providers, :class_name => 'OauthClientPlugin::Provider' | 5 | has_many :oauth_providers, :class_name => 'OauthClientPlugin::Provider' |
| 6 | 6 | ||
| 7 | + def signup_person_fields_with_oauth | ||
| 8 | + signup_person_fields_without_oauth + [:oauth_signup_token] | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + alias_method_chain :signup_person_fields, :oauth | ||
| 12 | + | ||
| 7 | end | 13 | end |
plugins/oauth_client/lib/ext/user.rb
| @@ -6,24 +6,54 @@ class User | @@ -6,24 +6,54 @@ class User | ||
| 6 | has_many :oauth_providers, :through => :oauth_user_providers, :source => :provider | 6 | has_many :oauth_providers, :through => :oauth_user_providers, :source => :provider |
| 7 | 7 | ||
| 8 | def password_required_with_oauth? | 8 | def password_required_with_oauth? |
| 9 | + # user creation through api does not set oauth_providers | ||
| 10 | + check_providers | ||
| 9 | password_required_without_oauth? && oauth_providers.empty? | 11 | password_required_without_oauth? && oauth_providers.empty? |
| 10 | end | 12 | end |
| 11 | 13 | ||
| 14 | + def oauth_data | ||
| 15 | + @oauth_data | ||
| 16 | + end | ||
| 17 | + | ||
| 18 | + def oauth_signup_token= value | ||
| 19 | + @oauth_signup_token = value | ||
| 20 | + end | ||
| 21 | + | ||
| 22 | + def oauth_signup_token | ||
| 23 | + @oauth_signup_token | ||
| 24 | + end | ||
| 25 | + | ||
| 12 | alias_method_chain :password_required?, :oauth | 26 | alias_method_chain :password_required?, :oauth |
| 13 | 27 | ||
| 14 | after_create :activate_oauth_user | 28 | after_create :activate_oauth_user |
| 15 | 29 | ||
| 30 | + # user creation through api does not set oauth_providers | ||
| 31 | + # so it is being shared through a distributed cache | ||
| 32 | + def check_providers | ||
| 33 | + if oauth_providers.empty? && oauth_signup_token.present? | ||
| 34 | + #check if is oauth user, reading oauth_data recorded at cache store | ||
| 35 | + @oauth_data = OauthClientPlugin::SignupDataStore.get_oauth_data(self.email, self.oauth_signup_token) | ||
| 36 | + if @oauth_data | ||
| 37 | + provider_id = @oauth_data.delete(:provider_id) | ||
| 38 | + self.oauth_providers = [OauthClientPlugin::Provider.find(provider_id)] | ||
| 39 | + end | ||
| 40 | + end | ||
| 41 | + end | ||
| 42 | + | ||
| 16 | def activate_oauth_user | 43 | def activate_oauth_user |
| 17 | - unless oauth_providers.empty? | ||
| 18 | - activate | ||
| 19 | - oauth_providers.each do |provider| | ||
| 20 | - OauthClientPlugin::UserProvider.create!(:user => self, :provider => provider, :enabled => true) | 44 | + self.oauth_providers.each do |provider| |
| 45 | + OauthClientPlugin::UserProvider.create! do |user_provider| | ||
| 46 | + user_provider.user = self | ||
| 47 | + user_provider.provider = provider | ||
| 48 | + user_provider.enabled = true | ||
| 49 | + user_provider.oauth_data = oauth_data | ||
| 21 | end | 50 | end |
| 22 | end | 51 | end |
| 52 | + activate unless oauth_providers.empty? | ||
| 23 | end | 53 | end |
| 24 | 54 | ||
| 25 | def make_activation_code_with_oauth | 55 | def make_activation_code_with_oauth |
| 26 | - oauth_providers.blank? ? make_activation_code_without_oauth : nil | 56 | + self.oauth_providers.blank? ? make_activation_code_without_oauth : nil |
| 27 | end | 57 | end |
| 28 | 58 | ||
| 29 | alias_method_chain :make_activation_code, :oauth | 59 | alias_method_chain :make_activation_code, :oauth |
plugins/oauth_client/lib/oauth_client_plugin.rb
| @@ -84,8 +84,11 @@ class OauthClientPlugin < Noosfero::Plugin | @@ -84,8 +84,11 @@ class OauthClientPlugin < Noosfero::Plugin | ||
| 84 | 84 | ||
| 85 | if auth.present? && params[:user].present? | 85 | if auth.present? && params[:user].present? |
| 86 | params[:user][:oauth_providers] = [OauthClientPlugin::Provider.find(session[:provider_id])] | 86 | params[:user][:oauth_providers] = [OauthClientPlugin::Provider.find(session[:provider_id])] |
| 87 | + | ||
| 87 | if request.post? && auth.info.email != params[:user][:email] | 88 | if request.post? && auth.info.email != params[:user][:email] |
| 88 | - raise "Wrong email for oauth signup" | 89 | + unless params[:user][:email].blank? |
| 90 | + raise "Wrong email for oauth signup. EMAIL: #{params[:user][:email]}" | ||
| 91 | + end | ||
| 89 | end | 92 | end |
| 90 | end | 93 | end |
| 91 | } | 94 | } |
plugins/oauth_client/lib/oauth_client_plugin/signup_data_store.rb
0 → 100644
| @@ -0,0 +1,34 @@ | @@ -0,0 +1,34 @@ | ||
| 1 | +# A Distributed Cache Store is needed | ||
| 2 | +# to save oauth autenthication to be | ||
| 3 | +# used on OAUTH flow using the Noosfero REST API. | ||
| 4 | +# Because of the nature session less of api implementation | ||
| 5 | +# When using more than one server is strongly recomended | ||
| 6 | +# provide your Rails application with a distributed Cache Store, | ||
| 7 | +# otherwise you will have to rely on client/server affinify provided by | ||
| 8 | +# network infrastructure | ||
| 9 | +class OauthClientPlugin::SignupDataStore | ||
| 10 | + | ||
| 11 | + def self.key_name_for email, signup_token | ||
| 12 | + "#{email}_#{signup_token}" | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | + def self.get_oauth_data email, signup_token | ||
| 16 | + key_name = key_name_for(email, signup_token) | ||
| 17 | + puts "OAUTH_KEY_NAME :::: #{key_name}" | ||
| 18 | + oauth_data = Rails.cache.fetch(key_name) | ||
| 19 | + Rails.cache.delete(key_name) | ||
| 20 | + oauth_data | ||
| 21 | + end | ||
| 22 | + | ||
| 23 | + def self.store_oauth_data email, auth_obj | ||
| 24 | + signup_token = SecureRandom.hex | ||
| 25 | + Rails.cache.write(key_name_for(email, signup_token), auth_obj, :expires_in => 300) | ||
| 26 | + signup_token | ||
| 27 | + end | ||
| 28 | + | ||
| 29 | + def self.delete_cache_for email | ||
| 30 | + Rails.cache.delete(cache_name_for(email)) | ||
| 31 | + end | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +end |
plugins/oauth_client/lib/oauth_client_plugin/user_provider.rb
| @@ -7,4 +7,5 @@ class OauthClientPlugin::UserProvider < Noosfero::Plugin::ActiveRecord | @@ -7,4 +7,5 @@ class OauthClientPlugin::UserProvider < Noosfero::Plugin::ActiveRecord | ||
| 7 | 7 | ||
| 8 | attr_accessible :user, :provider, :enabled | 8 | attr_accessible :user, :provider, :enabled |
| 9 | 9 | ||
| 10 | + acts_as_having_settings :field => :oauth_data | ||
| 10 | end | 11 | end |