Commit d3b8064fb95349a37aea58f43727aac204462b6f

Authored by Rodrigo Souto
2 parents 0eba5453 a499eda4

Merge branch 'master' into api

Showing 67 changed files with 661 additions and 274 deletions   Show diff stats
.gitlab-ci.yml 0 → 100644
... ... @@ -0,0 +1,23 @@
  1 +before_script:
  2 + - mkdir -p tmp/pids log
  3 + - script/noosfero-plugins disableall
  4 + - bundle check || bundle install
  5 +# database
  6 + - cp config/database.yml.gitlab-ci config/database.yml
  7 + - createdb gitlab_ci_test || true
  8 + - bundle exec rake db:schema:load
  9 + - bundle exec rake db:migrate
  10 +
  11 +units:
  12 + script: 'bundle exec rake test:units'
  13 +functionals:
  14 + script: 'bundle exec rake test:functionals'
  15 +integration:
  16 + script: 'bundle exec rake test:integration'
  17 +cucumber:
  18 + script: 'bundle exec rake cucumber'
  19 +selenium:
  20 + script: 'bundle exec rake selenium'
  21 +plugins:
  22 + script: 'bundle exec rake test:noosfero_plugins'
  23 +
... ...
.travis.yml 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +language: ruby
  2 +rvm:
  3 +# for 2.2 support we need to upgrade the pg gem
  4 + - 2.1.6
  5 +
  6 +before_install:
  7 +# dependencies
  8 + - sudo apt-get update
  9 + - sudo apt-get -y install po4a iso-codes tango-icon-theme pidgin-data openjdk-6-jre curl wget
  10 + - sudo apt-get -y install libmagickwand-dev libpq-dev libreadline-dev libsqlite3-dev libxslt1-dev
  11 +# selenium support
  12 + - export DISPLAY=:99.0
  13 + - sh -e /etc/init.d/xvfb start
  14 +
  15 +before_script:
  16 + - mkdir -p tmp/pids log
  17 + - script/noosfero-plugins disableall
  18 + - bundle check || bundle install
  19 +# database
  20 + - cp config/database.yml.travis config/database.yml
  21 + - psql -c 'create database myapp_test;' -U postgres
  22 + - bundle exec rake db:schema:load
  23 + - bundle exec rake db:migrate
  24 +
  25 +env:
  26 + - TASK=test:units
  27 + - TASK=test:functionals
  28 + - TASK=test:integration
  29 + - TASK=cucumber
  30 + - TASK=selenium
  31 + - TASK=test:noosfero_plugins
  32 +
  33 +script:
  34 + - bundle exec rake $TASK
  35 +
... ...
Gemfile
1 1 source "https://rubygems.org"
2   -gem 'rails', '~> 3.2.21'
  2 +gem 'rails', '~> 3.2.22'
3 3 gem 'minitest', '~> 3.2.0'
4 4 gem 'fast_gettext', '~> 0.6.8'
5 5 gem 'acts-as-taggable-on', '~> 3.4.2'
... ... @@ -44,6 +44,7 @@ group :test do
44 44 gem 'rspec', '~> 2.14.0'
45 45 gem 'rspec-rails', '~> 2.14.1'
46 46 gem 'mocha', '~> 1.1.0', :require => false
  47 + gem 'test-unit' if RUBY_VERSION >= '2.2.0'
47 48 end
48 49  
49 50 group :cucumber do
... ...
app/controllers/my_profile/cms_controller.rb
... ... @@ -94,6 +94,11 @@ class CmsController < MyProfileController
94 94 record_coming
95 95 if request.post?
96 96 @article.image = nil if params[:remove_image] == 'true'
  97 + if @article.image.present? && params[:article][:image_builder] &&
  98 + params[:article][:image_builder][:label]
  99 + @article.image.label = params[:article][:image_builder][:label]
  100 + @article.image.save!
  101 + end
97 102 @article.last_changed_by = user
98 103 if @article.update_attributes(params[:article])
99 104 if !continue
... ...
app/controllers/my_profile/manage_products_controller.rb
... ... @@ -35,7 +35,7 @@ class ManageProductsController < ApplicationController
35 35 end
36 36  
37 37 def categories_for_selection
38   - @category = Category.find(params[:category_id]) if params[:category_id]
  38 + @category = environment.categories.find_by_id params[:category_id]
39 39 @object_name = params[:object_name]
40 40 if @category
41 41 @categories = @category.children
... ... @@ -95,6 +95,20 @@ class ManageProductsController < ApplicationController
95 95 end
96 96 end
97 97  
  98 + def show_category_tree
  99 + @category = environment.categories.find params[:category_id]
  100 + render :partial => 'selected_category_tree'
  101 + end
  102 +
  103 + def search_categories
  104 + @term = params[:term].downcase
  105 + conditions = ['LOWER(name) LIKE ? OR LOWER(name) LIKE ?', "#{@term}%", "% #{@term}%"]
  106 + @categories = ProductCategory.all :conditions => conditions, :limit => 10
  107 + render :json => (@categories.map do |category|
  108 + {:label => category.name, :value => category.id}
  109 + end)
  110 + end
  111 +
98 112 def add_input
99 113 @product = @profile.products.find(params[:id])
100 114 @input = @product.inputs.build
... ...
app/controllers/my_profile/maps_controller.rb
... ... @@ -16,6 +16,7 @@ class MapsController < MyProfileController
16 16  
17 17 Profile.transaction do
18 18 if profile.update_attributes!(params[:profile_data])
  19 + BlockSweeper.expire_blocks profile.blocks.select{ |b| b.class == LocationBlock }
19 20 session[:notice] = _('Address was updated successfully!')
20 21 redirect_to :action => 'edit_location'
21 22 end
... ...
app/helpers/application_helper.rb
... ... @@ -1185,7 +1185,7 @@ module ApplicationHelper
1185 1185 pending_tasks_count = link_to(count.to_s, user.tasks_url, :id => 'pending-tasks-count', :title => _("Manage your pending tasks"))
1186 1186 end
1187 1187  
1188   - (_("<span class='welcome'>Welcome,</span> %s") % link_to("<i style='background-image:url(#{user.profile_custom_icon(gravatar_default)})'></i><strong>#{user.identifier}</strong>", user.public_profile_url, :id => "homepage-link", :title => _('Go to your homepage'))) +
  1188 + (_("<span class='welcome'>Welcome,</span> %s") % link_to("<i style='background-image:url(#{user.profile_custom_icon(gravatar_default)})'></i><strong>#{user.identifier}</strong>", user.url, :id => "homepage-link", :title => _('Go to your homepage'))) +
1189 1189 render_environment_features(:usermenu) +
1190 1190 admin_link +
1191 1191 manage_enterprises +
... ... @@ -1233,7 +1233,7 @@ module ApplicationHelper
1233 1233  
1234 1234 def task_information(task)
1235 1235 values = {}
1236   - values.merge!({:requestor => link_to(task.requestor.name, task.requestor.public_profile_url)}) if task.requestor
  1236 + values.merge!({:requestor => link_to(task.requestor.name, task.requestor.url)}) if task.requestor
1237 1237 values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject
1238 1238 values.merge!({:linked_subject => link_to(content_tag('span', task.linked_subject[:text], :class => 'task_target'), task.linked_subject[:url])}) if task.linked_subject
1239 1239 values.merge!(task.information[:variables]) if task.information[:variables]
... ...
app/helpers/comment_helper.rb
... ... @@ -16,7 +16,7 @@ module CommentHelper
16 16 content_tag('span', show_date(article.published_at), :class => 'date') +
17 17 content_tag('span', [_(", by %s") % link_to(article.author_name, article.author_url)], :class => 'author') +
18 18 content_tag('span', comments, :class => 'comments'),
19   - :class => 'created-at'
  19 + :class => 'publishing-info'
20 20 )
21 21 end
22 22 title
... ...
app/helpers/content_viewer_helper.rb
... ... @@ -30,7 +30,7 @@ module ContentViewerHelper
30 30 date_format +
31 31 content_tag('span', _(", by %s") % (article.author ? link_to(article.author_name, article.author_url) : article.author_name), :class => 'author') +
32 32 content_tag('span', comments, :class => 'comments'),
33   - :class => 'created-at'
  33 + :class => 'publishing-info'
