diff --git a/plugins/newsletter/config/schedule.rb b/plugins/newsletter/config/schedule.rb new file mode 100644 index 0000000..8f6386b --- /dev/null +++ b/plugins/newsletter/config/schedule.rb @@ -0,0 +1,3 @@ +every 1.day do + runner "NewsletterPlugin.compile_and_send_newsletters" +end diff --git a/plugins/newsletter/controllers/newsletter_plugin_admin_controller.rb b/plugins/newsletter/controllers/newsletter_plugin_admin_controller.rb new file mode 100644 index 0000000..20e3b40 --- /dev/null +++ b/plugins/newsletter/controllers/newsletter_plugin_admin_controller.rb @@ -0,0 +1,48 @@ +class NewsletterPluginAdminController < PluginAdminController + + def index + @newsletter = NewsletterPlugin::Newsletter.where(environment_id: environment.id).first_or_initialize + + if request.post? + # token input gives the param as a comma separated string + params[:newsletter][:blog_ids] = (params[:newsletter][:blog_ids] || '').split(',') + + params[:newsletter][:person_id] = user.id + + file = params[:file] + if file && file[:recipients].present? + @newsletter.import_recipients(file[:recipients], file[:name], file[:email], file[:headers].present?) + end + + if !@newsletter.errors.any? && @newsletter.update_attributes(params[:newsletter]) + if params['visualize'] + @message = @newsletter.body + render :file => 'mailing/sender/notification', :layout => false + else + session[:notice] = _('Newsletter updated.') + end + else + session[:notice] = _('Newsletter could not be saved.') + end + end + + @blogs = Blog.includes(:profile).find_all_by_id(@newsletter.blog_ids) + end + + #TODO: Make this query faster + def search_communities + communities = environment.communities + blogs = Blog.joins(:profile).where(profiles: {environment_id: environment.id}) + + found_communities = find_by_contents(:communities, environment, communities, params['q'], {:page => 1})[:results] + found_blogs = find_by_contents(:blogs, environment, blogs, params['q'], {:page => 1})[:results] + + results = (found_blogs + found_communities.map(&:blogs).flatten).uniq + render :text => results.map { |blog| {:id => blog.id, :name => _("%s in %s") % [blog.name, blog.profile.name]} }.to_json + end + + def recipients + @additional_recipients = NewsletterPlugin::Newsletter.where(environment_id: environment.id).first_or_initialize.additional_recipients + end + +end diff --git a/plugins/newsletter/controllers/newsletter_plugin_controller.rb b/plugins/newsletter/controllers/newsletter_plugin_controller.rb new file mode 100644 index 0000000..4b0f8c8 --- /dev/null +++ b/plugins/newsletter/controllers/newsletter_plugin_controller.rb @@ -0,0 +1,24 @@ +class NewsletterPluginController < PublicController + + before_filter :login_required, :only => :confirm_unsubscription + + def mailing + if NewsletterPlugin::NewsletterMailing.exists?(params[:id]) + mailing = NewsletterPlugin::NewsletterMailing.find(params[:id]) + @message = mailing.body + render :file => 'mailing/sender/notification', :layout => false + else + render :action => 'mailing_not_found' + end + end + + def confirm_unsubscription + if request.post? + session[:notice] = _('You was unsubscribed from newsletter.') + @newsletter = NewsletterPlugin::Newsletter.where(environment_id: environment.id).first + @newsletter.unsubscribe(current_user.email) + redirect_to :controller => :home + end + end + +end diff --git a/plugins/newsletter/db/migrate/20150717195546_newsletter_plugin_newsletters.rb b/plugins/newsletter/db/migrate/20150717195546_newsletter_plugin_newsletters.rb new file mode 100644 index 0000000..4b94c05 --- /dev/null +++ b/plugins/newsletter/db/migrate/20150717195546_newsletter_plugin_newsletters.rb @@ -0,0 +1,24 @@ +class NewsletterPluginNewsletters < ActiveRecord::Migration + def up + create_table :newsletter_plugin_newsletters do |t| + t.references :environment, :null => false + t.references :person, :null => false + t.boolean :enabled, :default => false + t.string :subject + t.integer :periodicity, :default => 0 + t.integer :posts_per_blog, :default => 0 + t.integer :image_id + t.text :footer + t.text :blog_ids + t.text :additional_recipients + t.boolean :moderated + t.text :unsubscribers + end + add_index :newsletter_plugin_newsletters, :environment_id, :uniq => true + end + + def down + remove_index :newsletter_plugin_newsletters, :environment_id + drop_table :newsletter_plugin_newsletters + end +end diff --git a/plugins/newsletter/features/newsletter_plugin.feature b/plugins/newsletter/features/newsletter_plugin.feature new file mode 100644 index 0000000..927f1d1 --- /dev/null +++ b/plugins/newsletter/features/newsletter_plugin.feature @@ -0,0 +1,40 @@ +Feature: newsletter plugin + + Background: + Given the following users + | login | name | + | joaosilva | Joao Silva | + And I am logged in as "joaosilva" + + Scenario: as admin I can configure plugin + Given I am logged in as admin + When I go to the environment control panel + And I follow "Plugins" + Then I should see "Configuration" linking to "/admin/plugin/newsletter" + + Scenario: in the newsletter settings I can see the field to enable/disable + Given I am logged in as admin + When I go to the environment control panel + And I follow "Plugins" + And I follow "Configuration" + Then I should see "Enable send of newsletter to members on this environment" + + Scenario: redirect to newsletter visualization after save and visualize + Given I am logged in as admin + And "NewsletterPlugin" plugin is enabled + When I go to the environment control panel + And I follow "Plugins" + And I follow "Configuration" + And I press "Save and visualize" + Then I should see "If you can't view this email, click here" + And I should not see "Newsletter settings" + + Scenario: stay on newsletter settings page after save + Given I am logged in as admin + And "NewsletterPlugin" plugin is enabled + When I go to the environment control panel + And I follow "Plugins" + And I follow "Configuration" + And I press "Save" + Then I should see "Newsletter settings" + And I should not see "If you can't view this email, click here" diff --git a/plugins/newsletter/lib/newsletter_plugin.rb b/plugins/newsletter/lib/newsletter_plugin.rb new file mode 100644 index 0000000..de21581 --- /dev/null +++ b/plugins/newsletter/lib/newsletter_plugin.rb @@ -0,0 +1,41 @@ +class NewsletterPlugin < Noosfero::Plugin + + def self.plugin_name + "Newsletter" + end + + def self.plugin_description + _("Periodically sends newsletter via email to network users") + end + + def js_files + 'newsletter_plugin.js' + end + + def stylesheet? + true + end + + def self.compile_and_send_newsletters + NewsletterPlugin::Newsletter.enabled.each do |newsletter| + if newsletter.must_be_sent_today? && newsletter.has_posts_in_the_period? + if newsletter.moderated + NewsletterPlugin::ModerateNewsletter.create!( + :newsletter_id => newsletter.id, + :environment => newsletter.environment + ) + else + mailing = NewsletterPlugin::NewsletterMailing.create!( + :source => newsletter, + :subject => newsletter.subject, + :body => newsletter.body, + :person => newsletter.person, + :locale => newsletter.environment.default_locale, + ) + mailing.update_attribute(:body, mailing.body.gsub('{mailing_url}', mailing.url)) + end + end + end + end + +end diff --git a/plugins/newsletter/lib/newsletter_plugin/moderate_newsletter.rb b/plugins/newsletter/lib/newsletter_plugin/moderate_newsletter.rb new file mode 100644 index 0000000..33f61a8 --- /dev/null +++ b/plugins/newsletter/lib/newsletter_plugin/moderate_newsletter.rb @@ -0,0 +1,53 @@ +class NewsletterPlugin::ModerateNewsletter < Task + + settings_items :newsletter_id, :post_ids + validates_presence_of :newsletter_id + + alias :environment :target + alias :environment= :target= + + def perform + newsletter = NewsletterPlugin::Newsletter.find(newsletter_id) + self.post_ids ||= [] + mailing = NewsletterPlugin::NewsletterMailing.create!( + :source => newsletter, + :subject => newsletter.subject, + :body => newsletter.body(:post_ids => self.post_ids.reject{|id| id.to_i.zero?}), + :person => newsletter.person, + :locale => newsletter.environment.default_locale, + ) + mailing.update_attribute(:body, mailing.body.gsub('{mailing_url}', mailing.url)) + end + + def title + _("Moderate newsletter") + end + + def subject + nil + end + + def linked_subject + nil + end + + def information + {:message => _('You have to moderate a newsletter.') } + end + + def accept_details + true + end + + def icon + {:type => :defined_image, :src => "/images/control-panel/email.png", :name => 'Newsletter'} + end + + def target_notification_message + _('A newsletter was generated and you need to review it before it is sent to users.') + end + + def target_notification_description + _('You need to moderate a newsletter.') + end +end diff --git a/plugins/newsletter/lib/newsletter_plugin/newsletter.rb b/plugins/newsletter/lib/newsletter_plugin/newsletter.rb new file mode 100644 index 0000000..a6a25ed --- /dev/null +++ b/plugins/newsletter/lib/newsletter_plugin/newsletter.rb @@ -0,0 +1,191 @@ +require 'csv' + +class NewsletterPlugin::Newsletter < Noosfero::Plugin::ActiveRecord + + belongs_to :environment + belongs_to :person + validates_presence_of :environment, :person + validates_uniqueness_of :environment_id + validates_numericality_of :periodicity, only_integer: true, greater_than: -1, message: _('must be a positive number') + validates_numericality_of :posts_per_blog, only_integer: true, greater_than: -1, message: _('must be a positive number') + + attr_accessible :environment, :enabled, :periodicity, :subject, :posts_per_blog, :footer, :blog_ids, :additional_recipients, :person, :person_id, :moderated + + scope :enabled, :conditions => { :enabled => true } + + # These methods are used by NewsletterMailing + def people + list = unsubscribers.map{|i| "'#{i}'"}.join(',') + if list.empty? + environment.people + else + environment.people.all( + :joins => "LEFT OUTER JOIN users ON (users.id = profiles.user_id)", + :conditions => "users.email NOT IN (#{list})" + ) + end + end + + def name + environment.name + end + + def contact_email + environment.noreply_email + end + + def top_url + environment.top_url + end + + def unsubscribe_url + "#{top_url}/plugin/newsletter/unsubscribe" + end + + serialize :blog_ids, Array + serialize :additional_recipients, Array + + def blog_ids + self[:blog_ids].map(&:to_i) || [] + end + + validates_each :blog_ids do |record, attr, value| + if record.environment + unless value.delete_if(&:zero?).select { |id| !Blog.find_by_id(id) || Blog.find(id).environment != record.environment }.empty? + record.errors.add(attr, _('must be valid')) + end + end + unless value.uniq.length == value.length + record.errors.add(attr, _('must not have duplicates')) + end + end + + validates_each :additional_recipients do |record, attr, value| + unless value.reject { |recipient| recipient[:email] =~ Noosfero::Constants::EMAIL_FORMAT }.empty? + record.errors.add(attr, _('must have only valid emails')) + end + end + + def next_send_at + (self.last_send_at || DateTime.now) + self.periodicity.days + end + + def must_be_sent_today? + return true unless self.last_send_at + Date.today >= self.next_send_at.to_date + end + + def blogs + Blog.where(:id => blog_ids) + end + + def posts(data = {}) + limit = self.posts_per_blog.zero? ? nil : self.posts_per_blog + posts = if self.last_send_at.nil? + self.blogs.map{|blog| blog.posts.all(:limit => limit)}.flatten + else + self.blogs.map{|blog| blog.posts.where("published_at >= :last_send_at", {last_send_at: self.last_send_at}).all(:limit => limit)}.flatten + end + data[:post_ids].nil? ? posts : posts.select{|post| data[:post_ids].include?(post.id.to_s)} + end + + CSS = { + 'breakingnews-wrap' => 'background-color: #EFEFEF; padding: 40px 0', + 'breakingnews' => 'width: 640px; margin: auto; background-color: white; border: 1px solid #ddd; border-spacing: 0; padding: 0', + 'newsletter-public-link' => 'width: 640px; margin: auto; font-size: small; color: #555; font-style: italic; text-align: right; margin-bottom: 15px; font-family: sans;', + 'newsletter-header' => 'padding: 0', + 'header-image' => 'width: 100%', + 'post-image' => 'padding-left: 20px; width: 25%; border-bottom: 1px dashed #DDD', + 'post-info' => 'font-family: Arial, Verdana; padding: 20px; width: 75%; border-bottom: 1px dashed #DDD', + 'post-date' => 'font-size: 12px;', + 'post-lead' => 'font-size: 14px; text-align: justify', + 'post-title' => 'color: #000; text-decoration: none; font-size: 16px; text-align: justify', + 'read-more-line' => 'text-align: right', + 'read-more-link' => 'color: #000; font-size: 12px;', + 'newsletter-unsubscribe' => 'width: 640px; margin: auto; font-size: small; color: #555; font-style: italic; text-align: center; margin-top: 15px; font-family: sans;' + } + + # to be able to generate HTML + include ActionView::Helpers + include Rails.application.routes.url_helpers + include DatesHelper + + def message_to_public_link + content_tag(:p, N_("If you can't view this email, %s.") % link_to(N_('click here'), '{mailing_url}'), :id => 'newsletter-public-link') + end + + def message_to_unsubscribe + content_tag(:div, N_("This is an automatically generated email, please do not reply. If you do not wish to receive future newsletter emails, %s.") % link_to(N_("cancel your subscription here"), self.unsubscribe_url, :style => CSS['public-link']), :style => CSS['newsletter-unsubscribe'], :id => 'newsletter-unsubscribe') + end + + def read_more(link_address) + content_tag(:p, link_to(N_('Read more'), link_address, :style => CSS['read-more-link']), :style => CSS['read-more-line']) + end + + def post_with_image(post) + content_tag(:tr,content_tag(:td,tag(:img, :src => "#{self.environment.top_url}#{post.image.public_filename(:big)}", :id => post.id),:style => CSS['post-image'])+content_tag(:td,content_tag(:span, show_date(post.published_at), :style => CSS['post-date'])+content_tag(:h3, link_to(h(post.title), post.url, :style => CSS['post-title']))+content_tag(:p,sanitize(post.lead(190)),:style => CSS['post-lead'])+read_more(post.url), :style => CSS['post-info'])) + end + + def post_without_image(post) + content_tag(:tr, content_tag(:td,content_tag(:span, show_date(post.published_at),:style => CSS['post-date'], :id => post.id)+content_tag(:h3, link_to(h(post.title), post.url,:style => CSS['post-title']))+content_tag(:p,sanitize(post.lead(360)),:style => CSS['post-lead'])+read_more(post.url),:colspan => 2, :style => CSS['post-info'])) + end + + def body(data = {}) + content_tag(:div, content_tag(:div, message_to_public_link, :style => CSS['newsletter-public-link'])+content_tag(:table,(self.image.nil? ? '' : content_tag(:tr, content_tag(:th, tag(:img, :src => "#{self.environment.top_url}#{self.image.public_filename}", :style => CSS['header-image']),:colspan => 2),:style => CSS['newsletter-header']))+self.posts(data).map do |post| + if post.image + post_with_image(post) + else + post_without_image(post) + end + end.join()+content_tag(:tr, content_tag(:td, self.footer, :colspan => 2)),:style => CSS['breakingnews'])+content_tag(:div,message_to_unsubscribe, :style => CSS['newsletter-unsubscribe']),:style => CSS['breakingnews-wrap']) + end + + def default_subject + N_('Breaking news') + end + + def subject + self[:subject] || default_subject + end + + def import_recipients(file, name_column = nil, email_column = nil, headers = nil) + name_column ||= 1 + email_column ||= 2 + headers ||= false + + if File.extname(file.original_filename) == '.csv' + parsed_recipients = [] + CSV.foreach(file.path, headers: headers) do |row| + parsed_recipients << {name: row[name_column.to_i - 1], email: row[email_column.to_i - 1]} + end + self.additional_recipients = parsed_recipients + else + #FIXME find a better way to deal with errors + self.errors.add(:additional_recipients, _("have unknown file type: %s" % file.original_filename)) + end + end + + acts_as_having_image + + def last_send_at + last_mailing = NewsletterPlugin::NewsletterMailing.last( + :conditions => {:source_id => self.id} + ) + last_mailing.nil? ? nil : last_mailing.created_at + end + + def sanitize(html) + html.gsub(/<\/?p>/, '') + end + + def has_posts_in_the_period? + ! self.posts.empty? + end + + serialize :unsubscribers, Array + + def unsubscribe(email) + unsubscribers.push(email).uniq! + end + +end diff --git a/plugins/newsletter/lib/newsletter_plugin/newsletter_mailing.rb b/plugins/newsletter/lib/newsletter_plugin/newsletter_mailing.rb new file mode 100644 index 0000000..ad84e65 --- /dev/null +++ b/plugins/newsletter/lib/newsletter_plugin/newsletter_mailing.rb @@ -0,0 +1,26 @@ +class NewsletterPlugin::NewsletterMailing < EnvironmentMailing + + attr_accessible :source, :person, :locale + + validates_presence_of :person + + def url + "#{self.source.top_url}/plugin/newsletter/mailing/#{self.id}" + end + + def source + NewsletterPlugin::Newsletter.find(source_id) + end + + def deliver + source.additional_recipients.each do |recipient| + begin + Mailing::Sender.notification(self, recipient[:email]).deliver + rescue Exception => ex + Rails.logger.error("#{ex.class.to_s} - #{ex.to_s} at #{__FILE__}:#{__LINE__}") + end + end + super + end + +end diff --git a/plugins/newsletter/public/newsletter_plugin.js b/plugins/newsletter/public/newsletter_plugin.js new file mode 100644 index 0000000..78e231e --- /dev/null +++ b/plugins/newsletter/public/newsletter_plugin.js @@ -0,0 +1,21 @@ +jQuery(function($) { + $(".newsletter-toggle-link").live('click', function(){ + element_id = this.getAttribute('element_id'); + toggle_link = this; + $(element_id).slideToggle(400, function() { + if ($(toggle_link).find('.ui-icon').hasClass('ui-icon-triangle-1-s')) + $(toggle_link).find('.ui-icon') + .removeClass('ui-icon-triangle-1-s') + .addClass('ui-icon-triangle-1-n'); + else + $(toggle_link).find('.ui-icon') + .removeClass('ui-icon-triangle-1-n') + .addClass('ui-icon-triangle-1-s'); + }); + return false; + }); + + $('#file_recipients').change(function(){ + $('#newsletter-file-options input').enable(); + }); +}); diff --git a/plugins/newsletter/public/style.css b/plugins/newsletter/public/style.css new file mode 100644 index 0000000..2431c15 --- /dev/null +++ b/plugins/newsletter/public/style.css @@ -0,0 +1,24 @@ +.newsletter-toggle-link { + cursor: pointer; +} +#newsletter-footer-field { + display: none; +} +#newsletter-enabled-field input#settings_enabled { + margin-right: 6px; +} + +#newsletter-moderation-preview #newsletter-public-link, +#newsletter-moderation-preview #newsletter-unsubscribe { + display: none; +} + +#newsletter-moderation-preview { + margin-left: 25px; +} + +#newsletter-moderation-preview input[type=checkbox] { + margin-left: -27px; + margin-top: 16px; + float: left; +} diff --git a/plugins/newsletter/test/functional/newsletter_plugin_admin_controller_test.rb b/plugins/newsletter/test/functional/newsletter_plugin_admin_controller_test.rb new file mode 100644 index 0000000..d4db644 --- /dev/null +++ b/plugins/newsletter/test/functional/newsletter_plugin_admin_controller_test.rb @@ -0,0 +1,176 @@ +require File.dirname(__FILE__) + '/../../../../test/test_helper' + +class NewsletterPluginAdminControllerTest < ActionController::TestCase + + def setup + @controller = NewsletterPluginAdminController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + + @admin = create_user('admin_newsletter').person + @environment = @admin.environment + @environment.add_admin(@admin) + + @environment.enable_plugin(NewsletterPlugin) + @controller.stubs(:environment).returns(@environment) + end + + should 'allow access to admin' do + login_as @admin.identifier + get :index + assert_response :success + end + + should 'save footer setting' do + login_as @admin.identifier + post :index, + :newsletter => { :footer => 'footer of newsletter' } + + assert_equal 'footer of newsletter', assigns(:newsletter).footer + end + + + should 'save header image' do + login_as @admin.identifier + post :index, + :newsletter => { + :image_builder => { + :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png') + } + } + assert_equal 'rails.png', assigns(:newsletter).image.filename + end + + should 'save enabled newsletter information' do + login_as @admin.identifier + post :index, + :newsletter => { :enabled => 'true' } + + newsletter = NewsletterPlugin::Newsletter.find_by_environment_id(@environment.id) + + assert newsletter.enabled + end + + should 'save periodicity newsletter information' do + login_as @admin.identifier + post :index, + :newsletter => { :periodicity => '10' } + + newsletter = NewsletterPlugin::Newsletter.find_by_environment_id(@environment.id) + + assert_equal 10, newsletter.periodicity + end + + should 'save number of posts per blog setting' do + login_as @admin.identifier + post :index, + :newsletter => { :posts_per_blog => '6' } + + assert_equal 6, assigns(:newsletter).posts_per_blog + end + + should 'show error if number of posts per blog is not a positive number' do + login_as @admin.identifier + post :index, + :newsletter => { :posts_per_blog => '-4' } + + assert_select 'li', 'Posts per blog must be a positive number' + end + + should 'save blogs for compiling newsletter setting' do + login_as @admin.identifier + + blog1 = fast_create(Blog) + blog1.profile = fast_create(Profile, environment_id: @environment.id) + blog1.save + + blog2 = fast_create(Blog) + blog2.profile = fast_create(Profile, environment_id: @environment.id) + blog2.save + + post :index, + :newsletter => { :blog_ids => "#{blog1.id},#{blog2.id}" } + + assert_equivalent [blog1.id,blog2.id], assigns(:newsletter).blog_ids + end + + should 'show error if blog is not in environment' do + login_as @admin.identifier + + blog = fast_create(Blog) + blog.profile = fast_create(Profile, environment_id: fast_create(Environment).id) + blog.save + + post :index, + :newsletter => { :blog_ids => "#{blog.id}" } + + assert_select 'li', 'Blog ids must be valid' + end + + should 'save logged in admin as person' do + login_as @admin.identifier + post :index, :newsletter => { } + + assert_equal @admin, assigns(:newsletter).person + end + + should 'receive csv file from user' do + content = <<-EOS +Coop1,name1@example.com +Coop2,name2@example.com +Coop3,name3@example.com +EOS + + file = Tempfile.new(['recipients', '.csv']) + file.write(content) + file.rewind + + login_as @admin.identifier + post :index, newsletter: {}, :file => { recipients: Rack::Test::UploadedFile.new(file, 'text/csv') } + + file.close + file.unlink + + assert_equivalent ["name1@example.com", "name2@example.com", "name3@example.com"], assigns(:newsletter).additional_recipients.map { |recipient| recipient[:email] } + assert_equivalent ["Coop1", "Coop2", "Coop3"], assigns(:newsletter).additional_recipients.map { |recipient| recipient[:name] } + end + + should 'parse csv file with configuration set by user' do + content = <<-EOS +Id,Name,City,Email +1,Coop1,Moscow,name1@example.com +2,Coop2,Beijing,name2@example.com +3,Coop3,Paris,name3@example.com +EOS + + file = Tempfile.new(['recipients', '.csv']) + file.write(content) + file.rewind + + login_as @admin.identifier + post :index, newsletter: {}, :file => { recipients: Rack::Test::UploadedFile.new(file, 'text/csv'), headers: 1, name: 2, email: 4 } + + file.close + file.unlink + + assert_equivalent ["name1@example.com", "name2@example.com", "name3@example.com"], assigns(:newsletter).additional_recipients.map { |recipient| recipient[:email] } + assert_equivalent ["Coop1", "Coop2", "Coop3"], assigns(:newsletter).additional_recipients.map { |recipient| recipient[:name] } + end + + should 'list additional recipients' do + login_as @admin.identifier + get :recipients + assert_select 'p', 'There are no additional recipients.' + + newsletter = NewsletterPlugin::Newsletter.create!(environment: @environment, person: fast_create(Person)) + newsletter.additional_recipients = [ {name: 'Coop1', email: 'name1@example.com'} ] + newsletter.save! + + get :recipients + assert_select 'tr' do + assert_select 'td', 'Coop1' + assert_select 'td', 'name1@example.com' + end + end + +end diff --git a/plugins/newsletter/test/functional/newsletter_plugin_controller_test.rb b/plugins/newsletter/test/functional/newsletter_plugin_controller_test.rb new file mode 100644 index 0000000..a00442d --- /dev/null +++ b/plugins/newsletter/test/functional/newsletter_plugin_controller_test.rb @@ -0,0 +1,37 @@ +require File.dirname(__FILE__) + '/../../../../test/test_helper' + +class NewsletterPluginControllerTest < ActionController::TestCase + + def setup + @controller = NewsletterPluginController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + environment = fast_create(Environment) + environment.enable_plugin(NewsletterPlugin) + @controller.stubs(:environment).returns(environment) + end + + should 'require login to confirm unsubscription' do + post :confirm_unsubscription + assert_response 302 + end + + should 'open unsubscription page for anonymous' do + get :unsubscribe + assert_response :success + end + + should 'add user email from unsubscribers list' do + NewsletterPlugin::Newsletter.create!( + :environment => @controller.environment, + :person => fast_create(Person) + ) + maria = create_user("maria").person + login_as("maria") + post :confirm_unsubscription + assert_response :redirect + assert_redirected_to :controller => 'home' + assert_includes assigns(:newsletter).unsubscribers, maria.email + end + +end diff --git a/plugins/newsletter/test/unit/newsletter_plugin_moderate_newsletter_test.rb b/plugins/newsletter/test/unit/newsletter_plugin_moderate_newsletter_test.rb new file mode 100644 index 0000000..02de440 --- /dev/null +++ b/plugins/newsletter/test/unit/newsletter_plugin_moderate_newsletter_test.rb @@ -0,0 +1,50 @@ +require 'test_helper' + +class NewsletterPluginModerateNewsletterTest < ActiveSupport::TestCase + + should 'validates presence of newsletter_id' do + task = NewsletterPlugin::ModerateNewsletter.new + task.valid? + assert task.errors.include?(:newsletter_id) + + task.newsletter_id = 1 + task.valid? + refute task.errors.include?(:newsletter_id) + end + + should 'create mailing on perform' do + person = create_user('john').person + newsletter = NewsletterPlugin::Newsletter.create!(:environment => fast_create(Environment), :person => person, :enabled => true) + task = NewsletterPlugin::ModerateNewsletter.create!( + :newsletter_id => newsletter.id, + :target => newsletter.environment + ) + + assert_difference 'NewsletterPlugin::NewsletterMailing.count', 1 do + task.finish + end + end + + should 'set posts for mailing body on perform' do + person = create_user('john').person + blog = fast_create(Blog, profile_id: person.id) + post_1 = fast_create(TextileArticle, :name => 'First post', :profile_id => person.id, :parent_id => blog.id, :body => 'Test') + post_2 = fast_create(TextileArticle, :name => 'Second post', :profile_id => person.id, :parent_id => blog.id, :body => 'Test') + post_3 = fast_create(TextileArticle, :name => 'Third post', :profile_id => person.id, :parent_id => blog.id, :body => 'Test') + + newsletter = NewsletterPlugin::Newsletter.create!(:environment => person.environment, :person => person, :enabled => true) + newsletter.blog_ids = [blog.id] + newsletter.save! + + task = NewsletterPlugin::ModerateNewsletter.create!( + :newsletter_id => newsletter.id, + :target => newsletter.environment, + :post_ids => [post_1.id.to_s,post_2.id.to_s] + ) + + task.finish + assert_match /First post/, NewsletterPlugin::NewsletterMailing.last.body + assert_match /Second post/, NewsletterPlugin::NewsletterMailing.last.body + assert_not_match /Third post/, NewsletterPlugin::NewsletterMailing.last.body + end +end diff --git a/plugins/newsletter/test/unit/newsletter_plugin_newsletter_mailing_test.rb b/plugins/newsletter/test/unit/newsletter_plugin_newsletter_mailing_test.rb new file mode 100644 index 0000000..949c213 --- /dev/null +++ b/plugins/newsletter/test/unit/newsletter_plugin_newsletter_mailing_test.rb @@ -0,0 +1,72 @@ +require 'test_helper' + +class NewsletterPluginNewsletterMailingTest < ActiveSupport::TestCase + + def setup + ActionMailer::Base.delivery_method = :test + ActionMailer::Base.perform_deliveries = true + ActionMailer::Base.deliveries = [] + end + + should 'require source id' do + mailing = NewsletterPlugin::NewsletterMailing.new + mailing.valid? + assert mailing.errors[:source_id].any? + + mailing.source_id = NewsletterPlugin::Newsletter.create!(:environment => fast_create(Environment), :person => fast_create(Person)).id + mailing.valid? + refute mailing.errors[:source_id].any? + end + + should 'deliver mail from noreply environment email address' do + environment = fast_create(Environment, :noreply_email => 'noreply@localhost') + person = fast_create Person + newsletter = NewsletterPlugin::Newsletter.create!(:environment => environment, :person => person, :enabled => true) + mailing = NewsletterPlugin::NewsletterMailing.create!( + :source => newsletter, + :subject => newsletter.subject, + :body => newsletter.body, + :person => newsletter.person, + :locale => environment.default_locale, + ) + response = NewsletterPlugin::NewsletterMailing::Sender.notification(mailing, 'recipient@example.com').deliver + assert_equal 'noreply@localhost', response.from.join + end + + should 'also send to additional recipients' do + environment = fast_create(Environment, :name => 'Network') + person = create_user('betty', :environment_id => environment.id).person + newsletter = NewsletterPlugin::Newsletter.create!(:environment => environment, :person => person) + + newsletter.additional_recipients = [{name: 'example', email: 'exemple@mail.co'}, {name: 'jon', email: 'jonsnow@mail.co'}] + newsletter.save! + + mailing = NewsletterPlugin::NewsletterMailing.create!( + :source => newsletter, + :subject => newsletter.subject, + :body => newsletter.body, + :person => newsletter.person, + :locale => newsletter.environment.default_locale, + ) + + process_delayed_job_queue + assert_equal 3, ActionMailer::Base.deliveries.count + end + + should 'generate url to view mailing' do + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => fast_create(Environment), + :person => fast_create(Person), + :enabled => true + ) + mailing = NewsletterPlugin::NewsletterMailing.create!( + :source => newsletter, + :subject => newsletter.subject, + :body => newsletter.body, + :person => newsletter.person, + :locale => newsletter.environment.default_locale, + ) + assert_equal "http://localhost/plugin/newsletter/mailing/#{mailing.id}", mailing.url + end + +end diff --git a/plugins/newsletter/test/unit/newsletter_plugin_newsletter_test.rb b/plugins/newsletter/test/unit/newsletter_plugin_newsletter_test.rb new file mode 100644 index 0000000..94faefe --- /dev/null +++ b/plugins/newsletter/test/unit/newsletter_plugin_newsletter_test.rb @@ -0,0 +1,414 @@ +require 'test_helper' + +class NewsletterPluginNewsletterTest < ActiveSupport::TestCase + + should 'throws exception when try to create newsletters without reference do environment' do + assert_raises ActiveRecord::RecordInvalid do |e| + NewsletterPlugin::Newsletter.create! + assert_match /Profile can't be blank/, e.to_s + end + end + + should 'allow save only one newsletter by environment' do + environment = fast_create Environment + NewsletterPlugin::Newsletter.create!(:environment => environment, :person => fast_create(Person)) + assert_raises ActiveRecord::RecordInvalid do |e| + NewsletterPlugin::Newsletter.create!(:environment => environment, :person => fast_create(Person)) + assert_match /Profile has already been taken/, e.to_s + end + end + + should 'collect enabled newsletters' do + enabled_newsletters = [] + 5.times do + environment = fast_create(Environment) + enabled = environment.id % 2 == 0 + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => environment, + :enabled => enabled, + :person => fast_create(Person)) + enabled_newsletters << newsletter.id if enabled + end + assert_equal enabled_newsletters, NewsletterPlugin::Newsletter.enabled.map(&:id) + end + + should 'people of newsletters are the same environment members' do + 3.times do + environment = fast_create(Environment) + 3.times do + fast_create(Person, environment_id: environment) + end + NewsletterPlugin::Newsletter.create!( + :environment => environment, + :enabled => true, + :person => fast_create(Person)) + end + NewsletterPlugin::Newsletter.enabled.each do |newsletter| + assert_not_equal [], newsletter.people + assert_equal newsletter.environment.people, newsletter.people + end + end + + should 'save period for newsletter' do + environment = fast_create Environment + NewsletterPlugin::Newsletter.create!( + :environment => environment, + :periodicity => '3', + :person => fast_create(Person)) + + assert_equal 3, NewsletterPlugin::Newsletter.find_by_environment_id(environment.id).periodicity + end + + should 'save period as number only' do + environment = fast_create Environment + assert_raises ActiveRecord::RecordInvalid do |e| + NewsletterPlugin::Newsletter.create!(:environment => environment, :periodicity => 'one week' ) + assert_match /Periodicity must be a positive number/, e.to_s + end + end + + should 'save period as a positive number only' do + environment = fast_create Environment + assert_raises ActiveRecord::RecordInvalid do |e| + NewsletterPlugin::Newsletter.create!(:environment => environment, :periodicity => -1 ) + assert_match /Periodicity must be a positive number/, e.to_s + end + end + + should 'save reference to environment blog' do + environment = fast_create Environment + blog = fast_create(Blog) + blog.profile = fast_create(Profile, environment_id: environment.id) + blog.save + assert_nothing_raised ActiveRecord::RecordInvalid do + NewsletterPlugin::Newsletter.create!( + :environment => environment, + :blog_ids => [blog.id], + :person => fast_create(Person)) + end + end + + should 'not save reference to unknown blog' do + environment = fast_create Environment + blog = fast_create(Blog) + blog.profile = fast_create(Profile, environment_id: fast_create(Environment).id) + blog.save + assert_raises ActiveRecord::RecordInvalid do |e| + NewsletterPlugin::Newsletter.create!(:environment => environment, :blog_ids => [blog.id]) + assert_match /Blog ids must be valid/, e.to_s + end + assert_raises ActiveRecord::RecordInvalid do |e| + NewsletterPlugin::Newsletter.create!(:environment => environment, :blog_ids => [blog.id*2]) + assert_match /Blog ids must be valid/, e.to_s + end + end + + should 'not save duplicates for blog ids' do + environment = fast_create Environment + blog = fast_create(Blog) + blog.profile = fast_create(Profile, environment_id: environment.id) + blog.save + assert_raises ActiveRecord::RecordInvalid do |e| + NewsletterPlugin::Newsletter.create!(:environment => environment, :blog_ids => [blog.id, blog.id]) + assert_match /Blog ids must not have duplicates/, e.to_s + end + end + + should "not send newsletters if periodicity isn't expired" do + newsletter = NewsletterPlugin::Newsletter.new + newsletter.periodicity = 10 + newsletter.stubs(:last_send_at).returns(DateTime.parse("2015-01-01")) + Date.stubs(:today).returns(Date.parse("2015-01-07")) + assert_equal false, newsletter.must_be_sent_today? + end + + should 'send newsletters when periodicity expires' do + newsletter = NewsletterPlugin::Newsletter.new + newsletter.periodicity = 10 + newsletter.stubs(:last_send_at).returns(DateTime.parse("2015-01-01")) + Date.stubs(:today).returns(Date.parse("2015-01-15")) + assert_equal true, newsletter.must_be_sent_today? + end + + should 'send now if never send before' do + newsletter = NewsletterPlugin::Newsletter.new(:environment => fast_create(Environment)) + newsletter.periodicity = 10 + assert newsletter.must_be_sent_today? + end + + should 'validate email format for additional recipients' do + environment = fast_create Environment + assert_raises ActiveRecord::RecordInvalid do |e| + NewsletterPlugin::Newsletter.create!(:environment => environment, :person => fast_create(Person), additional_recipients: [{name: 'Cooperative', email: 'cooperative@example'}]) + assert_match /Additional recipients must have only valid emails/, e.to_s + end + assert_nothing_raised ActiveRecord::RecordInvalid do |e| + NewsletterPlugin::Newsletter.create!(:environment => environment, :person => fast_create(Person), additional_recipients: [{name: 'Cooperative', email: 'cooperative@example.com'}]) + end + end + + should 'parse additional recipients' do + content = <<-EOS +Coop1,name1@example.com +Coop2,name2@example.com +Coop3,name3@example.com +EOS + + file = Tempfile.new(['recipients', '.csv']) + file.write(content) + file.rewind + + environment = fast_create Environment + newsletter = NewsletterPlugin::Newsletter.create!(:environment => environment, :person => fast_create(Person)) + newsletter.import_recipients(Rack::Test::UploadedFile.new(file, 'text/csv')) + + file.close + file.unlink + + assert_equivalent ["name1@example.com", "name2@example.com", "name3@example.com"], newsletter.additional_recipients.map { |recipient| recipient[:email] } + assert_equivalent ["Coop1", "Coop2", "Coop3"], newsletter.additional_recipients.map { |recipient| recipient[:name] } + end + + should 'only parse csv files' do + content = <<-EOS +Coop1,name1@example.com +Coop2,name2@example.com +Coop3,name3@example.com +EOS + + file = Tempfile.new(['recipients', '.txt']) + file.write(content) + file.rewind + + environment = fast_create Environment + newsletter = NewsletterPlugin::Newsletter.create!(:environment => environment, :person => fast_create(Person)) + newsletter.import_recipients(Rack::Test::UploadedFile.new(file)) + + file.close + file.unlink + + assert_equal [], newsletter.additional_recipients + assert_match /Additional recipients have unknown file type.*/, newsletter.errors.full_messages[0] + end + + should 'parse additional recipients with given column number and header' do + content = <<-EOS +Id,Name,City,Email +1,Coop1,Moscow,name1@example.com +2,Coop2,Beijing,name2@example.com +3,Coop3,Paris,name3@example.com +EOS + + file = Tempfile.new(['recipients', '.csv']) + file.write(content) + file.rewind + + environment = fast_create Environment + newsletter = NewsletterPlugin::Newsletter.create!(:environment => environment, :person => fast_create(Person)) + newsletter.import_recipients(Rack::Test::UploadedFile.new(file, 'text/csv'), 2, 4, true) + + file.close + file.unlink + + assert_equivalent ["name1@example.com", "name2@example.com", "name3@example.com"], newsletter.additional_recipients.map { |recipient| recipient[:email] } + assert_equivalent ["Coop1", "Coop2", "Coop3"], newsletter.additional_recipients.map { |recipient| recipient[:name] } + end + + should 'retrieve blogs related to newsletter' do + environment = fast_create Environment + community = fast_create(Community, :environment_id => environment.id) + blog1 = fast_create(Blog, :profile_id => community.id) + blog2 = fast_create(Blog, :profile_id => community.id) + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => environment, :blog_ids => [blog1.id, blog2.id], :person => fast_create(Person) + ) + assert_equivalent [blog1, blog2], newsletter.blogs + end + + should 'return empty if has no related blogs' do + environment = fast_create Environment + newsletter = NewsletterPlugin::Newsletter.create!(:environment => environment, :person => fast_create(Person)) + assert_empty newsletter.blogs + end + + should 'list posts for all selected blogs' do + environment = fast_create Environment + community = fast_create(Community, :environment_id => environment.id) + blog = fast_create(Blog, :profile_id => community.id) + post = fast_create(TextArticle, :parent_id => blog.id, :name => 'the last news') + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => environment, + :blog_ids => [blog.id], + :person => fast_create(Person)) + assert_includes newsletter.posts, post + end + + should 'generate HTML content using posts of selected blogs' do + environment = fast_create Environment + community = fast_create(Community, :environment_id => environment.id) + blog = fast_create(Blog, :profile_id => community.id) + fast_create(TextArticle, :profile_id => community.id, :parent_id => blog.id, :name => 'the last news') + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => environment, + :blog_ids => [blog.id], + :person => fast_create(Person)) + assert_tag_in_string newsletter.body, :tag => 'a', :content => 'the last news' + end + + should 'limit the number of posts per blog' do + environment = fast_create Environment + community = fast_create(Community, :environment_id => environment.id) + blog = fast_create(Blog, :profile_id => community.id) + fast_create(TextArticle, :parent_id => blog.id, :name => 'the last news 1') + fast_create(TextArticle, :parent_id => blog.id, :name => 'the last news 2') + fast_create(TextArticle, :parent_id => blog.id, :name => 'the last news 3') + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => environment, + :blog_ids => [blog.id], + :person => fast_create(Person), + :posts_per_blog => 2) + assert_equal 2, newsletter.posts.count + end + + should 'include all posts before today' do + environment = fast_create Environment + community = fast_create(Community, :environment_id => environment.id) + blog = fast_create(Blog, :profile_id => community.id) + + post1 = fast_create(TextArticle, :parent_id => blog.id, :name => 'the last news 1', + :published_at => DateTime.parse("2015-01-01")) + post2 = fast_create(TextArticle, :parent_id => blog.id, :name => 'the last news 2', + :published_at => DateTime.parse("2015-01-09")) + + Date.stubs(:today).returns(DateTime.parse("2015-01-10").to_date) + + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => environment, + :blog_ids => [blog.id], + :person => fast_create(Person)) + + newsletter_posts = newsletter.posts + assert_includes newsletter_posts, post1 + assert_includes newsletter_posts, post2 + end + + should 'not include posts already sent' do + environment = fast_create Environment + community = fast_create(Community, :environment_id => environment.id) + blog = fast_create(Blog, :profile_id => community.id) + + post1 = fast_create(TextArticle, :parent_id => blog.id, :name => 'the last news 1', + :published_at => DateTime.parse("2015-01-01")) + post2 = fast_create(TextArticle, :parent_id => blog.id, :name => 'the last news 2', + :published_at => DateTime.parse("2015-01-09")) + + Date.stubs(:today).returns(DateTime.parse("2015-01-10").to_date) + + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => environment, + :blog_ids => [blog.id], + :person => fast_create(Person)) + newsletter.stubs(:last_send_at).returns(DateTime.parse("2015-01-05")) + + newsletter_posts = newsletter.posts + assert_not_includes newsletter_posts, post1 + assert_includes newsletter_posts, post2 + end + + should 'sanitize tags
from news lead' do + environment = fast_create Environment + community = fast_create(Community, :environment_id => environment.id) + blog = fast_create(Blog, :profile_id => community.id) + post = fast_create(TextArticle, :parent_id => blog.id, + :name => 'the last news 1', + :profile_id => community.id, + :body => "
paragraph of news
") + + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => environment, + :blog_ids => [blog.id], + :person => fast_create(Person)) + + assert_match /paragraph of news<\/p>/, post.body + assert_not_match /
paragraph of news<\/p>/, newsletter.body + end + + should 'filter posts when listing posts for newsletter' do + person = fast_create(Person) + blog = fast_create(Blog, profile_id: person.id) + + post_1 = fast_create(TextileArticle, :name => 'First post', :profile_id => person.id, :parent_id => blog.id, :body => 'Test') + post_2 = fast_create(TextileArticle, :name => 'Second post', :profile_id => person.id, :parent_id => blog.id, :body => 'Test') + post_3 = fast_create(TextileArticle, :name => 'Third post', :profile_id => person.id, :parent_id => blog.id, :body => 'Test') + + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => person.environment, + :blog_ids => [blog.id], + :person => person) + + assert_equivalent [post_2.id, post_3.id], newsletter.posts({post_ids: [post_2.id.to_s, post_3.id.to_s]}).map(&:id) + end + + should 'filter posts in body for newsletter' do + person = fast_create(Person) + blog = fast_create(Blog, profile_id: person.id) + + post_1 = fast_create(TextileArticle, :name => 'First post', :profile_id => person.id, :parent_id => blog.id, :body => 'Test') + post_2 = fast_create(TextileArticle, :name => 'Second post', :profile_id => person.id, :parent_id => blog.id, :body => 'Test') + post_3 = fast_create(TextileArticle, :name => 'Third post', :profile_id => person.id, :parent_id => blog.id, :body => 'Test') + + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => person.environment, + :blog_ids => [blog.id], + :person => person) + + assert_match /First post/, NewsletterPlugin::Newsletter.last.body({post_ids: [post_1.id.to_s, post_3.id.to_s]}) + assert_not_match /Second post/, NewsletterPlugin::Newsletter.last.body({post_ids: [post_1.id.to_s, post_3.id.to_s]}) + assert_match /Third post/, NewsletterPlugin::Newsletter.last.body({post_ids: [post_1.id.to_s, post_3.id.to_s]}) + end + + should 'add email to unsubscribers list' do + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => fast_create(Environment), + :person => fast_create(Person) + ) + newsletter.unsubscribe("ze@localhost.localdomain") + assert_includes newsletter.unsubscribers, "ze@localhost.localdomain" + end + + should 'not add same email twice to unsubscribers list' do + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => fast_create(Environment), + :person => fast_create(Person) + ) + newsletter.unsubscribe("ze@localhost.localdomain") + newsletter.unsubscribe("ze@localhost.localdomain") + assert_equal ["ze@localhost.localdomain"], newsletter.unsubscribers + end + + should "filter newsletter's recipients using unsubscribers list" do + environment = fast_create Environment + p1 = create_user("person1", :environment_id => environment.id).person + p2 = create_user("person2", :environment_id => environment.id).person + p3 = create_user("person3", :environment_id => environment.id).person + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => environment, + :person => fast_create(Person) + ) + newsletter.unsubscribe(p2.email) + assert_equivalent [p1, p3], newsletter.people + end + + should "no filter newsletter's recipients if unsubscribers list empty" do + environment = fast_create Environment + p1 = create_user("person1", :environment_id => environment.id).person + p2 = create_user("person2", :environment_id => environment.id).person + p3 = create_user("person3", :environment_id => environment.id).person + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => environment, + :person => fast_create(Person) + ) + assert_equivalent [p1, p2, p3], newsletter.people + end + +end diff --git a/plugins/newsletter/test/unit/newsletter_plugin_test.rb b/plugins/newsletter/test/unit/newsletter_plugin_test.rb new file mode 100644 index 0000000..a33074a --- /dev/null +++ b/plugins/newsletter/test/unit/newsletter_plugin_test.rb @@ -0,0 +1,113 @@ +require 'test_helper' + +class NewsletterPluginTest < ActiveSupport::TestCase + + def setup + NewsletterPlugin::Newsletter.any_instance.stubs(:must_be_sent_today?).returns(true) + NewsletterPlugin::Newsletter.any_instance.stubs(:has_posts_in_the_period?).returns(true) + end + + should 'update newsletter send date only for enabled newsletters' do + newsletter_enabled = NewsletterPlugin::Newsletter.create!( + :environment => fast_create(Environment), + :enabled => true, + :subject => 'newsletter test', + :person => fast_create(Person)) + + newsletter_disabled = NewsletterPlugin::Newsletter.create!( + :environment => fast_create(Environment), + :enabled => false, + :subject => 'newsletter test', + :person => fast_create(Person)) + + NewsletterPlugin.compile_and_send_newsletters + + newsletter_enabled.reload + newsletter_disabled.reload + + assert_not_nil newsletter_enabled.last_send_at + assert_nil newsletter_disabled.last_send_at + end + + should 'create and schedule newsletter mailing if not moderated' do + NewsletterPlugin::Newsletter.create!( + :environment => fast_create(Environment), + :enabled => true, + :moderated => false, + :subject => 'newsletter test', + :person => fast_create(Person)) + + assert_difference 'NewsletterPlugin::NewsletterMailing.count', 1 do + NewsletterPlugin.compile_and_send_newsletters + end + + assert_equal 0, NewsletterPlugin::ModerateNewsletter.count + end + + should 'use same environment locale on mailing' do + NewsletterPlugin::Newsletter.create!( + :environment => fast_create(Environment, :default_language => 'pt_BR'), + :enabled => true, + :subject => 'newsletter test', + :person => fast_create(Person)) + + NewsletterPlugin.compile_and_send_newsletters + assert_equal 'pt_BR', NewsletterPlugin::NewsletterMailing.last.locale + end + + should 'create newsletter moderation task if newsletter is moderated' do + adminuser = create_user.person + Environment.any_instance.stubs(:admins).returns([adminuser]) + + NewsletterPlugin::Newsletter.create!( + :environment => fast_create(Environment), + :enabled => true, + :moderated => true, + :subject => 'newsletter test', + :person => fast_create(Person)) + + assert_difference 'NewsletterPlugin::ModerateNewsletter.count', 1 do + NewsletterPlugin.compile_and_send_newsletters + end + + assert_equal 0, NewsletterPlugin::NewsletterMailing.count + end + + should 'not create mailing if has no posts in the period' do + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => fast_create(Environment), + :person => fast_create(Person), + :enabled => true + ) + NewsletterPlugin::Newsletter.any_instance.stubs(:must_be_sent_today?).returns(true) + NewsletterPlugin::Newsletter.any_instance.stubs(:has_posts_in_the_period?).returns(false) + assert_no_difference 'NewsletterPlugin::NewsletterMailing.count' do + NewsletterPlugin.compile_and_send_newsletters + end + end + + should 'not create mailing if doesnt must be sent today' do + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => fast_create(Environment), + :person => fast_create(Person), + :enabled => true + ) + NewsletterPlugin::Newsletter.any_instance.stubs(:must_be_sent_today?).returns(false) + NewsletterPlugin::Newsletter.any_instance.stubs(:has_posts_in_the_period?).returns(true) + assert_no_difference 'NewsletterPlugin::NewsletterMailing.count' do + NewsletterPlugin.compile_and_send_newsletters + end + end + + should 'create mailing' do + newsletter = NewsletterPlugin::Newsletter.create!( + :environment => fast_create(Environment), + :person => fast_create(Person), + :enabled => true + ) + assert_difference 'NewsletterPlugin::NewsletterMailing.count' do + NewsletterPlugin.compile_and_send_newsletters + end + end + +end diff --git a/plugins/newsletter/views/newsletter_plugin/mailing_not_found.html.erb b/plugins/newsletter/views/newsletter_plugin/mailing_not_found.html.erb new file mode 100644 index 0000000..96ee4e8 --- /dev/null +++ b/plugins/newsletter/views/newsletter_plugin/mailing_not_found.html.erb @@ -0,0 +1,3 @@ +
<%= _("There is no mailing with id #%s") % params[:id] %>
diff --git a/plugins/newsletter/views/newsletter_plugin/unsubscribe.html.erb b/plugins/newsletter/views/newsletter_plugin/unsubscribe.html.erb new file mode 100644 index 0000000..39bc0e5 --- /dev/null +++ b/plugins/newsletter/views/newsletter_plugin/unsubscribe.html.erb @@ -0,0 +1,11 @@ +<%= _('Blogs from which news will be compiled') %>
+ <% search_action = url_for(:action => 'search_communities') %> + <% selected_blogs = @blogs.map { |blog| {:id => blog.id, :name => _("%s in %s") % [blog.name, blog.profile.name]} } %> + <%= token_input_field_tag( + 'newsletter[blog_ids]', 'search-communities', search_action, + { hint_text: _('Type in the communities\' or blogs\' names'), + focus: false, pre_populate: selected_blogs }) %> + ++ <%= _('You can follow the link below to see which e-mails are currently being used as additional recipients for this newsletter.') %> +
++ <%= link_to 'Currently set additional recipients', {action: :recipients}, target: '_blank' %> +
+ +<%= _('You can set additional e-mails to send the newsletter to in addition to all environment\'s users that already receive the newsletter by default. To do that, you need to upload a CSV file that contains a column for the person\'s or enterprise\'s name as well as a column with their e-mail.') %>
+ + <%= labelled_form_field( + _('Additional recipients for newsletter'), file_field_tag('file[recipients]', accept: '.csv')) + %> + + + +<%= _('Name') %> | +<%= _('E-mail') %> | +
---|---|
<%= recipient[:name] %> | +<%= recipient[:email] %> | +
<%= _('There are no additional recipients.') %>
+<% end %> + +