34 34 )
35 35 end
36 36 title
... ...
app/helpers/folder_helper.rb
1   -require 'short_filename'
2   -
3 1 module FolderHelper
4 2  
5   - include ShortFilename
6 3 include ArticleHelper
7 4  
8 5 def list_contents(configure={})
... ... @@ -10,8 +7,8 @@ module FolderHelper
10 7 configure[:list_type] ||= :folder
11 8 if !configure[:contents].blank?
12 9 configure[:contents] = configure[:contents].paginate(
13   - :order => "updated_at DESC",
14   - :per_page => 10,
  10 + :order => "name ASC",
  11 + :per_page => 30,
15 12 :page => params[:npage]
16 13 )
17 14  
... ...
app/helpers/manage_products_helper.rb
... ... @@ -75,9 +75,12 @@ module ManageProductsHelper
75 75 end
76 76  
77 77 def categories_container(categories_selection_html, hierarchy_html = '')
78   - hidden_field_tag('selected_category_id') +
79   - content_tag('div', hierarchy_html, :id => 'hierarchy_navigation') +
80   - content_tag('div', categories_selection_html, :id => 'categories_container_wrapper')
  78 + content_tag 'div',
  79 + render('categories_autocomplete') +
  80 + hidden_field_tag('selected_category_id') +
  81 + content_tag('div', hierarchy_html, :id => 'hierarchy_navigation') +
  82 + content_tag('div', categories_selection_html, :id => 'categories_container_wrapper'),
  83 + :id => 'categories-container'
81 84 end
82 85  
83 86 def select_for_categories(categories, level = 0)
... ...
app/models/article.rb
... ... @@ -736,8 +736,9 @@ class Article &lt; ActiveRecord::Base
736 736 paragraphs.empty? ? '' : paragraphs.first.to_html
737 737 end
738 738  
739   - def lead
740   - abstract.blank? ? first_paragraph.html_safe : abstract.html_safe
  739 + def lead(length = nil)
  740 + content = abstract.blank? ? first_paragraph.html_safe : abstract.html_safe
  741 + length.present? ? content.truncate(length) : content
741 742 end
742 743  
743 744 def short_lead
... ...
app/models/event.rb
... ... @@ -98,47 +98,19 @@ class Event &lt; Article
98 98 start_date..(end_date||start_date)
99 99 end
100 100  
101   - # FIXME this shouldn't be needed
102   - include ActionView::Helpers::TagHelper
103   - include ActionView::Helpers::UrlHelper
104   - include DatesHelper
  101 + def first_paragraph
  102 + paragraphs = Nokogiri::HTML.fragment(self.body).css('p')
  103 + paragraphs.empty? ? '' : paragraphs.first.to_html
  104 + end
105 105  
106 106 def to_html(options = {})
  107 + event = self
  108 + format = options[:format]
107 109  
108   - result = ''
109   - html = ::Builder::XmlMarkup.new(:target => result)
110   -
111   - html.div(:class => 'event-info' ) {
112   - html.ul(:class => 'event-data' ) {
113   - html.li(:class => 'event-dates' ) {
114   - html.span _('When:')
115   - html.text! show_period(start_date, end_date)
116   - } if start_date.present? || end_date.present?
117   - html.li {
118   - html.span _('URL:')
119   - html.a(self.link || "", 'href' => self.link || "")
120   - } if self.link.present?
121   - html.li {
122   - html.span _('Address:')
123   - html.text! self.address || ""
124   - } if self.address.present?
125   - }
126   -
127   - # TODO: some good soul, please clean this ugly hack:
128   - if self.body
129   - html.div('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', :class => 'event-description')
130   - end
131   - }
132   -
133   - if self.body
134   - if options[:format] == 'short'
135   - result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', display_short_format(self))
136   - else
137   - result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', self.body)
138   - end
  110 + proc do
  111 + render :file => 'content_viewer/event_page', :locals => { :event => event,
  112 + :format => format }
139 113 end
140   -
141   - result
142 114 end
143 115  
144 116 def duration
... ...
app/models/image.rb
... ... @@ -23,7 +23,7 @@ class Image &lt; ActiveRecord::Base
23 23  
24 24 postgresql_attachment_fu
25 25  
26   - attr_accessible :uploaded_data
  26 + attr_accessible :uploaded_data, :label
27 27  
28 28 def current_data
29 29 File.file?(full_filename) ? File.read(full_filename) : nil
... ...
app/models/organization.rb
... ... @@ -171,6 +171,12 @@ class Organization &lt; Profile
171 171 ]
172 172 end
173 173  
  174 + def short_name chars = 40
  175 + s = self.display_name
  176 + s = super(chars) if s.blank?
  177 + s
  178 + end
  179 +
174 180 def notification_emails
175 181 emails = [contact_email].select(&:present?) + admins.map(&:email)
176 182 if emails.empty?
... ...
app/models/uploaded_file.rb
1   -require 'short_filename'
2   -
3 1 # Article type that handles uploaded files.
4 2 #
5 3 # Limitation: only file metadata are versioned. Only the latest version
... ... @@ -14,8 +12,6 @@ class UploadedFile &lt; Article
14 12  
15 13 track_actions :upload_image, :after_create, :keep_params => ["view_url", "thumbnail_path", "parent.url", "parent.name"], :if => Proc.new { |a| a.published? && a.image? && !a.parent.nil? && a.parent.gallery? }, :custom_target => :parent
16 14  
17   - include ShortFilename
18   -
19 15 def title
20 16 if self.name.present? then self.name else self.filename end
21 17 end
... ...
app/models/user.rb
... ... @@ -332,6 +332,8 @@ class User &lt; ActiveRecord::Base
332 332  
333 333 {
334 334 'login' => self.login,
  335 + 'name' => self.person.name,
  336 + 'email' => self.email,
335 337 'avatar' => self.person.profile_custom_icon(gravatar_default),
336 338 'is_admin' => self.person.is_admin?,
337 339 'since_month' => self.person.created_at.month,
... ...
app/views/cms/_view_items.html.erb 0 → 100644
... ... @@ -0,0 +1,29 @@
  1 +<% @articles.each do |article| article = FilePresenter.for article %>
  2 + <tr title="<%= article.title%>" >
  3 + <td class="article-name">
  4 + <%= link_to_article(article) %>
  5 + </td>
  6 + <% short_description = article.respond_to?(:short_description) ?
  7 + article.short_description :
  8 + article.class.short_description %>
  9 + <td class="article-mime" title=<%= short_description.to_json %>>
  10 + <%= short_description %>
  11 + </td>
  12 + <td class="last-update">
  13 + <%= time_ago_in_words article.updated_at %>
  14 + </td>
  15 + <td class="article-controls">
  16 + <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit, article) %>
  17 + <%= button_without_text :eyes, _('Public view'), article.view_url %>
  18 + <%= display_spread_button(article) unless remove_content_button(:spread, article) %>
  19 + <% if user.can_change_homepage? && !remove_content_button(:home, article) %>
  20 + <% if profile.home_page != article %>
  21 + <%= expirable_button article, :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %>
  22 + <% else %>
  23 + <%= button_without_text(:'home-not', _('Reset homepage'), { :action => 'set_home_page', :id => nil }, :method => :post) %>
  24 + <% end %>
  25 + <% end %>
  26 + <%= display_delete_button(article) if !remove_content_button(:delete, article) %>
  27 + </td>
  28 + </tr>
  29 +<% end %>
... ...
app/views/cms/view.html.erb
... ... @@ -37,6 +37,7 @@
37 37 <tr>
38 38 <th><%= _('Name') %></th>
39 39 <th><%= _('Type') %></th>
  40 + <th><%= _('Last update') %></th>
40 41 <th><%= _('Actions') %></th>
41 42 </tr>
42 43  
... ... @@ -54,32 +55,7 @@
54 55 </tr>
55 56 <% end %>
56 57  
57   - <% @articles.each do |article| article = FilePresenter.for article %>
58   - <tr title="<%= article.title%>" >
59   - <td class="article-name">
60   - <%= link_to_article(article) %>
61   - </td>
62   - <% short_description = article.respond_to?(:short_description) ?
63   - article.short_description :
64   - article.class.short_description %>
65   - <td class="article-mime" title=<%= short_description.to_json %>>
66   - <%= short_description %>
67   - </td>
68   - <td class="article-controls">
69   - <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit, article) %>
70   - <%= button_without_text :eyes, _('Public view'), article.view_url %>
71   - <%= display_spread_button(article) unless remove_content_button(:spread, article) %>
72   - <% if user.can_change_homepage? && !remove_content_button(:home, article) %>
73   - <% if profile.home_page != article %>
74   - <%= expirable_button article, :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %>
75   - <% else %>
76   - <%= button_without_text(:'home-not', _('Reset homepage'), { :action => 'set_home_page', :id => nil }, :method => :post) %>
77   - <% end %>
78   - <% end %>
79   - <%= display_delete_button(article) if !remove_content_button(:delete, article) %>
80   - </td>
81   - </tr>
82   - <% end %>
  58 + <%= render 'view_items' %>
83 59  
84 60 </table>
85 61  
... ...
app/views/content_viewer/_article_title.html.erb 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +<% if @page.belongs_to_blog? || @page.belongs_to_forum?%>
  2 + <h1 class="title">
  3 + <% if no_link %>
  4 + <%= h(@page.title) %>
  5 + <% else %>
  6 + <%= link_to(@page.name, @page.url) %>
  7 + <% end %>
  8 + </h1>
  9 + <%= render :partial => "publishing_info" %>
  10 + <% unless @page.abstract.blank? %>
  11 + <div class="preview">
  12 + <%= @page.lead %>
  13 + </div>
  14 + <% end %>
  15 +<% else %>
  16 + <h1 class="title">
  17 + <%= h(@page.title) %>
  18 + </h1>
  19 + <%= render :partial => "publishing_info" %>
  20 +<% end %>
... ...
app/views/content_viewer/_article_toolbar.html.erb
... ... @@ -64,7 +64,7 @@
64 64 <% end %>
65 65 <%= link_to(image_tag('/images/icons-mime/rss-feed.png'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %>
66 66 <%= @plugins.dispatch(:article_header_extra_contents, @page).collect { |content| instance_exec(&content) }.join("") %>
67   - <%= article_title(@page, :no_link => true) %>
  67 + <%= render :partial => 'article_title', :locals => {:no_link => true} %>
68 68 <%= article_translations(@page) %>
69 69 </div>
70 70 </div>
... ...
app/views/content_viewer/_display_compact_format.html.erb
... ... @@ -16,6 +16,6 @@
16 16 </div>
17 17 <% end %>
18 18 <div class = <%= className %> >
19   - <%= article.abstract.truncate(400) %>
  19 + <%= article.lead(400) %>
20 20 </div>
21 21 </div>
... ...
app/views/content_viewer/_publishing_info.html.erb 0 → 100644
... ... @@ -0,0 +1,29 @@
  1 +<span class="publishing-info">
  2 + <span class="date">
  3 + <%= show_date(@page.published_at) %>
  4 + </span>
  5 + <span class="author">
  6 + <%= _(", by %s") % (@page.author ? link_to(@page.author_name, @page.author_url) : @page.author_name) %>
  7 + </span>
  8 +<% unless @no_comments %>
  9 + <span class="comments">
  10 + <%= (" - %s") % link_to_comments(@page)%>
  11 + </span>
  12 +<% end %>
  13 +</span>
  14 +
  15 +<% if @page.display_hits? || @page.license.present? %>
  16 + <div id='article-sub-header'>
  17 + <% if @page.display_hits? %>
  18 + <div id="article-hits">
  19 + <%= n_('Viewed one time', 'Viewed %{num} times', @page.hits) % { :num => @page.hits } %>
  20 + </div>
  21 + <% end %>
  22 +
  23 + <% if @page.license.present? %>
  24 + <div id="article-license">
  25 + <%= _('Licensed under %s') % (@page.license.url.present? ? link_to(@page.license.name, @page.license.url, :target => '_blank') : @page.license.name) %>
  26 + </div>
  27 + <% end %>
  28 + </div>
  29 +<% end %>
... ...
app/views/content_viewer/event_page.html.erb 0 → 100644
... ... @@ -0,0 +1,41 @@
  1 +<div class="event-card">
  2 + <div class="event-image">
  3 + <% if event.image %>
  4 + <%= image_tag(event.image.public_filename(:big)) %>
  5 + <% end %>
  6 + </div>
  7 + <div class="about-event">
  8 + <% if event.start_date.present? || event.end_date.present? %>
  9 + <span class="event-date">
  10 + <%= show_period(event.start_date, event.end_date) %>
  11 + </span>
  12 + <% end %>
  13 + <% if event.link.present? %>
  14 + <span class="event-link">
  15 + <%= link_to event.link, event.link %>
  16 + </span>
  17 + <% end %>
  18 + <% if event.address.present? %>
  19 + <span class="event-address">
  20 + <span>
  21 + <%= event.address %>
  22 + </span>
  23 + </span>
  24 + <% end %>
  25 + </div>
  26 +</div>
  27 +
  28 +<div class="event-body">
  29 + <% if format == 'short' %>
  30 + <%= display_short_format event, :comments_link => false, :read_more_link => false %>
  31 + <% else %>
  32 + <% unless event.abstract.blank? %>
  33 + <div class="event-lead">
  34 + <%= event.article_lead %>
  35 + </div>
  36 + <% end %>
  37 + <div class="event-content">
  38 + <%= event.body %>
  39 + </div>
  40 + <% end %>
  41 +</div>
... ...
app/views/content_viewer/view_page.html.erb
... ... @@ -24,22 +24,6 @@
24 24 <%= render :partial => 'article_toolbar' %>
25 25 </div>
26 26  
27   -<% if @page.display_hits? || @page.license.present? %>
28   - <div id='article-sub-header'>
29   - <% if @page.display_hits? %>
30   - <div id="article-hits">
31   - <%= n_('Viewed one time', 'Viewed %{num} times', @page.hits) % { :num => @page.hits } %>
32   - </div>
33   - <% end %>
34   -
35   - <% if @page.license.present? %>
36   - <div id="article-license">
37   - <%= _('Licensed under %s') % (@page.license.url.present? ? link_to(@page.license.name, @page.license.url, :target => '_blank') : @page.license.name) %>
38   - </div>
39   - <% end %>
40   - </div>
41   -<% end %>
42   -
43 27 <% if NOOSFERO_CONF['addthis_enabled'] %>
44 28 <%= render :partial => 'addthis' %>
45 29 <% end %>
... ... @@ -47,6 +31,12 @@
47 31 <% cache(@page.cache_key(params, user, language)) do %>
48 32 <div class="<%="article-body article-body-" + @page.css_class_name %>">
49 33 <% options = @page.image? ? {:gallery_view => true} : {} %>
  34 + <% if @page.image.present? && !@page.event? %>
  35 + <div class="article-body-img">
  36 + <%= image_tag(@page.image.public_filename) %>
  37 + <p><%= @page.image.label%></p>
  38 + </div>
  39 + <% end %>
50 40 <%= article_to_html(@page, options) %>
51 41 <br style="clear:both" />
52 42 </div> <!-- end class="article-body" -->
... ...
app/views/layouts/application-ng.html.erb
... ... @@ -27,6 +27,7 @@
27 27  
28 28 <script type="text/javascript">
29 29 DEFAULT_LOADING_MESSAGE = <%="'#{ _('loading...') }'" %>;
  30 + noosfero.profile = <%= (@profile.identifier if @profile).to_json %>
30 31 </script>
31 32  
32 33 </head>
... ...
app/views/manage_products/_categories_autocomplete.html.erb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<%= text_field_tag 'product_category_id', '', :placeholder => _('type a category for the product') %>
  2 +
  3 +<%= javascript_include_tag '/javascripts/product_categories.js' %>
  4 +<%= javascript_tag do %>
  5 + product_categories.autocomplete.search_url = <%= url_for(:controller => :manage_products, :action => :search_categories).to_json %>
  6 + product_categories.autocomplete.select_url = <%= url_for(:controller => :manage_products, :action => :show_category_tree).to_json %>
  7 + product_categories.autocomplete.load('#product_category_id')
  8 +<% end %>
... ...
app/views/manage_products/_selected_category_tree.html.erb 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +<%= categories_container selects_for_all_ancestors(@category), hierarchy_category_navigation(@category, :make_links => true) %>
  2 +
... ...
app/views/manage_products/edit_category.html.erb
... ... @@ -16,7 +16,7 @@
16 16  
17 17 <h3><%= _('Edit category of this product:') %></h3>
18 18  
19   - <%= categories_container(selects_for_all_ancestors(@category), hierarchy_category_navigation(@category, :make_links => true)) %>
  19 + <%= render 'manage_products/selected_category_tree' %>
20 20  
21 21 <div id='categories_selection_actionbar'>
22 22 <%= button(:back, _('Back to product'), :action => 'show', :id => @product) %>
... ...
app/views/shared/_change_image.html.erb
1   - <%= i.file_field( :uploaded_data, { :onchange => 'updateImg(this.value)' } ) %>
2   - <%= button_to_function(:cancel,_('Cancel'),"jQuery('#change-image-link').show(); jQuery('#change-image').html('')", :id => 'cancel-change-image-link', :style => 'display: none')%>
  1 +<%= i.file_field( :uploaded_data, { :onchange => 'updateImg(this.value)' } ) %>
  2 +<%= labelled_form_field(_("Image Label:"), i.text_field(:label)) %>
  3 +<%= button_to_function(:cancel,_('Cancel'),"jQuery('#change-image-link').show(); jQuery('#change-image').html('')", :id => 'cancel-change-image-link', :style => 'display: none')%>
... ...
app/views/shared/_content_item.html.erb
... ... @@ -4,8 +4,8 @@
4 4 <%= display_content_icon(content) %>
5 5 </div>
6 6 <span class="item-description">
7   - <%= link_to(short_filename_upper_ext(content.name), content.url) %>
  7 + <%= link_to(content.name, content.url) %>
8 8 </span>
9 9 <span class="item-date"><%= _("Published at: #{show_date(content.updated_at)}") %></span>
10 10 </div>
11   -</div>
12 11 \ No newline at end of file
  12 +</div>
... ...
config/database.yml.gitlab-ci 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +test: &TEST
  2 + adapter: postgresql
  3 + database: gitlab_ci_test
  4 + username: gitlab_ci_runner
  5 +development:
  6 + <<: *TEST
... ...
config/database.yml.travis 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +# From http://about.travis-ci.org/docs/user/database-setup/
  2 +test:
  3 + adapter: postgresql
  4 + database: myapp_test
  5 + username: postgres
... ...
config/environments/staging.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +# inherit from production
  2 +require_relative 'production'
  3 +
  4 +Noosfero::Application.configure do
  5 +
  6 + # expose errors
  7 + config.consider_all_requests_local = true
  8 +
  9 + # ease debug
  10 + config.assets.compress = false
  11 +
  12 +end
  13 +
... ...
db/migrate/20140708123314_index_role_assignments_filtered_fields.rb 0 → 100644
... ... @@ -0,0 +1,15 @@
  1 +class IndexRoleAssignmentsFilteredFields < ActiveRecord::Migration
  2 +
  3 + def self.up
  4 + add_index :role_assignments, [:accessor_id, :accessor_type]
  5 + add_index :role_assignments, [:accessor_id, :accessor_type, :role_id], name: :index_on_role_assigments_accessor_role
  6 + add_index :role_assignments, [:resource_id, :resource_type]
  7 + add_index :role_assignments, [:resource_id, :resource_type, :role_id], name: :index_on_role_assigments_resource_role
  8 + add_index :role_assignments, [:accessor_id, :accessor_type, :resource_id, :resource_type], name: :index_on_role_assigments_accessor_resource_role
  9 + add_index :profiles, [:type]
  10 + add_index :profiles, [:visible]
  11 + add_index :profiles, [:enabled]
  12 + add_index :profiles, [:validated]
  13 + end
  14 +
  15 +end
... ...
db/migrate/20150603182105_add_label_to_image.rb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +class AddLabelToImage < ActiveRecord::Migration
  2 + def up
  3 + add_column :images, :label, :string, :default => ""
  4 + end
  5 + def down
  6 + remove_column :images, :label
  7 + end
  8 +end
... ...
db/schema.rb
... ... @@ -11,7 +11,7 @@
11 11 #
12 12 # It's strongly recommended to check this file into your version control system.
13 13  
14   -ActiveRecord::Schema.define(:version => 20150602142030) do
  14 +ActiveRecord::Schema.define(:version => 20150603182105) do
15 15  
16 16 create_table "abuse_reports", :force => true do |t|
17 17 t.integer "reporter_id"
... ... @@ -376,6 +376,7 @@ ActiveRecord::Schema.define(:version =&gt; 20150602142030) do
376 376 t.integer "width"
377 377 t.integer "height"
378 378 t.boolean "thumbnails_processed", :default => false
  379 + t.string "label", :default => ""
379 380 end
380 381  
381 382 add_index "images", ["parent_id"], :name => "index_images_on_parent_id"
... ...
features/article_visualization.feature 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +Feature: article visualization
  2 + As a user
  3 + I want to change view modes
  4 + In order to see articles in fullscreen or not in fullscreen
  5 +
  6 + Background:
  7 + Given the following users
  8 + | login | name |
  9 + | joaosilva | Joao Silva |
  10 + And "joaosilva" has no articles
  11 + And the following articles
  12 + | owner | name | body |
  13 + | joaosilva | Sample Article | This is an article |
  14 + And I am logged in as "joaosilva"
  15 +
  16 + @selenium
  17 + Scenario: viewing the article in fullscreen by default
  18 + Given I go to /joaosilva/sample-article?fullscreen=1
  19 + Then I should see "Exit full screen"
  20 +
  21 + @selenium
  22 + Scenario: viewing the article not in fullscreen by default
  23 + Given I go to /joaosilva/sample-article
  24 + Then I should see "Full screen"
  25 +
  26 + @selenium
  27 + Scenario: changing the view mode from not in fullscreen to fullscreen
  28 + Given I go to /joaosilva/sample-article
  29 + And I follow "Full screen"
  30 + Then I should see "Exit full screen"
... ...
features/events.feature
... ... @@ -160,6 +160,7 @@ Feature: events
160 160 When I am on /search/events
161 161 Then I should see "Colivre.net's Events"
162 162  
  163 +
163 164 @selenium
164 165 Scenario: published events should be listed in the agenda too
165 166 Given the following community
... ...
lib/short_filename.rb
... ... @@ -1,21 +0,0 @@
1   -module ShortFilename
2   -
3   - def short_filename(filename, limit_chars = 43)
4   - extname = File.extname(filename)
5   - basename = File.basename(filename,extname)
6   - return shrink(basename, extname, limit_chars) + extname
7   - end
8   -
9   - def short_filename_upper_ext(filename, limit_chars = 43)
10   - extname = File.extname(filename)
11   - display_name = shrink(File.basename(filename, extname), extname, limit_chars)
12   - return extname.present? ? (display_name + ' - ' + extname.upcase.delete(".")) : display_name
13   - end
14   -
15   - def shrink(filename, extname, limit_chars)
16   - return filename if filename.size <= limit_chars
17   - str_complement = '(...)'
18   - return filename[0..(limit_chars - extname.size - str_complement.size - 1)] + str_complement
19   - end
20   -
21   -end
plugins/context_content/views/blocks/context_content.html.erb
... ... @@ -6,7 +6,7 @@
6 6 <%= instance_eval(&block.content_image(content)) if block.show_image %>
7 7 </div>
8 8 <% if block.show_name %>
9   - <div class="name"><%= short_filename(content.name, 30) %></div>
  9 + <div class="name"><%= content.name %></div>
10 10 <% end %>
11 11 </a>
12 12 </span>
... ...
plugins/metadata/lib/ext/product.rb
... ... @@ -7,7 +7,7 @@ class Product
7 7 url: proc{ |p, plugin| plugin.og_url_for p.url },
8 8 gr_hascurrencyvalue: proc{ |p, plugin| p.price.to_f },
9 9 gr_hascurrency: proc{ |p, plugin| p.environment.currency_unit },
10   - title: proc{ |a, plugin| "#{p.name} - #{p.profile.name}" },
  10 + title: proc{ |p, plugin| "#{p.name} - #{p.profile.name}" if p },
11 11 description: proc{ |p, plugin| ActionView::Base.full_sanitizer.sanitize p.description },
12 12  
13 13 image: proc{ |p, plugin| "#{p.environment.top_url}#{p.image.public_filename}" if p.image },
... ... @@ -17,7 +17,7 @@ class Product
17 17  
18 18 see_also: [],
19 19 site_name: proc{ |p, plugin| plugin.og_url_for p.profile.url },
20   - updated_time: proc{ |p, plugin| p.updated_at.iso8601 },
  20 + updated_time: proc{ |p, plugin| p.updated_at.iso8601 if p.updated_at },
21 21  
22 22 'locale:locale' => proc{ |p, plugin| p.environment.default_language },
23 23 'locale:alternate' => proc{ |p, plugin| p.environment.languages - [p.environment.default_language] if p.environment.languages },
... ...
plugins/metadata/test/functional/home_controller_test.rb
... ... @@ -12,7 +12,7 @@ class HomeControllerTest &lt; ActionController::TestCase
12 12 @response = ActionController::TestResponse.new
13 13  
14 14 Noosfero::Plugin.stubs(:all).returns([MetadataPlugin.name])
15   - Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([MetadataPlugin.new])
  15 + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([MetadataPlugin.new(@controller)])
16 16 end
17 17  
18 18 should 'display meta tags for social media' do
... ...
plugins/metadata/test/functional/manage_products_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,27 @@
  1 +require 'test_helper'
  2 +require 'home_controller'
  3 +
  4 +# Re-raise errors caught by the controller.
  5 +class ManageProductsController; def rescue_action(e) raise e end; end
  6 +
  7 +class ManageProductsControllerTest < ActionController::TestCase
  8 +
  9 + def setup
  10 + @controller = ManageProductsController.new
  11 + @request = ActionController::TestRequest.new
  12 + @response = ActionController::TestResponse.new
  13 + @enterprise = fast_create(Enterprise, name: 'test', identifier: 'test_ent')
  14 + @user = create_user_with_permission('test_user', 'manage_products', @enterprise)
  15 + @environment = @enterprise.environment
  16 + @environment.enable('products_for_enterprises')
  17 + login_as :test_user
  18 +
  19 + Noosfero::Plugin.stubs(:all).returns([MetadataPlugin.name])
  20 + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([MetadataPlugin.new(@controller)])
  21 + end
  22 +
  23 + should "not crash on new products" do
  24 + get :new, profile: @enterprise.identifier
  25 + end
  26 +
  27 +end
... ...
plugins/shopping_cart/lib/shopping_cart_plugin.rb
1 1 class ShoppingCartPlugin < Noosfero::Plugin
  2 + include ModalHelper
  3 + include ActionView::Helpers::UrlHelper
2 4  
3 5 class << self
4 6 def plugin_name
... ... @@ -63,4 +65,8 @@ class ShoppingCartPlugin &lt; Noosfero::Plugin
63 65  
64 66 buttons
65 67 end
  68 +
  69 + def controller
  70 + context
  71 + end
66 72 end
... ...
plugins/shopping_cart/lib/shopping_cart_plugin/cart_helper.rb
... ... @@ -42,7 +42,7 @@ module ShoppingCartPlugin::CartHelper
42 42 else
43 43 delivery = Product.new(:name => delivery_option || _('Delivery'), :price => settings.delivery_options[delivery_option])
44 44 end
45   - delivery.save(false)
  45 + delivery.save(validate: false)
46 46 items << [delivery.id, '']
47 47 end
48 48  
... ...
plugins/shopping_cart/test/unit/shopping_cart_plugin/cart_helper_test.rb
... ... @@ -41,5 +41,19 @@ class ShoppingCartPlugin::CartHelperTest &lt; ActiveSupport::TestCase
41 41 assert_equal "#{environment.currency_unit}13#{environment.currency_separator}70", float_to_currency_cart(value,environment)
42 42 end
43 43  
44   -end
  44 + should 'return a table of items' do
  45 + enterprise = Enterprise.new(name: "Test Enterprise", identifier: "test-enterprise")
  46 + enterprise.environment = Environment.default
  47 + enterprise.save!
  48 +
  49 + product_category = fast_create(ProductCategory, :name => 'Products')
  50 + product = fast_create(Product, :name => 'test product1', :product_category_id => product_category.id, :profile_id => enterprise.id)
  51 + setting = Noosfero::Plugin::Settings.new(enterprise, ShoppingCartPlugin)
  52 + setting.delivery = true
  53 + setting.save!
  54 +
  55 + assert_match 'table id="cart-items-table"', items_table([product], enterprise)
  56 + assert_match '<td>test product1</td>', items_table([product], enterprise)
  57 + end
45 58  
  59 +end
... ...
plugins/shopping_cart/views/cart.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <a href="cart:clean" onclick="Cart.clean(this); return false" class="cart-clean"><%=_('Clean basket')%></a>
6 6 <ul class="cart-items"></ul>
7 7 <div class="cart-total"><%=_('Total:')%> <b></b></div>
8   - <a href="/plugin/shopping_cart/buy" class="cart-buy modal"><%=_('Shopping checkout')%></a>
  8 + <%= modal_link_to _('Shopping checkout'), { controller: 'shopping_cart_plugin', action: 'buy' }, { class: "cart-buy modal" } %>
9 9 </div>
10 10 <a href="#" onclick="Cart.toggle(this); return false" class="cart-toggle">
11 11 <span class="str-show"><%=_('Show basket')%></span>
... ...
plugins/shopping_cart/views/shopping_cart_plugin/buy.html.erb
... ... @@ -24,7 +24,6 @@
24 24 <% end %>
25 25 <% delivery_option = @settings.delivery_options.first && @settings.delivery_options.first.first %>
26 26 <%= items_table(@cart[:items], @profile, delivery_option) %>
27   - <%= link_to_function '', "noosfero.modal.close();", :class => 'cart-box-close icon-cancel' %>
28 27 </div>
29 28  
30 29 <%= javascript_include_tag '../plugins/shopping_cart/buy' %>
... ...
plugins/shopping_cart/views/shopping_cart_plugin_profile/buy.html.erb
... ... @@ -17,7 +17,6 @@
17 17 </div>
18 18 <% end %>
19 19 <%= items_table(session[:cart][:items], profile) %>
20   - <%= link_to_function '', "noosfero.modal.close();", :class => 'cart-box-close icon-cancel' %>
21 20 </div>
22 21  
23 22 <script type="text/javascript">
... ...
plugins/social_share_privacy/lib/social_share_privacy_plugin.rb
... ... @@ -19,12 +19,14 @@ class SocialSharePrivacyPlugin &lt; Noosfero::Plugin
19 19 def article_extra_contents(article)
20 20 proc do
21 21 settings = Noosfero::Plugin::Settings.new(environment, SocialSharePrivacyPlugin)
  22 + modules = settings.get_setting(:networks).map { |service| "/plugins/social_share_privacy/socialshareprivacy/javascripts/modules/#{service}.js" }
22 23 locale = FastGettext.locale
23   - javascript_include_tag('plugins/social_share_privacy/socialshareprivacy/javascripts/socialshareprivacy.js') +
24   - javascript_include_tag('plugins/social_share_privacy/socialshareprivacy/javascripts/localstorage.js') +
25   - javascript_include_tag(settings.get_setting(:networks).map { |service| "plugins/social_share_privacy/socialshareprivacy/javascripts/modules/#{service}.js" }) +
26   - (locale != 'en' ? javascript_include_tag("plugins/social_share_privacy/socialshareprivacy/javascripts/locale/jquery.socialshareprivacy.min.#{locale}.js") : '') +
27   - javascript_tag("jQuery.fn.socialSharePrivacy.settings.path_prefix = '../../plugins/social_share_privacy/socialshareprivacy/'; jQuery.fn.socialSharePrivacy.settings.order = #{settings.get_setting(:networks)}; jQuery(document).ready(function () { jQuery('.social-buttons').socialSharePrivacy({info_link_target: '_blank'});});") +
  24 + javascript_include_tag('/plugins/social_share_privacy/socialshareprivacy/javascripts/socialshareprivacy.js') +
  25 + javascript_include_tag('/plugins/social_share_privacy/socialshareprivacy/javascripts/localstorage.js') +
  26 + (modules.present? ? javascript_include_tag(*modules) : '') +
  27 + javascript_include_tag("/plugins/social_share_privacy/socialshareprivacy/javascripts/modules/facebook.js") +
  28 + (locale != 'en' ? javascript_include_tag("/plugins/social_share_privacy/socialshareprivacy/javascripts/locale/jquery.socialshareprivacy.min.#{locale}.js") : '') +
  29 + javascript_tag("jQuery.fn.socialSharePrivacy.settings.path_prefix = '/plugins/social_share_privacy/socialshareprivacy/'; jQuery.fn.socialSharePrivacy.settings.order = #{settings.get_setting(:networks)}; jQuery(document).ready(function () { jQuery('.social-buttons').socialSharePrivacy({info_link_target: '_blank'});});") +
28 30 content_tag(:div, '', :class => "social-buttons")
29 31 end
30 32 end
... ...
plugins/social_share_privacy/test/functional/content_viewer_controller_test.rb
... ... @@ -23,7 +23,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
23 23  
24 24 get :view_page, :profile => @profile.identifier, :page => ['test']
25 25  
26   - assert_tag :tag => 'script', :attributes => {:src => /\/javascripts\/plugins\/social_share_privacy\/socialshareprivacy\/javascripts\/socialshareprivacy\.js\??\d*/}
  26 + assert_tag :tag => 'script', :attributes => {:src => /\/plugins\/social_share_privacy\/socialshareprivacy\/javascripts\/socialshareprivacy\.js\??\d*/}
27 27 assert_tag :tag => 'div', :attributes => {:class => "social-buttons"}
28 28 end
29 29  
... ... @@ -34,8 +34,8 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
34 34  
35 35 get :view_page, :profile => @profile.identifier, :page => ['test']
36 36  
37   - assert_tag :tag => 'script', :attributes => {:src => /\/javascripts\/plugins\/social_share_privacy\/socialshareprivacy\/javascripts\/modules\/twitter\.js\??\d*/}
38   - assert_tag :tag => 'script', :attributes => {:src => /\/javascripts\/plugins\/social_share_privacy\/socialshareprivacy\/javascripts\/modules\/gplus\.js\??\d*/}
  37 + assert_tag :tag => 'script', :attributes => {:src => /\/plugins\/social_share_privacy\/socialshareprivacy\/javascripts\/modules\/twitter\.js\??\d*/}
  38 + assert_tag :tag => 'script', :attributes => {:src => /\/plugins\/social_share_privacy\/socialshareprivacy\/javascripts\/modules\/gplus\.js\??\d*/}
39 39 end
40 40  
41 41 should 'add javascript with string translations if not english' do
... ... @@ -45,12 +45,12 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
45 45  
46 46 get :view_page, :profile => @profile.identifier, :page => ['test']
47 47  
48   - assert_tag :tag => 'script', :attributes => {:src => /\/javascripts\/plugins\/social_share_privacy\/socialshareprivacy\/javascripts\/locale\/jquery\.socialshareprivacy\.min\.pt\.js\??\d*/}
  48 + assert_tag :tag => 'script', :attributes => {:src => /\/plugins\/social_share_privacy\/socialshareprivacy\/javascripts\/locale\/jquery\.socialshareprivacy\.min\.pt\.js\??\d*/}
49 49  
50 50 FastGettext.stubs(:locale).returns('en')
51 51  
52 52 get :view_page, :profile => @profile.identifier, :page => ['test']
53 53  
54   - assert_no_tag :tag => 'script', :attributes => {:src => /\/javascripts\/plugins\/social_share_privacy\/socialshareprivacy\/javascripts\/locale\/jquery\.socialshareprivacy\.min\.en\.js\??\d*/}
  54 + assert_no_tag :tag => 'script', :attributes => {:src => /\/plugins\/social_share_privacy\/socialshareprivacy\/javascripts\/locale\/jquery\.socialshareprivacy\.min\.en\.js\??\d*/}
55 55 end
56 56 end
... ...
public/designs/themes/base/style.css
... ... @@ -1061,15 +1061,18 @@ hr.pre-posts, hr.sep-posts {
1061 1061 text-decoration: none;
1062 1062 }
1063 1063  
1064   -#content .main-block .created-at {
  1064 +#content .main-block .publishing-info {
1065 1065 text-align: left;
1066 1066 color: #AAA;
  1067 + font-size: 11px;
  1068 + /*padding-top: 20px;*/
  1069 + margin-bottom:15px;
1067 1070 }
1068   -#content .main-block .created-at a {
  1071 +#content .main-block .publishing-info a {
1069 1072 color: #AAA;
1070 1073 text-decoration: none;
1071 1074 }
1072   -#content .main-block .created-at a:hover {
  1075 +#content .main-block .publishing-info a:hover {
1073 1076 color: #555;
1074 1077 text-decoration: underline;
1075 1078 }
... ... @@ -1401,3 +1404,129 @@ table.profile th {
1401 1404 table#recaptcha_table tr:hover td {
1402 1405 background-color: #fff;
1403 1406 }
  1407 +
  1408 +/* product cateogories */
  1409 +#categories-container #product_category_id {
  1410 + font-size: 18px;
  1411 + width: 100%;
  1412 + margin-bottom: 8px;
  1413 +}
  1414 +#categories-container #product_category_id:focus {
  1415 + outline: none;
  1416 + border-color: green;
  1417 + box-shadow: 0 0 10px green;
  1418 + color:#333;
  1419 +}
  1420 +
  1421 +/************************* Article Page *****************************/
  1422 +
  1423 +#article-header .preview {
  1424 + font-size: 15px;
  1425 +}
  1426 +
  1427 +.article-body-img {
  1428 + float: left;
  1429 + margin-right: 20px;
  1430 + margin-top: 5px;
  1431 +}
  1432 +
  1433 +#content #article .article-body .article-body-img img {
  1434 + height: auto;
  1435 + width: auto;
  1436 + min-height: 120px;
  1437 + max-height: 180px;
  1438 + max-width: 250px;
  1439 + background-position: center center;
  1440 + background-repeat: no-repeat;
  1441 +}
  1442 +
  1443 +#content #article .article-body .article-body-img p {
  1444 + margin-bottom: 10px;
  1445 + font-size: 10px;
  1446 + min-height: 20px;
  1447 +}
  1448 +/* Noosfero Events */
  1449 +
  1450 +.event-card {
  1451 + float: left;
  1452 + padding-top: 25px;
  1453 + width: 494px;
  1454 + height: 116px;
  1455 + background-repeat: no-repeat;
  1456 + margin-bottom: 30px;
  1457 +}
  1458 +
  1459 +.event-image {
  1460 + position: relative;
  1461 + float: left;
  1462 + padding-right: 22px;
  1463 + max-width: 130px;
  1464 + height: 130px;
  1465 +}
  1466 +
  1467 +#content #article .article-body img{
  1468 + max-height: 100%;
  1469 +}
  1470 +
  1471 +.about-event {
  1472 + position: relative;
  1473 + float: left;
  1474 + height: 160px;
  1475 + width: 300px;
  1476 + max-width: 300px;
  1477 +}
  1478 +
  1479 +.about-event > span {
  1480 + display: block;
  1481 + max-width: inherit;
  1482 + margin-left: 20px;
  1483 + padding-left: 21px;
  1484 + line-height: 13px;
  1485 + margin-right: 11px;
  1486 +}
  1487 +
  1488 +.about-event .event-date {
  1489 + margin-top: 3px;
  1490 +}
  1491 +
  1492 +.about-event .event-address {
  1493 + margin-top: 19px;
  1494 +}
  1495 +
  1496 +.about-event .event-address span {
  1497 + display: block;
  1498 + margin-left: 0px;
  1499 + margin-top: 4.4px;
  1500 + line-height: 14px;
  1501 +}
  1502 +
  1503 +.event-date {
  1504 + background: url('/images/calendar_date_select/calendar-icon.png') no-repeat left center;
  1505 + padding: 5px;
  1506 +}
  1507 +
  1508 +.event-link {
  1509 + background: url('/images/globe-icon.png') no-repeat left center;
  1510 + margin-top: 18px;
  1511 +}
  1512 +
  1513 +.event-link a {
  1514 +}
  1515 +
  1516 +.event-address {
  1517 + background: url('/images/icone_pin.png') no-repeat left top;
  1518 +}
  1519 +
  1520 +.event-body {
  1521 + float: left;
  1522 +}
  1523 +
  1524 +.event-body .event-lead {
  1525 + font-size: 15px;
  1526 +}
  1527 +
  1528 +.event-body .event-content p {
  1529 + margin-top: 20px;
  1530 + width: 494px;
  1531 + padding-left: 2px;
  1532 +}
... ...
public/images/calendar_date_select/calendar-icon.png 0 → 100644

283 Bytes

public/images/globe-icon.png 0 → 100644

464 Bytes

public/images/icone_pin.png 0 → 100644

51.9 KB

public/javascripts/application.js
... ... @@ -14,6 +14,7 @@
14 14 *= require jquery.ba-bbq.min.js
15 15 *= require jquery.tokeninput.js
16 16 *= require jquery-timepicker-addon/dist/jquery-ui-timepicker-addon.js
  17 +*= require select-or-die/_src/selectordie
17 18 *= require inputosaurus.js
18 19 *= require reflection.js
19 20 *= require rails.js
... ... @@ -830,7 +831,7 @@ Array.min = function(array) {
830 831  
831 832 function hideAndGetUrl(link) {
832 833 document.body.style.cursor = 'wait';
833   - link.hide();
  834 + jQuery(link).hide();
834 835 url = jQuery(link).attr('href');
835 836 jQuery.get(url, function( data ) {
836 837 document.body.style.cursor = 'default';
... ... @@ -1178,7 +1179,10 @@ window.isHidden = function isHidden() { return (typeof(document.hidden) != &#39;unde
1178 1179  
1179 1180 function $_GET(id){
1180 1181 var a = new RegExp(id+"=([^&#=]*)");
1181   - return decodeURIComponent(a.exec(window.location.search)[1]);
  1182 + var result_of_search = a.exec(window.location.search)
  1183 + if(result_of_search != null){
  1184 + return decodeURIComponent(result_of_search[1]);
  1185 + }
1182 1186 }
1183 1187  
1184 1188 var fullwidth=false;
... ... @@ -1206,4 +1210,3 @@ function fullscreenPageLoad(itemId){
1206 1210 }
1207 1211 });
1208 1212 }
1209   -
... ...
public/javascripts/jquery.tokeninput.js
... ... @@ -345,7 +345,7 @@ $.TokenList = function (input, url_or_data, options) {
345 345 dropdown.appendTo("body");
346 346 if (!settings.permanentDropdown)
347 347 dropdown.appendTo("body");
348   - else
  348 + else
349 349 $(input).after(dropdown.show());
350 350  
351 351 if (settings.permanentDropdown || settings.showAllResults) {
... ... @@ -382,7 +382,7 @@ $.TokenList = function (input, url_or_data, options) {
382 382 if(li_data && li_data.length) {
383 383 $.each(li_data, function (index, value) {
384 384 insert_token(value);
385   - checkTokenLimit();
  385 + checkTokenLimit({init: true});
386 386 });
387 387 }
388 388  
... ... @@ -425,12 +425,12 @@ $.TokenList = function (input, url_or_data, options) {
425 425 // Private functions
426 426 //
427 427  
428   - function checkTokenLimit() {
  428 + function checkTokenLimit(options) {
429 429 if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
430 430 input_box.hide();
431 431 hide_dropdown();
432 432 return;
433   - } else {
  433 + } else if (options && !options.init) {
434 434 input_box.focus();
435 435 }
436 436 }
... ... @@ -618,7 +618,7 @@ $.TokenList = function (input, url_or_data, options) {
618 618 if (!settings.showAllResults)
619 619 dropdown.empty();
620 620 selected_dropdown_item = null;
621   - }
  621 + }
622 622 if (settings.showAllResults)
623 623 show_dropdown_hint();
624 624 }
... ... @@ -728,7 +728,7 @@ $.TokenList = function (input, url_or_data, options) {
728 728 item.addClass(settings.classes.selectedDropdownItem);
729 729 selected_dropdown_item = item.get(0);
730 730  
731   - isBefore = item[0].offsetTop <= (dropdown[0].scrollTop + dropdown[0].scrollWidth);
  731 + isBefore = item[0].offsetTop <= (dropdown[0].scrollTop + dropdown[0].scrollWidth);
732 732 isAfter = item[0].offsetTop >= dropdown[0].scrollTop;
733 733 visible = isBefore && isAfter;
734 734 if (!visible) {
... ...
public/javascripts/product_categories.js 0 → 100644
... ... @@ -0,0 +1,40 @@
  1 +product_categories = {
  2 +
  3 + autocomplete: {
  4 + search_url: '',
  5 + select_url: '',
  6 +
  7 + load: function(elem) {
  8 + elem = jQuery(elem)
  9 +
  10 + elem.autocomplete({
  11 + minLength: 3,
  12 + selectFirst: true,
  13 +
  14 + //define callback to retrieve results
  15 + source: function(req, add) {
  16 + //pass request to server
  17 + //The alt attribute contains the wordpress callback action
  18 + var params = { term: req.term };
  19 + jQuery.getJSON(product_categories.autocomplete.search_url, params, function(data) {
  20 + add(data);
  21 + });
  22 + },
  23 +
  24 + focus: function( event, ui ) {
  25 + jQuery(this).val(ui.item.label);
  26 + return false;
  27 + },
  28 +
  29 + select: function(e, ui) {
  30 + jQuery('#categories-container').load(product_categories.autocomplete.select_url, {category_id: ui.item.value})
  31 +
  32 + jQuery(this).val("")
  33 + },
  34 +
  35 + });
  36 +
  37 + },
  38 + },
  39 +
  40 +};
... ...
public/stylesheets/application.scss
... ... @@ -1054,6 +1054,11 @@ code input {
1054 1054 margin-top: 10px;
1055 1055 display: none;
1056 1056 }
  1057 +
  1058 +#change-image {
  1059 + display: table-caption;
  1060 +}
  1061 +
1057 1062 .zoomable-image {
1058 1063 position: relative;
1059 1064 display: inline-block;
... ... @@ -1520,13 +1525,13 @@ a.comment-picture {
1520 1525 text-align: right;
1521 1526 color: gray;
1522 1527 }
1523   -#content .created-at {
  1528 +#content .publishing-info {
1524 1529 color: gray;
1525 1530 font-size: 12px;
1526 1531 display: block;
1527 1532 text-align: right;
1528 1533 }
1529   -#content .blog-post .created-at {
  1534 +#content .blog-post .publishing-info {
1530 1535 text-align: left;
1531 1536 }
1532 1537 #content #article .pagination .prev_page {
... ... @@ -1598,7 +1603,7 @@ div.article-body p img {
1598 1603 .blog-post.not-published a {
1599 1604 text-decoration: none;
1600 1605 }
1601   -#content .blog-post.not-published .created-at {
  1606 +#content .blog-post.not-published .publishing-info {
1602 1607 text-align: left;
1603 1608 }
1604 1609 .blog-post.not-published .metadata {
... ...
script/gitlab-ci
... ... @@ -1,56 +0,0 @@
1   -#!/usr/bin/env ruby
2   -
3   -# These just forward the signals to the whole process group and
4   -# then immediately exit.
5   -pgid = Process.getpgid Process.pid
6   -Signal.trap(:TERM) { Process.kill(:TERM, -pgid); exit }
7   -Signal.trap(:INT) { Process.kill(:INT, -pgid); exit }
8   -
9   -def run command, options = {}
10   - command = "#{command} 2>&1 > /dev/null" if options[:output] == false
11   - #command = "time #{command}" unless options[:runtime] == false
12   - puts "== #{command}"
13   - system command
14   -end
15   -
16   -@id = (0...10).map{ ('a'..'z').to_a[rand(26)] }.join
17   -@db = "gitlab-ci-#{@id}"
18   -
19   -def config
20   - require 'yaml'
21   - db_config = {
22   - 'adapter' => 'postgresql', 'encoding' => 'unicode',
23   - 'database' => @db, 'username' => ENV['USER'],
24   - }
25   - File.write 'config/database.yml', YAML.dump('test' => db_config, 'development' => db_config)
26   -end
27   -
28   -def prepare
29   - run("createdb #{@db}") and
30   - run('mkdir -p tmp/pids log') and
31   - run('bundle check || bundle install') and
32   - run('rake db:schema:load', output: false) and
33   - run('script/noosfero-plugins disableall') and
34   - run('rake db:migrate')
35   -end
36   -
37   -def test
38   - %w[
39   - test:units
40   - test:functionals
41   - test:integration
42   - cucumber
43   - test:noosfero_plugins
44   - ].each do |task|
45   - run "rake #{task}"
46   - end
47   -end
48   -
49   -def cleanup
50   - run "dropdb #{@db}"
51   -end
52   -
53   -ret = config and prepare and test
54   -cleanup
55   -
56   -exit (if ret == true then 0 else 1 end)
script/install-dependencies/debian-wheezy.sh
... ... @@ -80,4 +80,5 @@ packages=$(grep-dctrl -n -s Build-Depends,Depends,Recommends -S -X noosfero debi
80 80 run sudo apt-get -y install $packages
81 81 sudo apt-get -y install iceweasel || sudo apt-get -y install firefox
82 82  
  83 +run rm -f Gemfile.lock
83 84 run bundle --local
... ...
test/functional/cms_controller_test.rb
... ... @@ -223,6 +223,20 @@ class CmsControllerTest &lt; ActionController::TestCase
223 223 assert_equal profile, a.last_changed_by
224 224 end
225 225  
  226 + should 'be able to set label to article image' do
  227 + login_as(profile.identifier)
  228 + post :new, :type => TextileArticle.name, :profile => profile.identifier,
  229 + :article => {
  230 + :name => 'adding-image-label',
  231 + :image_builder => {
  232 + :uploaded_data => fixture_file_upload('/files/tux.png', 'image/png'),
  233 + :label => 'test-label'
  234 + }
  235 + }
  236 + a = Article.last
  237 + assert_equal a.image.label, 'test-label'
  238 + end
  239 +
226 240 should 'edit by using the correct template to display the editor depending on the mime-type' do
227 241 a = profile.articles.build(:name => 'test document')
228 242 a.save!
... ... @@ -318,6 +332,20 @@ class CmsControllerTest &lt; ActionController::TestCase
318 332 end
319 333 end
320 334  
  335 + should 'be able to edit an image label' do
  336 + image = fast_create(Image, :content_type => 'image/png', :filename => 'event-image.png', :label => 'test_label', :size => 1014)
  337 + article = fast_create(Article, :profile_id => profile.id, :name => 'test_label_article', :body => 'test_content')
  338 + article.image = image
  339 + article.save
  340 + assert_not_nil article
  341 + assert_not_nil article.image
  342 + assert_equal 'test_label', article.image.label
  343 +
  344 + post :edit, :profile => profile.identifier, :id => article.id, :article => {:image_builder => { :label => 'test_label_modified'}}
  345 + article.reload
  346 + assert_equal 'test_label_modified', article.image.label
  347 + end
  348 +
321 349 should 'be able to upload more than one file at once' do
322 350 assert_difference 'UploadedFile.count', 2 do
323 351 post :upload_files, :profile => profile.identifier, :uploaded_files => [fixture_file_upload('/files/test.txt', 'text/plain'), fixture_file_upload('/files/rails.png', 'text/plain')]
... ...
test/functional/content_viewer_controller_test.rb
... ... @@ -124,6 +124,19 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
124 124 assert_tag :tag => 'div', :attributes => { :id => 'article-tags' }, :descendant => { :content => /This article's tags:/ }
125 125 end
126 126  
  127 + should "display image label on article image" do
  128 + page = TinyMceArticle.create!(
  129 + :profile => profile,
  130 + :name => 'myarticle',
  131 + :image_builder => {
  132 + :uploaded_data => fixture_file_upload('/files/tux.png', 'image/png'),
  133 + :label => 'test-label'
  134 + }
  135 + )
  136 + get :view_page, page.url
  137 + assert_match /test-label/, @response.body
  138 + end
  139 +
127 140 should "not display current article's tags" do
128 141 page = profile.articles.create!(:name => 'myarticle', :body => 'test article')
129 142  
... ...
test/unit/application_helper_test.rb
... ... @@ -564,18 +564,6 @@ class ApplicationHelperTest &lt; ActionView::TestCase
564 564 assert_equal environment.theme, current_theme
565 565 end
566 566  
567   - should 'trunc to 15 chars the big filename' do
568   - assert_equal 'AGENDA(...).mp3', short_filename('AGENDA_CULTURA_-_FESTA_DE_VAQUEIROS_PONTO_DE_SERRA_PRETA_BAIXA.mp3',15)
569   - end
570   -
571   - should 'trunc to default limit the big filename' do
572   - assert_equal 'AGENDA_CULTURA_-_FESTA_DE_VAQUEIRO(...).mp3', short_filename('AGENDA_CULTURA_-_FESTA_DE_VAQUEIROS_PONTO_DE_SERRA_PRETA_BAIXA.mp3')
573   - end
574   -
575   - should 'does not trunc short filename' do
576   - assert_equal 'filename.mp3', short_filename('filename.mp3')
577   - end
578   -
579 567 should 'return nil when :show_balloon_with_profile_links_when_clicked is not enabled in environment' do
580 568 env = Environment.default
581 569 env.stubs(:enabled?).with(:show_balloon_with_profile_links_when_clicked).returns(false)
... ...
test/unit/event_test.rb
... ... @@ -109,17 +109,20 @@ class EventTest &lt; ActiveSupport::TestCase
109 109 end
110 110  
111 111 should 'provide nice display format' do
112   - e = build(Event, :start_date => Date.new(2008,1,1), :end_date => Date.new(2008,1,1), :link => 'http://www.myevent.org', :body => 'my somewhat short description')
  112 + event = build(Event, :start_date => Date.new(2008,1,1), :end_date => Date.new(2008,1,1), :link => 'http://www.myevent.org', :body => '<p>my somewhat short description</p>')
  113 + display = instance_eval(&event.to_html)
113 114  
114   - assert_tag_in_string e.to_html, :content => Regexp.new("January 1, 2008")
115   - assert_tag_in_string e.to_html, :content => 'my somewhat short description'
116   - assert_tag_in_string e.to_html, :tag => 'a', :attributes => { :href => 'http://www.myevent.org' }, :content => 'http://www.myevent.org'
  115 + assert_tag_in_string display, :content => Regexp.new("January 1, 2008")
  116 + assert_tag_in_string display, :content => Regexp.new('my somewhat short description')
  117 + assert_tag_in_string display, :content => Regexp.new('http://www.myevent.org')
117 118 end
118 119  
119 120 should 'not crash when body is blank' do
120 121 e = Event.new
121 122 assert_nil e.body
122   - assert_no_match(/_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____/, e.to_html)
  123 + assert_nothing_raised do
  124 + instance_eval(&e.to_html)
  125 + end
123 126 end
124 127  
125 128 should 'add http:// to the link if not already present' do
... ... @@ -141,10 +144,19 @@ class EventTest &lt; ActiveSupport::TestCase
141 144 assert_equal '', a.link
142 145 end
143 146  
  147 + should 'get the first paragraph' do
  148 + profile = create_user('testuser').person
  149 + event = create(Event, :profile => profile, :name => 'test',
  150 + :body => '<p>first paragraph </p><p>second paragraph </p>',
  151 + :link => 'www.colivre.coop.br', :start_date => Date.today)
  152 +
  153 + assert_match '<p>first paragraph </p>', event.first_paragraph
  154 + end
  155 +
144 156 should 'not escape HTML in body' do
145 157 a = build(Event, :body => '<p>a paragraph of text</p>', :link => 'www.gnu.org')
146 158  
147   - assert_match '<p>a paragraph of text</p>', a.to_html
  159 + assert_match '<p>a paragraph of text</p>', instance_eval(&a.to_html)
148 160 end
149 161  
150 162 should 'filter HTML in body' do
... ... @@ -324,7 +336,7 @@ class EventTest &lt; ActiveSupport::TestCase
324 336 environment = fast_create(Environment)
325 337 environment.languages = nil
326 338 profile = fast_create(Person, :environment_id => environment.id)
327   -
  339 +
328 340 event = Event.new(:profile => profile)
329 341  
330 342 assert !event.translatable?
... ... @@ -337,11 +349,11 @@ class EventTest &lt; ActiveSupport::TestCase
337 349 event = fast_create(Event, :profile_id => profile.id)
338 350  
339 351 assert !event.translatable?
340   -
  352 +
341 353  
342 354 environment.languages = ['en','pt','fr']
343 355 environment.save
344   - event.reload
  356 + event.reload
345 357 assert event.translatable?
346 358 end
347 359  
... ...
test/unit/short_filename_test.rb
... ... @@ -1,34 +0,0 @@
1   -require_relative "../test_helper"
2   -
3   -class NoosferoFilenamesTest < ActiveSupport::TestCase
4   -
5   - include ShortFilename
6   -
7   - should 'trunc to 15 chars the big filename' do
8   - assert_equal 'AGENDA(...).mp3', short_filename('AGENDA_CULTURA_-_FESTA_DE_VAQUEIROS_PONTO_DE_SERRA_PRETA_BAIXA.mp3',15)
9   - end
10   -
11   - should 'trunc to default limit the big filename' do
12   - assert_equal 'AGENDA_CULTURA_-_FESTA_DE_VAQUEIRO(...).mp3', short_filename('AGENDA_CULTURA_-_FESTA_DE_VAQUEIROS_PONTO_DE_SERRA_PRETA_BAIXA.mp3')
13   - end
14   -
15   - should 'does not trunc short filename' do
16   - assert_equal 'filename.mp3', short_filename('filename.mp3')
17   - end
18   -
19   - should 'highlight the file extansion' do
20   - assert_equal 'AGENDA(...) - MP3', short_filename_upper_ext('AGENDA_CULTURA_-_FESTA_DE_VAQUEIROS_PONTO_DE_SERRA_PRETA_BAIXA.mp3',15)
21   -
22   - assert_equal 'AGENDA - MP3', short_filename_upper_ext('AGENDA.mp3',15)
23   - end
24   -
25   - should 'return the full filename if its size is smaller than the limit' do
26   - assert_equal 'AGENDA', shrink('AGENDA', 'mp3', 15)
27   - end
28   -
29   - should 'shrink the filename if its size is bigger than the limit' do
30   - assert_equal 'AGENDA(...)', shrink('AGENDA_CULTURA_-_FESTA_DE_VAQUEIROS_PONTO_DE_SERRA_PRETA_BAIXA', 'mp3', 14)
31   - end
32   -
33   -end
34   -