Commit 4ef79c2dbe9eb60b91a367e48cfb5a4bcc0f7834
Exists in
master
and in
29 other branches
Merge branch 'master' into move-article-folder
Conflicts: app/helpers/forms_helper.rb
Showing
159 changed files
with
5612 additions
and
622 deletions
Show diff stats
app/controllers/application_controller.rb
@@ -114,7 +114,9 @@ class ApplicationController < ActionController::Base | @@ -114,7 +114,9 @@ class ApplicationController < ActionController::Base | ||
114 | # plugin to the current controller being initialized. | 114 | # plugin to the current controller being initialized. |
115 | def init_noosfero_plugins_controller_filters | 115 | def init_noosfero_plugins_controller_filters |
116 | plugins.each do |plugin| | 116 | plugins.each do |plugin| |
117 | - plugin.send(self.class.name.underscore + '_filters').each do |plugin_filter| | 117 | + filters = plugin.send(self.class.name.underscore + '_filters') |
118 | + filters = [filters] if !filters.kind_of?(Array) | ||
119 | + filters.each do |plugin_filter| | ||
118 | self.class.send(plugin_filter[:type], plugin.class.name.underscore + '_' + plugin_filter[:method_name], (plugin_filter[:options] || {})) | 120 | self.class.send(plugin_filter[:type], plugin.class.name.underscore + '_' + plugin_filter[:method_name], (plugin_filter[:options] || {})) |
119 | self.class.send(:define_method, plugin.class.name.underscore + '_' + plugin_filter[:method_name], plugin_filter[:block]) | 121 | self.class.send(:define_method, plugin.class.name.underscore + '_' + plugin_filter[:method_name], plugin_filter[:block]) |
120 | end | 122 | end |
app/controllers/box_organizer_controller.rb
@@ -68,7 +68,8 @@ class BoxOrganizerController < ApplicationController | @@ -68,7 +68,8 @@ class BoxOrganizerController < ApplicationController | ||
68 | raise ArgumentError.new("Type %s is not allowed. Go away." % type) | 68 | raise ArgumentError.new("Type %s is not allowed. Go away." % type) |
69 | end | 69 | end |
70 | else | 70 | else |
71 | - @block_types = available_blocks | 71 | + @center_block_types = Box.acceptable_center_blocks & available_blocks |
72 | + @side_block_types = Box.acceptable_side_blocks & available_blocks | ||
72 | @boxes = boxes_holder.boxes | 73 | @boxes = boxes_holder.boxes |
73 | render :action => 'add_block', :layout => false | 74 | render :action => 'add_block', :layout => false |
74 | end | 75 | end |
app/controllers/my_profile/profile_design_controller.rb
@@ -5,7 +5,7 @@ class ProfileDesignController < BoxOrganizerController | @@ -5,7 +5,7 @@ class ProfileDesignController < BoxOrganizerController | ||
5 | protect 'edit_profile_design', :profile | 5 | protect 'edit_profile_design', :profile |
6 | 6 | ||
7 | def available_blocks | 7 | def available_blocks |
8 | - blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock ] | 8 | + blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock, HighlightsBlock ] |
9 | 9 | ||
10 | # blocks exclusive for organizations | 10 | # blocks exclusive for organizations |
11 | if profile.has_members? | 11 | if profile.has_members? |
app/controllers/public/account_controller.rb
@@ -25,11 +25,13 @@ class AccountController < ApplicationController | @@ -25,11 +25,13 @@ class AccountController < ApplicationController | ||
25 | 25 | ||
26 | # action to perform login to the application | 26 | # action to perform login to the application |
27 | def login | 27 | def login |
28 | - @user = User.new | ||
29 | - @person = @user.build_person | ||
30 | store_location(request.referer) unless session[:return_to] | 28 | store_location(request.referer) unless session[:return_to] |
31 | return unless request.post? | 29 | return unless request.post? |
32 | - self.current_user = User.authenticate(params[:user][:login], params[:user][:password], environment) if params[:user] | 30 | + |
31 | + self.current_user = plugins_alternative_authentication | ||
32 | + | ||
33 | + self.current_user ||= User.authenticate(params[:user][:login], params[:user][:password], environment) if params[:user] | ||
34 | + | ||
33 | if logged_in? | 35 | if logged_in? |
34 | if params[:remember_me] == "1" | 36 | if params[:remember_me] == "1" |
35 | self.current_user.remember_me | 37 | self.current_user.remember_me |
@@ -41,7 +43,6 @@ class AccountController < ApplicationController | @@ -41,7 +43,6 @@ class AccountController < ApplicationController | ||
41 | end | 43 | end |
42 | else | 44 | else |
43 | session[:notice] = _('Incorrect username or password') if redirect? | 45 | session[:notice] = _('Incorrect username or password') if redirect? |
44 | - redirect_to :back if redirect? | ||
45 | end | 46 | end |
46 | end | 47 | end |
47 | 48 | ||
@@ -56,6 +57,11 @@ class AccountController < ApplicationController | @@ -56,6 +57,11 @@ class AccountController < ApplicationController | ||
56 | 57 | ||
57 | # action to register an user to the application | 58 | # action to register an user to the application |
58 | def signup | 59 | def signup |
60 | + if @plugins.dispatch(:allow_user_registration).include?(false) | ||
61 | + redirect_back_or_default(:controller => 'home') | ||
62 | + session[:notice] = _("This environment doesn't allow user registration.") | ||
63 | + end | ||
64 | + | ||
59 | @invitation_code = params[:invitation_code] | 65 | @invitation_code = params[:invitation_code] |
60 | begin | 66 | begin |
61 | if params[:user] | 67 | if params[:user] |
@@ -125,6 +131,10 @@ class AccountController < ApplicationController | @@ -125,6 +131,10 @@ class AccountController < ApplicationController | ||
125 | # | 131 | # |
126 | # Posts back. | 132 | # Posts back. |
127 | def forgot_password | 133 | def forgot_password |
134 | + if @plugins.dispatch(:allow_password_recovery).include?(false) | ||
135 | + redirect_back_or_default(:controller => 'home') | ||
136 | + session[:notice] = _("This environment doesn't allow password recovery.") | ||
137 | + end | ||
128 | @change_password = ChangePassword.new(params[:change_password]) | 138 | @change_password = ChangePassword.new(params[:change_password]) |
129 | 139 | ||
130 | if request.post? | 140 | if request.post? |
@@ -316,4 +326,13 @@ class AccountController < ApplicationController | @@ -316,4 +326,13 @@ class AccountController < ApplicationController | ||
316 | end | 326 | end |
317 | end | 327 | end |
318 | 328 | ||
329 | + def plugins_alternative_authentication | ||
330 | + user = nil | ||
331 | + @plugins.each do |plugin| | ||
332 | + user = plugin.alternative_authentication | ||
333 | + break unless user.nil? | ||
334 | + end | ||
335 | + user | ||
336 | + end | ||
337 | + | ||
319 | end | 338 | end |
app/controllers/public/content_viewer_controller.rb
@@ -2,6 +2,8 @@ class ContentViewerController < ApplicationController | @@ -2,6 +2,8 @@ class ContentViewerController < ApplicationController | ||
2 | 2 | ||
3 | needs_profile | 3 | needs_profile |
4 | 4 | ||
5 | + before_filter :comment_author, :only => :edit_comment | ||
6 | + | ||
5 | helper ProfileHelper | 7 | helper ProfileHelper |
6 | helper TagsHelper | 8 | helper TagsHelper |
7 | 9 | ||
@@ -121,6 +123,27 @@ class ContentViewerController < ApplicationController | @@ -121,6 +123,27 @@ class ContentViewerController < ApplicationController | ||
121 | end | 123 | end |
122 | end | 124 | end |
123 | 125 | ||
126 | + def edit_comment | ||
127 | + path = params[:page].join('/') | ||
128 | + @page = profile.articles.find_by_path(path) | ||
129 | + @form_div = 'opened' | ||
130 | + @comment = @page.comments.find_by_id(params[:id]) | ||
131 | + if @comment | ||
132 | + if request.post? | ||
133 | + begin | ||
134 | + @comment.update_attributes(params[:comment]) | ||
135 | + session[:notice] = _('Comment succesfully updated') | ||
136 | + redirect_to :action => 'view_page', :profile => profile.identifier, :page => @comment.article.explode_path | ||
137 | + rescue | ||
138 | + session[:notice] = _('Comment could not be updated') | ||
139 | + end | ||
140 | + end | ||
141 | + else | ||
142 | + redirect_to @page.view_url | ||
143 | + session[:notice] = _('Could not find the comment in the article') | ||
144 | + end | ||
145 | + end | ||
146 | + | ||
124 | protected | 147 | protected |
125 | 148 | ||
126 | def add_comment | 149 | def add_comment |
@@ -198,4 +221,13 @@ class ContentViewerController < ApplicationController | @@ -198,4 +221,13 @@ class ContentViewerController < ApplicationController | ||
198 | end | 221 | end |
199 | end | 222 | end |
200 | 223 | ||
224 | + def comment_author | ||
225 | + comment = Comment.find_by_id(params[:id]) | ||
226 | + if comment | ||
227 | + render_access_denied if comment.author.blank? || comment.author != user | ||
228 | + else | ||
229 | + render_not_found | ||
230 | + end | ||
231 | + end | ||
232 | + | ||
201 | end | 233 | end |
app/helpers/application_helper.rb
@@ -265,9 +265,9 @@ module ApplicationHelper | @@ -265,9 +265,9 @@ module ApplicationHelper | ||
265 | 265 | ||
266 | VIEW_EXTENSIONS = %w[.rhtml .html.erb] | 266 | VIEW_EXTENSIONS = %w[.rhtml .html.erb] |
267 | 267 | ||
268 | - def partial_for_class_in_view_path(klass, view_path) | 268 | + def partial_for_class_in_view_path(klass, view_path, suffix = nil) |
269 | return nil if klass.nil? | 269 | return nil if klass.nil? |
270 | - name = klass.name.underscore | 270 | + name = [klass.name.underscore, suffix].compact.map(&:to_s).join('_') |
271 | 271 | ||
272 | search_name = String.new(name) | 272 | search_name = String.new(name) |
273 | if search_name.include?("/") | 273 | if search_name.include?("/") |
@@ -285,28 +285,17 @@ module ApplicationHelper | @@ -285,28 +285,17 @@ module ApplicationHelper | ||
285 | partial_for_class_in_view_path(klass.superclass, view_path) | 285 | partial_for_class_in_view_path(klass.superclass, view_path) |
286 | end | 286 | end |
287 | 287 | ||
288 | - def partial_for_class(klass) | 288 | + def partial_for_class(klass, suffix=nil) |
289 | raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil? | 289 | raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil? |
290 | name = klass.name.underscore | 290 | name = klass.name.underscore |
291 | @controller.view_paths.each do |view_path| | 291 | @controller.view_paths.each do |view_path| |
292 | - partial = partial_for_class_in_view_path(klass, view_path) | 292 | + partial = partial_for_class_in_view_path(klass, view_path, suffix) |
293 | return partial if partial | 293 | return partial if partial |
294 | end | 294 | end |
295 | 295 | ||
296 | raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' | 296 | raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' |
297 | end | 297 | end |
298 | 298 | ||
299 | - def partial_for_task_class(klass, action) | ||
300 | - raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil? | ||
301 | - | ||
302 | - name = "#{klass.name.underscore}_#{action.to_s}" | ||
303 | - VIEW_EXTENSIONS.each do |ext| | ||
304 | - return name if File.exists?(File.join(RAILS_ROOT, 'app', 'views', params[:controller], '_'+name+ext)) | ||
305 | - end | ||
306 | - | ||
307 | - partial_for_task_class(klass.superclass, action) | ||
308 | - end | ||
309 | - | ||
310 | def view_for_profile_actions(klass) | 299 | def view_for_profile_actions(klass) |
311 | raise ArgumentError, 'No profile actions view for this class.' if klass.nil? | 300 | raise ArgumentError, 'No profile actions view for this class.' if klass.nil? |
312 | 301 | ||
@@ -1323,6 +1312,19 @@ module ApplicationHelper | @@ -1323,6 +1312,19 @@ module ApplicationHelper | ||
1323 | end | 1312 | end |
1324 | end | 1313 | end |
1325 | 1314 | ||
1315 | + def expirable_link_to(expired, content, url, options = {}) | ||
1316 | + if expired | ||
1317 | + options[:class] = (options[:class] || '') + ' disabled' | ||
1318 | + content_tag('a', ' '+content_tag('span', content), options) | ||
1319 | + else | ||
1320 | + link_to content, url, options | ||
1321 | + end | ||
1322 | + end | ||
1323 | + | ||
1324 | + def remove_content_button(action) | ||
1325 | + @plugins.dispatch("content_remove_#{action.to_s}", @page).include?(true) | ||
1326 | + end | ||
1327 | + | ||
1326 | def template_options(klass, field_name) | 1328 | def template_options(klass, field_name) |
1327 | return '' if klass.templates.count == 0 | 1329 | return '' if klass.templates.count == 0 |
1328 | return hidden_field_tag("#{field_name}[template_id]", klass.templates.first.id) if klass.templates.count == 1 | 1330 | return hidden_field_tag("#{field_name}[template_id]", klass.templates.first.id) if klass.templates.count == 1 |
@@ -1388,4 +1390,19 @@ module ApplicationHelper | @@ -1388,4 +1390,19 @@ module ApplicationHelper | ||
1388 | result | 1390 | result |
1389 | end | 1391 | end |
1390 | 1392 | ||
1393 | + def expirable_content_reference(content, action, text, url, options = {}) | ||
1394 | + reason = @plugins.dispatch("content_expire_#{action.to_s}", content).first | ||
1395 | + options[:title] = reason | ||
1396 | + expirable_link_to reason.present?, text, url, options | ||
1397 | + end | ||
1398 | + | ||
1399 | + def expirable_button(content, action, text, url, options = {}) | ||
1400 | + options[:class] = "button with-text icon-#{action.to_s}" | ||
1401 | + expirable_content_reference content, action, text, url, options | ||
1402 | + end | ||
1403 | + | ||
1404 | + def expirable_comment_link(content, action, text, url, options = {}) | ||
1405 | + options[:class] = "comment-footer comment-footer-link comment-footer-hide" | ||
1406 | + expirable_content_reference content, action, text, url, options | ||
1407 | + end | ||
1391 | end | 1408 | end |
app/helpers/boxes_helper.rb
@@ -162,9 +162,6 @@ module BoxesHelper | @@ -162,9 +162,6 @@ module BoxesHelper | ||
162 | # | 162 | # |
163 | # +box+ is always needed | 163 | # +box+ is always needed |
164 | def block_target(box, block = nil) | 164 | def block_target(box, block = nil) |
165 | - # FIXME hardcoded | ||
166 | - return '' if box.position == 1 | ||
167 | - | ||
168 | id = | 165 | id = |
169 | if block.nil? | 166 | if block.nil? |
170 | "end-of-box-#{box.id}" | 167 | "end-of-box-#{box.id}" |
@@ -172,14 +169,11 @@ module BoxesHelper | @@ -172,14 +169,11 @@ module BoxesHelper | ||
172 | "before-block-#{block.id}" | 169 | "before-block-#{block.id}" |
173 | end | 170 | end |
174 | 171 | ||
175 | - content_tag('div', ' ', :id => id, :class => 'block-target' ) + drop_receiving_element(id, :url => { :action => 'move_block', :target => id }, :accept => 'block', :hoverclass => 'block-target-hover') | 172 | + content_tag('div', ' ', :id => id, :class => 'block-target' ) + drop_receiving_element(id, :url => { :action => 'move_block', :target => id }, :accept => box.acceptable_blocks, :hoverclass => 'block-target-hover') |
176 | end | 173 | end |
177 | 174 | ||
178 | # makes the given block draggable so it can be moved away. | 175 | # makes the given block draggable so it can be moved away. |
179 | def block_handle(block) | 176 | def block_handle(block) |
180 | - # FIXME hardcoded | ||
181 | - return '' if block.box.position == 1 | ||
182 | - | ||
183 | draggable_element("block-#{block.id}", :revert => true) | 177 | draggable_element("block-#{block.id}", :revert => true) |
184 | end | 178 | end |
185 | 179 | ||
@@ -211,7 +205,7 @@ module BoxesHelper | @@ -211,7 +205,7 @@ module BoxesHelper | ||
211 | end | 205 | end |
212 | 206 | ||
213 | if block.editable? | 207 | if block.editable? |
214 | - buttons << lightbox_icon_button(:edit, _('Edit'), { :action => 'edit', :id => block.id }) | 208 | + buttons << colorbox_icon_button(:edit, _('Edit'), { :action => 'edit', :id => block.id }) |
215 | end | 209 | end |
216 | 210 | ||
217 | if !block.main? | 211 | if !block.main? |
app/helpers/cms_helper.rb
@@ -42,13 +42,25 @@ module CmsHelper | @@ -42,13 +42,25 @@ module CmsHelper | ||
42 | 42 | ||
43 | def display_spread_button(profile, article) | 43 | def display_spread_button(profile, article) |
44 | if profile.person? | 44 | if profile.person? |
45 | - button_without_text :spread, _('Spread this'), :action => 'publish', :id => article.id | 45 | + expirable_button article, :spread, _('Spread this'), :action => 'publish', :id => article.id |
46 | elsif profile.community? && environment.portal_community | 46 | elsif profile.community? && environment.portal_community |
47 | - button_without_text :spread, _('Spread this'), :action => 'publish_on_portal_community', :id => article.id | 47 | + expirable_button article, :spread, _('Spread this'), :action => 'publish_on_portal_community', :id => article.id |
48 | end | 48 | end |
49 | end | 49 | end |
50 | 50 | ||
51 | def display_delete_button(article) | 51 | def display_delete_button(article) |
52 | - button_without_text :delete, _('Delete'), { :action => 'destroy', :id => article.id }, :method => :post, :confirm => delete_article_message(article) | 52 | + expirable_button article, :delete, _('Delete'), { :action => 'destroy', :id => article.id }, :method => :post, :confirm => delete_article_message(article) |
53 | + end | ||
54 | + | ||
55 | + def expirable_button(content, action, title, url, options = {}) | ||
56 | + reason = @plugins.dispatch("content_expire_#{action.to_s}", content).first | ||
57 | + if reason.present? | ||
58 | + options[:class] = (options[:class] || '') + ' disabled' | ||
59 | + options[:disabled] = 'disabled' | ||
60 | + options.delete(:confirm) | ||
61 | + options.delete(:method) | ||
62 | + title = reason | ||
63 | + end | ||
64 | + button_without_text action.to_sym, title, url, options | ||
53 | end | 65 | end |
54 | end | 66 | end |
app/helpers/colorbox_helper.rb
@@ -8,6 +8,10 @@ module ColorboxHelper | @@ -8,6 +8,10 @@ module ColorboxHelper | ||
8 | button(type, label, url, colorbox_options(options)) | 8 | button(type, label, url, colorbox_options(options)) |
9 | end | 9 | end |
10 | 10 | ||
11 | + def colorbox_icon_button(type, label, url, options = {}) | ||
12 | + icon_button(type, label, url, colorbox_options(options)) | ||
13 | + end | ||
14 | + | ||
11 | # options must be an HTML options hash as passed to link_to etc. | 15 | # options must be an HTML options hash as passed to link_to etc. |
12 | # | 16 | # |
13 | # returns a new hash with colorbox class added. Keeps existing classes. | 17 | # returns a new hash with colorbox class added. Keeps existing classes. |
app/helpers/forms_helper.rb
@@ -155,6 +155,119 @@ module FormsHelper | @@ -155,6 +155,119 @@ module FormsHelper | ||
155 | return result | 155 | return result |
156 | end | 156 | end |
157 | 157 | ||
158 | + def date_field(name, value, format = '%Y-%m-%d', datepicker_options = {}, html_options = {}) | ||
159 | + datepicker_options[:disabled] ||= false | ||
160 | + datepicker_options[:alt_field] ||= '' | ||
161 | + datepicker_options[:alt_format] ||= '' | ||
162 | + datepicker_options[:append_text] ||= '' | ||
163 | + datepicker_options[:auto_size] ||= false | ||
164 | + datepicker_options[:button_image] ||= '' | ||
165 | + datepicker_options[:button_image_only] ||= false | ||
166 | + datepicker_options[:button_text] ||= '...' | ||
167 | + datepicker_options[:calculate_week] ||= 'jQuery.datepicker.iso8601Week' | ||
168 | + datepicker_options[:change_month] ||= false | ||
169 | + datepicker_options[:change_year] ||= false | ||
170 | + datepicker_options[:close_text] ||= _('Done') | ||
171 | + datepicker_options[:constrain_input] ||= true | ||
172 | + datepicker_options[:current_text] ||= _('Today') | ||
173 | + datepicker_options[:date_format] ||= 'mm/dd/yy' | ||
174 | + datepicker_options[:day_names] ||= [_('Sunday'), _('Monday'), _('Tuesday'), _('Wednesday'), _('Thursday'), _('Friday'), _('Saturday')] | ||
175 | + datepicker_options[:day_names_min] ||= [_('Su'), _('Mo'), _('Tu'), _('We'), _('Th'), _('Fr'), _('Sa')] | ||
176 | + datepicker_options[:day_names_short] ||= [_('Sun'), _('Mon'), _('Tue'), _('Wed'), _('Thu'), _('Fri'), _('Sat')] | ||
177 | + datepicker_options[:default_date] ||= nil | ||
178 | + datepicker_options[:duration] ||= 'normal' | ||
179 | + datepicker_options[:first_day] ||= 0 | ||
180 | + datepicker_options[:goto_current] ||= false | ||
181 | + datepicker_options[:hide_if_no_prev_next] ||= false | ||
182 | + datepicker_options[:is_rtl] ||= false | ||
183 | + datepicker_options[:max_date] ||= nil | ||
184 | + datepicker_options[:min_date] ||= nil | ||
185 | + datepicker_options[:month_names] ||= [_('January'), _('February'), _('March'), _('April'), _('May'), _('June'), _('July'), _('August'), _('September'), _('October'), _('November'), _('December')] | ||
186 | + datepicker_options[:month_names_short] ||= [_('Jan'), _('Feb'), _('Mar'), _('Apr'), _('May'), _('Jun'), _('Jul'), _('Aug'), _('Sep'), _('Oct'), _('Nov'), _('Dec')] | ||
187 | + datepicker_options[:navigation_as_date_format] ||= false | ||
188 | + datepicker_options[:next_text] ||= _('Next') | ||
189 | + datepicker_options[:number_of_months] ||= 1 | ||
190 | + datepicker_options[:prev_text] ||= _('Prev') | ||
191 | + datepicker_options[:select_other_months] ||= false | ||
192 | + datepicker_options[:short_year_cutoff] ||= '+10' | ||
193 | + datepicker_options[:show_button_panel] ||= false | ||
194 | + datepicker_options[:show_current_at_pos] ||= 0 | ||
195 | + datepicker_options[:show_month_after_year] ||= false | ||
196 | + datepicker_options[:show_on] ||= 'focus' | ||
197 | + datepicker_options[:show_options] ||= {} | ||
198 | + datepicker_options[:show_other_months] ||= false | ||
199 | + datepicker_options[:show_week] ||= false | ||
200 | + datepicker_options[:step_months] ||= 1 | ||
201 | + datepicker_options[:week_header] ||= _('Wk') | ||
202 | + datepicker_options[:year_range] ||= 'c-10:c+10' | ||
203 | + datepicker_options[:year_suffix] ||= '' | ||
204 | + | ||
205 | + element_id = html_options[:id] || 'datepicker-date' | ||
206 | + value = value.strftime(format) if value.present? | ||
207 | + method = datepicker_options[:time] ? 'datetimepicker' : 'datepicker' | ||
208 | + result = text_field_tag(name, value, html_options) | ||
209 | + result += | ||
210 | + " | ||
211 | + <script type='text/javascript'> | ||
212 | + jQuery('##{element_id}').#{method}({ | ||
213 | + disabled: #{datepicker_options[:disabled].to_json}, | ||
214 | + altField: #{datepicker_options[:alt_field].to_json}, | ||
215 | + altFormat: #{datepicker_options[:alt_format].to_json}, | ||
216 | + appendText: #{datepicker_options[:append_text].to_json}, | ||
217 | + autoSize: #{datepicker_options[:auto_size].to_json}, | ||
218 | + buttonImage: #{datepicker_options[:button_image].to_json}, | ||
219 | + buttonImageOnly: #{datepicker_options[:button_image_only].to_json}, | ||
220 | + buttonText: #{datepicker_options[:button_text].to_json}, | ||
221 | + calculateWeek: #{datepicker_options[:calculate_week].to_json}, | ||
222 | + changeMonth: #{datepicker_options[:change_month].to_json}, | ||
223 | + changeYear: #{datepicker_options[:change_year].to_json}, | ||
224 | + closeText: #{datepicker_options[:close_text].to_json}, | ||
225 | + constrainInput: #{datepicker_options[:constrain_input].to_json}, | ||
226 | + currentText: #{datepicker_options[:current_text].to_json}, | ||
227 | + dateFormat: #{datepicker_options[:date_format].to_json}, | ||
228 | + dayNames: #{datepicker_options[:day_names].to_json}, | ||
229 | + dayNamesMin: #{datepicker_options[:day_names_min].to_json}, | ||
230 | + dayNamesShort: #{datepicker_options[:day_names_short].to_json}, | ||
231 | + defaultDate: #{datepicker_options[:default_date].to_json}, | ||
232 | + duration: #{datepicker_options[:duration].to_json}, | ||
233 | + firstDay: #{datepicker_options[:first_day].to_json}, | ||
234 | + gotoCurrent: #{datepicker_options[:goto_current].to_json}, | ||
235 | + hideIfNoPrevNext: #{datepicker_options[:hide_if_no_prev_next].to_json}, | ||
236 | + isRTL: #{datepicker_options[:is_rtl].to_json}, | ||
237 | + maxDate: #{datepicker_options[:max_date].to_json}, | ||
238 | + minDate: #{datepicker_options[:min_date].to_json}, | ||
239 | + monthNames: #{datepicker_options[:month_names].to_json}, | ||
240 | + monthNamesShort: #{datepicker_options[:month_names_short].to_json}, | ||
241 | + navigationAsDateFormat: #{datepicker_options[:navigation_as_date_format].to_json}, | ||
242 | + nextText: #{datepicker_options[:next_text].to_json}, | ||
243 | + numberOfMonths: #{datepicker_options[:number_of_months].to_json}, | ||
244 | + prevText: #{datepicker_options[:prev_text].to_json}, | ||
245 | + selectOtherMonths: #{datepicker_options[:select_other_months].to_json}, | ||
246 | + shortYearCutoff: #{datepicker_options[:short_year_cutoff].to_json}, | ||
247 | + showButtonPanel: #{datepicker_options[:show_button_panel].to_json}, | ||
248 | + showCurrentAtPos: #{datepicker_options[:show_current_at_pos].to_json}, | ||
249 | + showMonthAfterYear: #{datepicker_options[:show_month_after_year].to_json}, | ||
250 | + showOn: #{datepicker_options[:show_on].to_json}, | ||
251 | + showOptions: #{datepicker_options[:show_options].to_json}, | ||
252 | + showOtherMonths: #{datepicker_options[:show_other_months].to_json}, | ||
253 | + showWeek: #{datepicker_options[:show_week].to_json}, | ||
254 | + stepMonths: #{datepicker_options[:step_months].to_json}, | ||
255 | + weekHeader: #{datepicker_options[:week_header].to_json}, | ||
256 | + yearRange: #{datepicker_options[:year_range].to_json}, | ||
257 | + yearSuffix: #{datepicker_options[:year_suffix].to_json} | ||
258 | + }) | ||
259 | + </script> | ||
260 | + " | ||
261 | + result | ||
262 | + end | ||
263 | + | ||
264 | + def date_range_field(from_name, to_name, from_value, to_value, format = '%Y-%m-%d', datepicker_options = {}, html_options = {}) | ||
265 | + from_id = html_options[:from_id] || 'datepicker-from-date' | ||
266 | + to_id = html_options[:to_id] || 'datepicker-to-date' | ||
267 | + return _('From') +' '+ date_field(from_name, from_value, format, datepicker_options, html_options.merge({:id => from_id})) + | ||
268 | + ' ' + _('until') +' '+ date_field(to_name, to_value, format, datepicker_options, html_options.merge({:id => to_id})) | ||
269 | + end | ||
270 | + | ||
158 | protected | 271 | protected |
159 | def self.next_id_number | 272 | def self.next_id_number |
160 | if defined? @@id_num | 273 | if defined? @@id_num |
app/models/box.rb
@@ -2,4 +2,76 @@ class Box < ActiveRecord::Base | @@ -2,4 +2,76 @@ class Box < ActiveRecord::Base | ||
2 | belongs_to :owner, :polymorphic => true | 2 | belongs_to :owner, :polymorphic => true |
3 | acts_as_list :scope => 'owner_id = #{owner_id} and owner_type = \'#{owner_type}\'' | 3 | acts_as_list :scope => 'owner_id = #{owner_id} and owner_type = \'#{owner_type}\'' |
4 | has_many :blocks, :dependent => :destroy, :order => 'position' | 4 | has_many :blocks, :dependent => :destroy, :order => 'position' |
5 | + | ||
6 | + def acceptable_blocks | ||
7 | + to_css_class_name central? ? Box.acceptable_center_blocks : Box.acceptable_side_blocks | ||
8 | + end | ||
9 | + | ||
10 | + def central? | ||
11 | + position == 1 | ||
12 | + end | ||
13 | + | ||
14 | + def self.acceptable_center_blocks | ||
15 | + [ ArticleBlock, | ||
16 | + BlogArchivesBlock, | ||
17 | + CategoriesBlock, | ||
18 | + CommunitiesBlock, | ||
19 | + EnterprisesBlock, | ||
20 | + EnvironmentStatisticsBlock, | ||
21 | + FansBlock, | ||
22 | + FavoriteEnterprisesBlock, | ||
23 | + FeedReaderBlock, | ||
24 | + FriendsBlock, | ||
25 | + HighlightsBlock, | ||
26 | + LinkListBlock, | ||
27 | + LoginBlock, | ||
28 | + MainBlock, | ||
29 | + MembersBlock, | ||
30 | + MyNetworkBlock, | ||
31 | + PeopleBlock, | ||
32 | + ProfileImageBlock, | ||
33 | + RawHTMLBlock, | ||
34 | + RecentDocumentsBlock, | ||
35 | + SellersSearchBlock, | ||
36 | + TagsBlock ] | ||
37 | + end | ||
38 | + | ||
39 | + def self.acceptable_side_blocks | ||
40 | + [ ArticleBlock, | ||
41 | + BlogArchivesBlock, | ||
42 | + CategoriesBlock, | ||
43 | + CommunitiesBlock, | ||
44 | + DisabledEnterpriseMessageBlock, | ||
45 | + EnterprisesBlock, | ||
46 | + EnvironmentStatisticsBlock, | ||
47 | + FansBlock, | ||
48 | + FavoriteEnterprisesBlock, | ||
49 | + FeaturedProductsBlock, | ||
50 | + FeedReaderBlock, | ||
51 | + FriendsBlock, | ||
52 | + HighlightsBlock, | ||
53 | + LinkListBlock, | ||
54 | + LocationBlock, | ||
55 | + LoginBlock, | ||
56 | + MembersBlock, | ||
57 | + MyNetworkBlock, | ||
58 | + PeopleBlock, | ||
59 | + ProductsBlock, | ||
60 | + ProfileImageBlock, | ||
61 | + ProfileInfoBlock, | ||
62 | + ProfileSearchBlock, | ||
63 | + RawHTMLBlock, | ||
64 | + RecentDocumentsBlock, | ||
65 | + SellersSearchBlock, | ||
66 | + SlideshowBlock, | ||
67 | + TagsBlock | ||
68 | + ] | ||
69 | + end | ||
70 | + | ||
71 | + private | ||
72 | + | ||
73 | + def to_css_class_name(blocks) | ||
74 | + blocks.map{ |block| block.to_s.underscore.tr('_', '-') } | ||
75 | + end | ||
76 | + | ||
5 | end | 77 | end |
app/models/comment.rb
@@ -28,6 +28,8 @@ class Comment < ActiveRecord::Base | @@ -28,6 +28,8 @@ class Comment < ActiveRecord::Base | ||
28 | 28 | ||
29 | xss_terminate :only => [ :body, :title, :name ], :on => 'validation' | 29 | xss_terminate :only => [ :body, :title, :name ], :on => 'validation' |
30 | 30 | ||
31 | + delegate :environment, :to => :source | ||
32 | + | ||
31 | def action_tracker_target | 33 | def action_tracker_target |
32 | self.article.profile | 34 | self.article.profile |
33 | end | 35 | end |
app/models/person.rb
@@ -71,10 +71,7 @@ class Person < Profile | @@ -71,10 +71,7 @@ class Person < Profile | ||
71 | Friendship.find(:all, :conditions => { :friend_id => person.id}).each { |friendship| friendship.destroy } | 71 | Friendship.find(:all, :conditions => { :friend_id => person.id}).each { |friendship| friendship.destroy } |
72 | end | 72 | end |
73 | 73 | ||
74 | - after_destroy :destroy_user | ||
75 | - def destroy_user | ||
76 | - self.user.destroy if self.user | ||
77 | - end | 74 | + belongs_to :user, :dependent => :delete |
78 | 75 | ||
79 | def can_control_scrap?(scrap) | 76 | def can_control_scrap?(scrap) |
80 | begin | 77 | begin |
app/models/task.rb
@@ -31,7 +31,7 @@ class Task < ActiveRecord::Base | @@ -31,7 +31,7 @@ class Task < ActiveRecord::Base | ||
31 | end | 31 | end |
32 | end | 32 | end |
33 | 33 | ||
34 | - belongs_to :requestor, :class_name => 'Person', :foreign_key => :requestor_id | 34 | + belongs_to :requestor, :class_name => 'Profile', :foreign_key => :requestor_id |
35 | belongs_to :target, :foreign_key => :target_id, :polymorphic => true | 35 | belongs_to :target, :foreign_key => :target_id, :polymorphic => true |
36 | 36 | ||
37 | validates_uniqueness_of :code, :on => :create | 37 | validates_uniqueness_of :code, :on => :create |
app/models/user.rb
@@ -30,7 +30,7 @@ class User < ActiveRecord::Base | @@ -30,7 +30,7 @@ class User < ActiveRecord::Base | ||
30 | 30 | ||
31 | after_create do |user| | 31 | after_create do |user| |
32 | user.person ||= Person.new | 32 | user.person ||= Person.new |
33 | - user.person.attributes = user.person_data.merge(:identifier => user.login, :user_id => user.id, :environment_id => user.environment_id) | 33 | + user.person.attributes = user.person_data.merge(:identifier => user.login, :user => user, :environment_id => user.environment_id) |
34 | user.person.name ||= user.login | 34 | user.person.name ||= user.login |
35 | user.person.visible = false unless user.activated? | 35 | user.person.visible = false unless user.activated? |
36 | user.person.save! | 36 | user.person.save! |
@@ -88,13 +88,13 @@ class User < ActiveRecord::Base | @@ -88,13 +88,13 @@ class User < ActiveRecord::Base | ||
88 | attr_protected :activated_at | 88 | attr_protected :activated_at |
89 | 89 | ||
90 | # Virtual attribute for the unencrypted password | 90 | # Virtual attribute for the unencrypted password |
91 | - attr_accessor :password | 91 | + attr_accessor :password, :name |
92 | 92 | ||
93 | validates_presence_of :login, :email | 93 | validates_presence_of :login, :email |
94 | validates_format_of :login, :with => Profile::IDENTIFIER_FORMAT, :if => (lambda {|user| !user.login.blank?}) | 94 | validates_format_of :login, :with => Profile::IDENTIFIER_FORMAT, :if => (lambda {|user| !user.login.blank?}) |
95 | validates_presence_of :password, :if => :password_required? | 95 | validates_presence_of :password, :if => :password_required? |
96 | - validates_presence_of :password_confirmation, :if => :password_required?, :if => (lambda {|user| !user.password.blank?}) | ||
97 | - validates_length_of :password, :within => 4..40, :if => :password_required?, :if => (lambda {|user| !user.password.blank?}) | 96 | + validates_presence_of :password_confirmation, :if => :password_required? |
97 | + validates_length_of :password, :within => 4..40, :if => :password_required? | ||
98 | validates_confirmation_of :password, :if => :password_required? | 98 | validates_confirmation_of :password, :if => :password_required? |
99 | validates_length_of :login, :within => 2..40, :if => (lambda {|user| !user.login.blank?}) | 99 | validates_length_of :login, :within => 2..40, :if => (lambda {|user| !user.login.blank?}) |
100 | validates_length_of :email, :within => 3..100, :if => (lambda {|user| !user.email.blank?}) | 100 | validates_length_of :email, :within => 3..100, :if => (lambda {|user| !user.email.blank?}) |
@@ -228,7 +228,12 @@ class User < ActiveRecord::Base | @@ -228,7 +228,12 @@ class User < ActiveRecord::Base | ||
228 | end | 228 | end |
229 | 229 | ||
230 | def name | 230 | def name |
231 | - person ? person.name : login | 231 | + name = (self[:name] || login) |
232 | + person.nil? ? name : (person.name || name) | ||
233 | + end | ||
234 | + | ||
235 | + def name= name | ||
236 | + self[:name] = name | ||
232 | end | 237 | end |
233 | 238 | ||
234 | def enable_email! | 239 | def enable_email! |
@@ -274,6 +279,11 @@ class User < ActiveRecord::Base | @@ -274,6 +279,11 @@ class User < ActiveRecord::Base | ||
274 | 15 # in minutes | 279 | 15 # in minutes |
275 | end | 280 | end |
276 | 281 | ||
282 | + | ||
283 | + def not_require_password! | ||
284 | + @is_password_required = false | ||
285 | + end | ||
286 | + | ||
277 | protected | 287 | protected |
278 | # before filter | 288 | # before filter |
279 | def encrypt_password | 289 | def encrypt_password |
@@ -282,9 +292,13 @@ class User < ActiveRecord::Base | @@ -282,9 +292,13 @@ class User < ActiveRecord::Base | ||
282 | self.password_type ||= User.system_encryption_method.to_s | 292 | self.password_type ||= User.system_encryption_method.to_s |
283 | self.crypted_password = encrypt(password) | 293 | self.crypted_password = encrypt(password) |
284 | end | 294 | end |
285 | - | 295 | + |
286 | def password_required? | 296 | def password_required? |
287 | - crypted_password.blank? || !password.blank? | 297 | + (crypted_password.blank? || !password.blank?) && is_password_required? |
298 | + end | ||
299 | + | ||
300 | + def is_password_required? | ||
301 | + @is_password_required.nil? ? true : @is_password_required | ||
288 | end | 302 | end |
289 | 303 | ||
290 | def make_activation_code | 304 | def make_activation_code |
@@ -292,6 +306,7 @@ class User < ActiveRecord::Base | @@ -292,6 +306,7 @@ class User < ActiveRecord::Base | ||
292 | end | 306 | end |
293 | 307 | ||
294 | def deliver_activation_code | 308 | def deliver_activation_code |
309 | + return if person.is_template? | ||
295 | User::Mailer.deliver_activation_code(self) unless self.activation_code.blank? | 310 | User::Mailer.deliver_activation_code(self) unless self.activation_code.blank? |
296 | end | 311 | end |
297 | 312 |
app/views/account/login.rhtml
@@ -13,6 +13,8 @@ | @@ -13,6 +13,8 @@ | ||
13 | 13 | ||
14 | <%= f.password_field :password %> | 14 | <%= f.password_field :password %> |
15 | 15 | ||
16 | + <%= @plugins.dispatch(:login_extra_contents).collect { |content| instance_eval(&content) }.join("") %> | ||
17 | + | ||
16 | <% button_bar do %> | 18 | <% button_bar do %> |
17 | <%= submit_button( 'login', _('Log in') )%> | 19 | <%= submit_button( 'login', _('Log in') )%> |
18 | <% if is_thickbox %> | 20 | <% if is_thickbox %> |
@@ -23,8 +25,13 @@ | @@ -23,8 +25,13 @@ | ||
23 | <% end %> | 25 | <% end %> |
24 | 26 | ||
25 | <% button_bar do %> | 27 | <% button_bar do %> |
26 | - <%= button :add, _("New user"), :controller => 'account', :action => 'signup' %> | ||
27 | - <%= button :help, _("I forgot my password!"), :controller => 'account', :action => 'forgot_password' %> | 28 | + <% unless @plugins.dispatch(:allow_user_registration).include?(false) %> |
29 | + <%= button :add, _("New user"), :controller => 'account', :action => 'signup' %> | ||
30 | + <% end %> | ||
31 | + | ||
32 | + <% unless @plugins.dispatch(:allow_password_recovery).include?(false) %> | ||
33 | + <%= button :help, _("I forgot my password!"), :controller => 'account', :action => 'forgot_password' %> | ||
34 | + <% end %> | ||
28 | <% end %> | 35 | <% end %> |
29 | 36 | ||
30 | </div><!-- end class="login-box" --> | 37 | </div><!-- end class="login-box" --> |
app/views/account/login_block.rhtml
@@ -9,25 +9,30 @@ | @@ -9,25 +9,30 @@ | ||
9 | @user ||= User.new | 9 | @user ||= User.new |
10 | %> | 10 | %> |
11 | 11 | ||
12 | - <% labelled_form_for :user, @user, | ||
13 | - :url => login_url do |f| %> | 12 | + <% labelled_form_for :user, @user, :url => login_url do |f| %> |
14 | 13 | ||
15 | - <%= f.text_field :login, :onchange => 'this.value = convToValidLogin( this.value )' %> | 14 | + <%= f.text_field :login, :onchange => 'this.value = convToValidLogin( this.value )' %> |
16 | 15 | ||
17 | - <%= f.password_field :password %> | 16 | + <%= f.password_field :password %> |
17 | + | ||
18 | + <%= @plugins.dispatch(:login_extra_contents).collect { |content| instance_eval(&content) }.join("") %> | ||
18 | 19 | ||
19 | <% button_bar do %> | 20 | <% button_bar do %> |
20 | <%= submit_button( 'login', _('Log in') )%> | 21 | <%= submit_button( 'login', _('Log in') )%> |
21 | - <%= link_to content_tag( 'span', _('New user') ), | ||
22 | - { :controller => 'account', :action => 'signup' }, | ||
23 | - :class => 'button with-text icon-add' %> | 22 | + <% unless @plugins.dispatch(:allow_user_registration).include?(false) %> |
23 | + <%= link_to content_tag( 'span', _('New user') ), | ||
24 | + { :controller => 'account', :action => 'signup' }, | ||
25 | + :class => 'button with-text icon-add' %> | ||
26 | + <% end %> | ||
24 | <% end %> | 27 | <% end %> |
25 | 28 | ||
26 | <% end %> | 29 | <% end %> |
27 | 30 | ||
28 | - <p class="forgot-passwd"> | ||
29 | - <%= link_to _("I forgot my password!"), :controller => 'account', :action => 'forgot_password' %> | ||
30 | - </p> | 31 | + <% unless @plugins.dispatch(:allow_password_recovery).include?(false) %> |
32 | + <p class="forgot-passwd"> | ||
33 | + <%= link_to _("I forgot my password!"), :controller => 'account', :action => 'forgot_password' %> | ||
34 | + </p> | ||
35 | + <% end %> | ||
31 | 36 | ||
32 | </div> | 37 | </div> |
33 | 38 |
@@ -0,0 +1,10 @@ | @@ -0,0 +1,10 @@ | ||
1 | +<% block_types.in_groups_of(2) do |block1, block2| %> | ||
2 | + <div style='float: left; width: 48%; padding-top: 2px;'> | ||
3 | + <%= labelled_radio_button(block1.description, :type, block1.name) %> | ||
4 | + </div> | ||
5 | + <% if block2 %> | ||
6 | + <div style='float: left; width: 48%; padding-top: 2px;'> | ||
7 | + <%= labelled_radio_button(block2.description, :type, block2.name) %> | ||
8 | + </div> | ||
9 | + <% end %> | ||
10 | +<% end %> |
app/views/box_organizer/_highlights_block.rhtml
1 | <strong><%= _('Highlights') %></strong> | 1 | <strong><%= _('Highlights') %></strong> |
2 | -<div id='edit-highlights-block'> | 2 | +<div id='edit-highlights-block' style='width:450px'> |
3 | <table id='highlights' class='noborder'> | 3 | <table id='highlights' class='noborder'> |
4 | <tr><th><%= _('Image') %></th><th><%= _('Address') %></th><th><%= _('Position') %></th><th><%= _('Title') %></th></tr> | 4 | <tr><th><%= _('Image') %></th><th><%= _('Address') %></th><th><%= _('Position') %></th><th><%= _('Title') %></th></tr> |
5 | <% for image in @block.images do %> | 5 | <% for image in @block.images do %> |
app/views/box_organizer/_link_list_block.rhtml
1 | <strong><%= _('Links') %></strong> | 1 | <strong><%= _('Links') %></strong> |
2 | -<div id='edit-link-list-block'> | 2 | +<div id='edit-link-list-block' style='width:450px'> |
3 | <table id='links' class='noborder'> | 3 | <table id='links' class='noborder'> |
4 | <tr><th><%= _('Icon') %></th><th><%= _('Name') %></th><th><%= _('Address') %></th></tr> | 4 | <tr><th><%= _('Icon') %></th><th><%= _('Name') %></th><th><%= _('Address') %></th></tr> |
5 | <% for link in @block.links do %> | 5 | <% for link in @block.links do %> |
app/views/box_organizer/_raw_html_block.rhtml
app/views/box_organizer/add_block.rhtml
1 | -<% form_tag do %> | ||
2 | - | ||
3 | - <p><%= _('In what area do you want to put your new block?') %></p> | ||
4 | - | ||
5 | - <%# FIXME hardcoded stuff %> | ||
6 | - <%= select_tag('box_id', options_for_select(@boxes.select { |item| item.position != 1 }.map {|item| [ _("Area %d") % item.position, item.id]})) %> | ||
7 | - | ||
8 | - <p><%= _('Select the type of block you want to add to your page.') %></p> | ||
9 | - | ||
10 | - <% @block_types.in_groups_of(2) do |block1, block2| %> | ||
11 | - <div style='float: left; width: 48%; padding-top: 2px;'> | ||
12 | - <%= radio_button_tag('type', block1.name) %> | ||
13 | - <%= label_tag "type_#{block1.name.downcase}", block1.description %> | 1 | +<div style='height:350px'> |
2 | + <% form_tag do %> | ||
3 | + | ||
4 | + <p><%= _('In what area do you want to put your new block?') %></p> | ||
5 | + | ||
6 | + <% @boxes.each do |box| %> | ||
7 | + <%= labelled_radio_button(_("Area %d") % box.position, :box_id, box.id, box.central?, { :class => 'box-position', 'data-position' => box.position }) %> | ||
8 | + <% end %> | ||
9 | + | ||
10 | + <script type="text/javascript"> | ||
11 | + (function ($) { | ||
12 | + $(document).ready(function () { | ||
13 | + $(".box-position").live('change', function () { | ||
14 | + if ($(this).attr('data-position') == '1') { | ||
15 | + $('#center-block-types').show(); | ||
16 | + $('#side-block-types').hide(); | ||
17 | + } else { | ||
18 | + $('#center-block-types').hide(); | ||
19 | + $('#side-block-types').show(); | ||
20 | + }; | ||
21 | + }); | ||
22 | + })})(jQuery); | ||
23 | + </script> | ||
24 | + | ||
25 | + <p><%= _('Select the type of block you want to add to your page.') %></p> | ||
26 | + | ||
27 | + <div id='center-block-types'> | ||
28 | + <%= render :partial => 'block_types', :locals => { :block_types => @center_block_types } %> | ||
14 | </div> | 29 | </div> |
15 | - <% if block2 %> | ||
16 | - <div style='float: left; width: 48%; padding-top: 2px;'> | ||
17 | - <%= radio_button_tag('type', block2.name) %> | ||
18 | - <%= label_tag "type_#{block2.name.downcase}", block2.description %> | ||
19 | - </div> | 30 | + |
31 | + <div id='side-block-types' style='display:none'> | ||
32 | + <%= render :partial => 'block_types', :locals => { :block_types => @side_block_types } %> | ||
33 | + </div> | ||
34 | + | ||
35 | + <br style='clear: both'/> | ||
36 | + | ||
37 | + <% button_bar do %> | ||
38 | + <%= submit_button(:add, _("Add")) %> | ||
39 | + <%= colorbox_close_button(_('Close')) %> | ||
20 | <% end %> | 40 | <% end %> |
21 | - <% end %> | ||
22 | - <br style='clear: both'/> | ||
23 | - | ||
24 | - <% button_bar do %> | ||
25 | - <%= submit_button(:add, _("Add")) %> | ||
26 | - <%= lightbox_close_button(_('Close')) %> | ||
27 | - <% end %> | ||
28 | 41 | ||
29 | -<% end %> | 42 | + <% end %> |
43 | +</div> |
app/views/box_organizer/edit.rhtml
1 | -<h2><%= _('Editing block') %></h2> | 1 | +<div style='width: 500px;'> |
2 | + <h2><%= _('Editing block') %></h2> | ||
2 | 3 | ||
3 | -<% form_tag(:action => 'save', :id => @block.id) do %> | 4 | + <% form_tag(:action => 'save', :id => @block.id) do %> |
4 | 5 | ||
5 | - <%= labelled_form_field(_('Custom title for this block: '), text_field(:block, :title, :maxlength => 20)) %> | 6 | + <%= labelled_form_field(_('Custom title for this block: '), text_field(:block, :title, :maxlength => 20)) %> |
6 | 7 | ||
7 | - <%= render :partial => partial_for_class(@block.class) %> | 8 | + <%= render :partial => partial_for_class(@block.class) %> |
8 | 9 | ||
9 | - <%= labelled_form_field _('Display this block:'), '' %> | ||
10 | - <div style='margin-left: 10px'> | ||
11 | - <%= radio_button(:block, :display, 'always') %> | ||
12 | - <%= label_tag('block_display_always', _('In all pages')) %> | ||
13 | - <br/> | ||
14 | - <%= radio_button(:block, :display, 'home_page_only') %> | ||
15 | - <%= label_tag('block_display_home_page_only', _('Only in the homepage')) %> | ||
16 | - <br/> | ||
17 | - <%= radio_button(:block, :display, 'except_home_page') %> | ||
18 | - <%= label_tag('block_display_except_home_page', _('In all pages, except in the homepage')) %> | ||
19 | - <br/> | ||
20 | - <%= radio_button(:block, :display, 'never') %> | ||
21 | - <%= label_tag('block_display_never', _("Don't display")) %> | ||
22 | - </div> | 10 | + <%= labelled_form_field _('Display this block:'), '' %> |
11 | + <div style='margin-left: 10px'> | ||
12 | + <%= radio_button(:block, :display, 'always') %> | ||
13 | + <%= label_tag('block_display_always', _('In all pages')) %> | ||
14 | + <br/> | ||
15 | + <%= radio_button(:block, :display, 'home_page_only') %> | ||
16 | + <%= label_tag('block_display_home_page_only', _('Only in the homepage')) %> | ||
17 | + <br/> | ||
18 | + <%= radio_button(:block, :display, 'except_home_page') %> | ||
19 | + <%= label_tag('block_display_except_home_page', _('In all pages, except in the homepage')) %> | ||
20 | + <br/> | ||
21 | + <%= radio_button(:block, :display, 'never') %> | ||
22 | + <%= label_tag('block_display_never', _("Don't display")) %> | ||
23 | + </div> | ||
23 | 24 | ||
24 | - <%= labelled_form_field(_('Show for:'), select(:block, :language, [ [ _('all languages'), 'all']] + Noosfero.locales.map {|key, value| [value, key]} )) %> | 25 | + <%= labelled_form_field(_('Show for:'), select(:block, :language, [ [ _('all languages'), 'all']] + Noosfero.locales.map {|key, value| [value, key]} )) %> |
25 | 26 | ||
26 | - <% button_bar do %> | ||
27 | - <%= submit_button(:save, _('Save')) %> | ||
28 | - <%= lightbox_close_button(_('Cancel')) %> | ||
29 | - <% end %> | 27 | + <% button_bar do %> |
28 | + <%= submit_button(:save, _('Save')) %> | ||
29 | + <%= colorbox_close_button(_('Cancel')) %> | ||
30 | + <% end %> | ||
30 | 31 | ||
31 | -<% end %> | 32 | + <% end %> |
33 | +</div> |
app/views/box_organizer/index.rhtml
1 | <h1><%= _('Editing sideboxes')%></h1> | 1 | <h1><%= _('Editing sideboxes')%></h1> |
2 | 2 | ||
3 | <% button_bar do %> | 3 | <% button_bar do %> |
4 | - <%= lightbox_button('add', _('Add a block'), { :action => 'add_block' }) %> | 4 | + <%= colorbox_button('add', _('Add a block'), { :action => 'add_block' }) %> |
5 | <%= button(:back, _('Back to control panel'), :controller => (profile.nil? ? 'admin_panel': 'profile_editor')) %> | 5 | <%= button(:back, _('Back to control panel'), :controller => (profile.nil? ? 'admin_panel': 'profile_editor')) %> |
6 | <% end %> | 6 | <% end %> |
app/views/cms/view.rhtml
@@ -49,13 +49,13 @@ | @@ -49,13 +49,13 @@ | ||
49 | <%= article.class.short_description %> | 49 | <%= article.class.short_description %> |
50 | </td> | 50 | </td> |
51 | <td class="article-controls"> | 51 | <td class="article-controls"> |
52 | - <%= button_without_text :edit, _('Edit'), :action => 'edit', :id => article.id %> | 52 | + <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit) %> |
53 | <%= button_without_text :eyes, _('Public view'), article.view_url %> | 53 | <%= button_without_text :eyes, _('Public view'), article.view_url %> |
54 | - <%= display_spread_button(profile, article) unless article.folder? %> | ||
55 | - <% if !environment.enabled?('cant_change_homepage') %> | ||
56 | - <%= button_without_text :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %> | 54 | + <%= display_spread_button(profile, article) unless article.folder? || remove_content_button(:spread)%> |
55 | + <% if !environment.enabled?('cant_change_homepage') && !remove_content_button(:home) %> | ||
56 | + <%= expirable_button article, :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %> | ||
57 | <% end %> | 57 | <% end %> |
58 | - <%= display_delete_button(article) %> | 58 | + <%= display_delete_button(article) if !remove_content_button(:delete) %> |
59 | </td> | 59 | </td> |
60 | </tr> | 60 | </tr> |
61 | <% end %> | 61 | <% end %> |
app/views/content_viewer/_article_toolbar.rhtml
1 | <div<%= user && " class='logged-in'" %>> | 1 | <div<%= user && " class='logged-in'" %>> |
2 | <div id="article-actions"> | 2 | <div id="article-actions"> |
3 | 3 | ||
4 | - <% if @page.allow_edit?(user) %> | ||
5 | - <%= link_to content_tag( 'span', label_for_edit_article(@page) ), | ||
6 | - profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }), | ||
7 | - :class => 'button with-text icon-edit' %> | 4 | + |
5 | + <% if @page.allow_edit?(user) && !remove_content_button(:edit) %> | ||
6 | + <% content = content_tag('span', label_for_edit_article(@page)) %> | ||
7 | + <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }) %> | ||
8 | + <%= expirable_button @page, :edit, content, url %> | ||
8 | <% end %> | 9 | <% end %> |
9 | 10 | ||
10 | - <% if @page != profile.home_page && !@page.has_posts? && @page.allow_delete?(user) %> | ||
11 | - <%= link_to content_tag( 'span', _('Delete') ), | ||
12 | - profile.admin_url.merge({ :controller => 'cms', :action => 'destroy', :id => @page}), | ||
13 | - :method => :post, | ||
14 | - :class => 'button with-text icon-delete', | ||
15 | - :confirm => delete_article_message(@page) %> | 11 | + <% if @page != profile.home_page && !@page.has_posts? && @page.allow_delete?(user) && !remove_content_button(:delete)%> |
12 | + <% content = content_tag( 'span', _('Delete') ) %> | ||
13 | + <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'destroy', :id => @page}) %> | ||
14 | + <% options = {:method => :post, :confirm => delete_article_message(@page)} %> | ||
15 | + <%= expirable_button @page, :delete, content, url, options %> | ||
16 | <% end %> | 16 | <% end %> |
17 | 17 | ||
18 | - <% if !@page.folder? && @page.allow_spread?(user) %> | 18 | + <% if !@page.folder? && @page.allow_spread?(user) && !remove_content_button(:spread) %> |
19 | + <% content = content_tag( 'span', _('Spread this') ) %> | ||
20 | + <% url = nil %> | ||
19 | <% if profile.kind_of?(Person) %> | 21 | <% if profile.kind_of?(Person) %> |
20 | - <%= link_to content_tag( 'span', _('Spread this') ), | ||
21 | - profile.admin_url.merge({ :controller => 'cms', :action => 'publish', :id => @page }), | ||
22 | - :class => 'button with-text icon-spread' %> | 22 | + <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'publish', :id => @page }) %> |
23 | <% elsif profile.kind_of?(Community) && environment.portal_community %> | 23 | <% elsif profile.kind_of?(Community) && environment.portal_community %> |
24 | - <%= link_to content_tag( 'span', _('Spread this') ), | ||
25 | - profile.admin_url.merge({ :controller => 'cms', :action => 'publish_on_portal_community', :id => @page }), | ||
26 | - :class => 'button with-text icon-spread' %> | 24 | + <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'publish_on_portal_community', :id => @page }) %> |
27 | <% end %> | 25 | <% end %> |
26 | + <%= expirable_button @page, :spread, content, url if url %> | ||
28 | <% end %> | 27 | <% end %> |
29 | 28 | ||
30 | <% if !@page.gallery? && @page.allow_create?(user) %> | 29 | <% if !@page.gallery? && @page.allow_create?(user) %> |
31 | - <%= link_to _('Add translation'), | ||
32 | - profile.admin_url.merge(:controller => 'cms', :action => 'new', | ||
33 | - :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)), | ||
34 | - :type => @page.type, :article => { :translation_of_id => @page.native_translation.id }), | ||
35 | - :class => 'button with-text icon-locale' if @page.translatable? && !@page.native_translation.language.blank? %> | 30 | + <% if @page.translatable? && !@page.native_translation.language.blank? && !remove_content_button(:locale) %> |
31 | + <% content = _('Add translation') %> | ||
32 | + <% parent_id = (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)) %> | ||
33 | + <% url = profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => parent_id, :type => @page.type, :article => { :translation_of_id => @page.native_translation.id })%> | ||
34 | + <%= expirable_button @page, :locale, content, url %> | ||
35 | + <% end %> | ||
36 | + | ||
36 | <%= colorbox_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)))) %> | 37 | <%= colorbox_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)))) %> |
37 | <% end %> | 38 | <% end %> |
38 | 39 | ||
@@ -40,8 +41,11 @@ | @@ -40,8 +41,11 @@ | ||
40 | <%= button('upload-file', _('Upload files'), profile.admin_url.merge(:controller => 'cms', :action => 'upload_files', :parent_id => (@page.folder? ? @page : @page.parent))) %> | 41 | <%= button('upload-file', _('Upload files'), profile.admin_url.merge(:controller => 'cms', :action => 'upload_files', :parent_id => (@page.folder? ? @page : @page.parent))) %> |
41 | <% end %> | 42 | <% end %> |
42 | 43 | ||
43 | - <% if !@page.allow_create?(user) && profile.community? && (@page.blog? || @page.parent && @page.parent.blog?) %> | ||
44 | - <%= link_to content_tag( 'span', _('Suggest an article') ), profile.admin_url.merge({ :controller => 'cms', :action => 'suggest_an_article'}), :id => 'suggest-article-link', :class => 'button with-text icon-new' %> | 44 | + <% if !@page.allow_create?(user) && profile.community? && (@page.blog? || @page.parent && @page.parent.blog?) && !remove_content_button(:suggest) %> |
45 | + <% content = content_tag( 'span', _('Suggest an article') ) %> | ||
46 | + <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'suggest_an_article'}) %> | ||
47 | + <% options = {:id => 'suggest-article-link'} %> | ||
48 | + <%= expirable_button @page, :suggest, content, url, options %> | ||
45 | <% end %> | 49 | <% end %> |
46 | 50 | ||
47 | <%= report_abuse(profile, :link, @page) %> | 51 | <%= report_abuse(profile, :link, @page) %> |
app/views/content_viewer/_comment.rhtml
@@ -63,6 +63,11 @@ | @@ -63,6 +63,11 @@ | ||
63 | <% end %> | 63 | <% end %> |
64 | <% end %> | 64 | <% end %> |
65 | 65 | ||
66 | + <% if comment.author && comment.author == user %> | ||
67 | + | ||
68 | + <%= expirable_comment_link comment, :edit, _('Edit'), {:action => 'edit_comment', :id => comment.id, :profile => profile.identifier} %> | ||
69 | + <% end %> | ||
70 | + | ||
66 | <% if logged_in? && (user == profile || user == comment.author || user.has_permission?(:moderate_comments, profile)) %> | 71 | <% if logged_in? && (user == profile || user == comment.author || user.has_permission?(:moderate_comments, profile)) %> |
67 | | 72 | |
68 | <%= link_to_function(_('Remove'), 'remove_comment(this, %s, %s); return false ;' % [url_for(:profile => params[:profile], :remove_comment => comment.id, :view => params[:view]).to_json, _('Are you sure you want to remove this comment and all its replies?').to_json], :class => 'comment-footer comment-footer-link comment-footer-hide remove-children') %> | 73 | <%= link_to_function(_('Remove'), 'remove_comment(this, %s, %s); return false ;' % [url_for(:profile => params[:profile], :remove_comment => comment.id, :view => params[:view]).to_json, _('Are you sure you want to remove this comment and all its replies?').to_json], :class => 'comment-footer comment-footer-link comment-footer-hide remove-children') %> |
app/views/content_viewer/_comment_form.rhtml
@@ -32,15 +32,17 @@ function submit_comment_form(button) { | @@ -32,15 +32,17 @@ function submit_comment_form(button) { | ||
32 | 32 | ||
33 | <div class="post_comment_box <%= @form_div %>"> | 33 | <div class="post_comment_box <%= @form_div %>"> |
34 | 34 | ||
35 | -<h4 onclick="var d = jQuery(this).parent('.post_comment_box'); | ||
36 | - if (d.hasClass('closed')) { | ||
37 | - d.removeClass('closed'); | ||
38 | - d.addClass('opened'); | ||
39 | - d.find('input[name=comment[title]], textarea').val(''); | ||
40 | - d.find('.comment_form input[name=comment[<%= focus_on %>]]').focus(); | ||
41 | - }"> | ||
42 | - <%= content_tag('a', '', :name => 'comment_form') + _('Post a comment') %> | ||
43 | -</h4> | 35 | +<% if display_link %> |
36 | + <h4 onclick="var d = jQuery(this).parent('.post_comment_box'); | ||
37 | + if (d.hasClass('closed')) { | ||
38 | + d.removeClass('closed'); | ||
39 | + d.addClass('opened'); | ||
40 | + d.find('input[name=comment[title]], textarea').val(''); | ||
41 | + d.find('.comment_form input[name=comment[<%= focus_on %>]]').focus(); | ||
42 | + }"> | ||
43 | + <%= content_tag('a', '', :name => 'comment_form') + _('Post a comment') %> | ||
44 | + </h4> | ||
45 | +<% end %> | ||
44 | 46 | ||
45 | <% unless pass_without_comment_captcha? %> | 47 | <% unless pass_without_comment_captcha? %> |
46 | <div id="recaptcha-container" style="display: none"> | 48 | <div id="recaptcha-container" style="display: none"> |
@@ -59,7 +61,7 @@ function submit_comment_form(button) { | @@ -59,7 +61,7 @@ function submit_comment_form(button) { | ||
59 | </script> | 61 | </script> |
60 | <% end %> | 62 | <% end %> |
61 | 63 | ||
62 | -<% form_tag( url_for(@page.view_url.merge({:only_path => true})), { :class => 'comment_form' } ) do %> | 64 | +<% form_tag( url, { :class => 'comment_form' } ) do %> |
63 | <%= hidden_field_tag(:confirm, 'false') %> | 65 | <%= hidden_field_tag(:confirm, 'false') %> |
64 | 66 | ||
65 | <%= required_fields_message %> | 67 | <%= required_fields_message %> |
@@ -84,7 +86,11 @@ function submit_comment_form(button) { | @@ -84,7 +86,11 @@ function submit_comment_form(button) { | ||
84 | 86 | ||
85 | <% button_bar do %> | 87 | <% button_bar do %> |
86 | <%= submit_button('add', _('Post comment'), :onclick => "submit_comment_form(this); return false") %> | 88 | <%= submit_button('add', _('Post comment'), :onclick => "submit_comment_form(this); return false") %> |
87 | - <%= button_to_function :cancel, _('Cancel'), "f=jQuery(this).parents('.post_comment_box'); f.removeClass('opened'); f.addClass('closed'); return false" %> | 89 | + <% if cancel_triggers_hide %> |
90 | + <%= button_to_function :cancel, _('Cancel'), "f=jQuery(this).parents('.post_comment_box'); f.removeClass('opened'); f.addClass('closed'); return false" %> | ||
91 | + <% else %> | ||
92 | + <%= button('cancel', _('Cancel'), {:action => 'view_page', :profile => profile.identifier, :page => @comment.article.explode_path})%> | ||
93 | + <% end %> | ||
88 | <% end %> | 94 | <% end %> |
89 | <% end %> | 95 | <% end %> |
90 | 96 |
app/views/content_viewer/view_page.rhtml
@@ -98,7 +98,7 @@ | @@ -98,7 +98,7 @@ | ||
98 | </ul> | 98 | </ul> |
99 | 99 | ||
100 | <% if @page.accept_comments? %> | 100 | <% if @page.accept_comments? %> |
101 | - <div id="page-comment-form"><%= render :partial => 'comment_form' %></div> | 101 | + <div id="page-comment-form"><%= render :partial => 'comment_form', :locals => {:url => url_for(@page.view_url.merge({:only_path => true})), :display_link => true, :cancel_triggers_hide => true}%></div> |
102 | <% end %> | 102 | <% end %> |
103 | </div><!-- end class="comments" --> | 103 | </div><!-- end class="comments" --> |
104 | 104 |
app/views/layouts/_javascript.rhtml
@@ -2,7 +2,8 @@ | @@ -2,7 +2,8 @@ | ||
2 | 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox', | 2 | 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox', |
3 | 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate', | 3 | 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate', |
4 | 'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput', | 4 | 'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput', |
5 | -'add-and-join', 'report-abuse', 'catalog', 'manage-products', :cache => 'cache-general' %> | 5 | +'add-and-join', 'report-abuse', 'catalog', 'manage-products', |
6 | +'jquery-ui-timepicker-addon', :cache => 'cache-general' %> | ||
6 | 7 | ||
7 | <% language = FastGettext.locale %> | 8 | <% language = FastGettext.locale %> |
8 | <% %w{messages methods}.each do |type| %> | 9 | <% %w{messages methods}.each do |type| %> |
app/views/layouts/application-ng.rhtml
@@ -56,10 +56,18 @@ | @@ -56,10 +56,18 @@ | ||
56 | <%= usermenu_logged_in %> | 56 | <%= usermenu_logged_in %> |
57 | </span> | 57 | </span> |
58 | <span class='not-logged-in' style='display: none'> | 58 | <span class='not-logged-in' style='display: none'> |
59 | - <%= _("<span class='login'>%s</span> <span class='or'>or</span> <span class='signup'>%s</span>") % [thickbox_inline_popup_link('<i class="icon-menu-login"></i><strong>' + _('Login') + '</strong>', login_url, 'inlineLoginBox', :id => 'link_login'), link_to('<strong>' + _('Sign up') + '</strong>', :controller => 'account', :action => 'signup') ] %> | 59 | + |
60 | + <%= _("<span class='login'>%s</span>") % thickbox_inline_popup_link('<i class="icon-menu-login"></i><strong>' + _('Login') + '</strong>', login_url, 'inlineLoginBox', :id => 'link_login') %> | ||
61 | + <%= @plugins.dispatch(:alternative_authentication_link).collect { |content| instance_eval(&content) }.join("") %> | ||
62 | + | ||
60 | <div id='inlineLoginBox' style='display: none;'> | 63 | <div id='inlineLoginBox' style='display: none;'> |
61 | <%= render :file => 'account/login', :locals => { :is_thickbox => true } %> | 64 | <%= render :file => 'account/login', :locals => { :is_thickbox => true } %> |
62 | </div> | 65 | </div> |
66 | + | ||
67 | + <% unless @plugins.dispatch(:allow_user_registration).include?(false) %> | ||
68 | + <%= _("<span class='or'>or</span> <span class='signup'>%s</span>") % link_to('<strong>' + _('Sign up') + '</strong>', :controller => 'account', :action => 'signup')%> | ||
69 | + <% end %> | ||
70 | + | ||
63 | </span> | 71 | </span> |
64 | <form action="/search" class="search_form" method="get" class="clean"> | 72 | <form action="/search" class="search_form" method="get" class="clean"> |
65 | <input name="query" size="15" title="<%=_('Search...')%>" onfocus="this.form.className='focused';" onblur="this.form.className=''" /> | 73 | <input name="query" size="15" title="<%=_('Search...')%>" onfocus="this.form.className='focused';" onblur="this.form.className=''" /> |
app/views/profile/_comment.rhtml
@@ -62,6 +62,10 @@ | @@ -62,6 +62,10 @@ | ||
62 | </script> | 62 | </script> |
63 | <% end %> | 63 | <% end %> |
64 | <%= report_abuse(comment.author, :comment_link, comment) if comment.author %> | 64 | <%= report_abuse(comment.author, :comment_link, comment) if comment.author %> |
65 | + <% if comment.author && comment.author == user %> | ||
66 | + <%= expirable_comment_link comment, :edit, _('Edit'), {:action => 'edit_comment', :id => comment.id, :profile => profile.identifier} %> | ||
67 | + <%= content_tag('span', ' | ', :class => 'comment-footer comment-footer-hide') %> | ||
68 | + <% end %> | ||
65 | <%= link_to_function _('Reply'), | 69 | <%= link_to_function _('Reply'), |
66 | "var f = add_comment_reply_form(this, %s); f.find('input[name=comment[title]], textarea').val(''); return false" % comment.id, | 70 | "var f = add_comment_reply_form(this, %s); f.find('input[name=comment[title]], textarea').val(''); return false" % comment.id, |
67 | :class => 'comment-footer comment-footer-link comment-footer-hide', | 71 | :class => 'comment-footer comment-footer-link comment-footer-hide', |
app/views/tasks/_task.rhtml
@@ -50,13 +50,13 @@ | @@ -50,13 +50,13 @@ | ||
50 | <% fields_for "tasks[#{task.id}][task]", task do |f| %> | 50 | <% fields_for "tasks[#{task.id}][task]", task do |f| %> |
51 | <% if task.accept_details %> | 51 | <% if task.accept_details %> |
52 | <div id="on-accept-information-<%=task.id%>" style="display: none"> | 52 | <div id="on-accept-information-<%=task.id%>" style="display: none"> |
53 | - <%= render :partial => partial_for_task_class(task.class, :accept_details), :locals => {:task => task, :f => f} %> | 53 | + <%= render :partial => partial_for_class(task.class, :accept_details), :locals => {:task => task, :f => f} %> |
54 | </div> | 54 | </div> |
55 | <% end %> | 55 | <% end %> |
56 | 56 | ||
57 | <% if task.reject_details %> | 57 | <% if task.reject_details %> |
58 | <div id="on-reject-information-<%=task.id%>" style="display: none"> | 58 | <div id="on-reject-information-<%=task.id%>" style="display: none"> |
59 | - <%= render :partial => partial_for_task_class(task.class, :reject_details), :locals => {:task => task, :f => f} %> | 59 | + <%= render :partial => partial_for_class(task.class, :reject_details), :locals => {:task => task, :f => f} %> |
60 | </div> | 60 | </div> |
61 | <% end %> | 61 | <% end %> |
62 | <% end %> | 62 | <% end %> |
config/routes.rb
@@ -19,6 +19,7 @@ ActionController::Routing::Routes.draw do |map| | @@ -19,6 +19,7 @@ ActionController::Routing::Routes.draw do |map| | ||
19 | 19 | ||
20 | # -- just remember to delete public/index.html. | 20 | # -- just remember to delete public/index.html. |
21 | # You can have the root of your site routed by hooking up '' | 21 | # You can have the root of your site routed by hooking up '' |
22 | + map.root :controller => "home", :conditions => { :if => lambda { |env| !Domain.hosting_profile_at(env[:host]) } } | ||
22 | map.connect '', :controller => "home", :conditions => { :if => lambda { |env| !Domain.hosting_profile_at(env[:host]) } } | 23 | map.connect '', :controller => "home", :conditions => { :if => lambda { |env| !Domain.hosting_profile_at(env[:host]) } } |
23 | map.home 'site/:action', :controller => 'home' | 24 | map.home 'site/:action', :controller => 'home' |
24 | 25 | ||
@@ -121,9 +122,12 @@ ActionController::Routing::Routes.draw do |map| | @@ -121,9 +122,12 @@ ActionController::Routing::Routes.draw do |map| | ||
121 | # cache stuff - hack | 122 | # cache stuff - hack |
122 | map.cache 'public/:action/:id', :controller => 'public' | 123 | map.cache 'public/:action/:id', :controller => 'public' |
123 | 124 | ||
125 | + map.connect ':profile/edit_comment/:id/*page', :controller => 'content_viewer', :action => 'edit_comment', :profile => /#{Noosfero.identifier_format}/ | ||
126 | + | ||
124 | # match requests for profiles that don't have a custom domain | 127 | # match requests for profiles that don't have a custom domain |
125 | map.homepage ':profile/*page', :controller => 'content_viewer', :action => 'view_page', :profile => /#{Noosfero.identifier_format}/, :conditions => { :if => lambda { |env| !Domain.hosting_profile_at(env[:host]) } } | 128 | map.homepage ':profile/*page', :controller => 'content_viewer', :action => 'view_page', :profile => /#{Noosfero.identifier_format}/, :conditions => { :if => lambda { |env| !Domain.hosting_profile_at(env[:host]) } } |
126 | 129 | ||
130 | + | ||
127 | # match requests for content in domains hosted for profiles | 131 | # match requests for content in domains hosted for profiles |
128 | map.connect '*page', :controller => 'content_viewer', :action => 'view_page' | 132 | map.connect '*page', :controller => 'content_viewer', :action => 'view_page' |
129 | 133 |
debian/noosfero-console
lib/noosfero/plugin.rb
@@ -12,7 +12,11 @@ class Noosfero::Plugin | @@ -12,7 +12,11 @@ class Noosfero::Plugin | ||
12 | end | 12 | end |
13 | 13 | ||
14 | def init_system | 14 | def init_system |
15 | - Dir.glob(File.join(Rails.root, 'config', 'plugins', '*')).select do |entry| | 15 | + enabled_plugins = Dir.glob(File.join(Rails.root, 'config', 'plugins', '*')) |
16 | + if Rails.env.test? && !enabled_plugins.include?(File.join(Rails.root, 'config', 'plugins', 'foo')) | ||
17 | + enabled_plugins << File.join(Rails.root, 'plugins', 'foo') | ||
18 | + end | ||
19 | + enabled_plugins.select do |entry| | ||
16 | File.directory?(entry) | 20 | File.directory?(entry) |
17 | end.each do |dir| | 21 | end.each do |dir| |
18 | plugin_name = File.basename(dir) | 22 | plugin_name = File.basename(dir) |
@@ -31,6 +35,11 @@ class Noosfero::Plugin | @@ -31,6 +35,11 @@ class Noosfero::Plugin | ||
31 | if plugin_dependencies_ok | 35 | if plugin_dependencies_ok |
32 | Rails.configuration.controller_paths << File.join(dir, 'controllers') | 36 | Rails.configuration.controller_paths << File.join(dir, 'controllers') |
33 | ActiveSupport::Dependencies.load_paths << File.join(dir, 'controllers') | 37 | ActiveSupport::Dependencies.load_paths << File.join(dir, 'controllers') |
38 | + controllers_folders = %w[public profile myprofile admin] | ||
39 | + controllers_folders.each do |folder| | ||
40 | + Rails.configuration.controller_paths << File.join(dir, 'controllers', folder) | ||
41 | + ActiveSupport::Dependencies.load_paths << File.join(dir, 'controllers', folder) | ||
42 | + end | ||
34 | [ ActiveSupport::Dependencies.load_paths, $:].each do |path| | 43 | [ ActiveSupport::Dependencies.load_paths, $:].each do |path| |
35 | path << File.join(dir, 'models') | 44 | path << File.join(dir, 'models') |
36 | path << File.join(dir, 'lib') | 45 | path << File.join(dir, 'lib') |
@@ -211,28 +220,6 @@ class Noosfero::Plugin | @@ -211,28 +220,6 @@ class Noosfero::Plugin | ||
211 | nil | 220 | nil |
212 | end | 221 | end |
213 | 222 | ||
214 | - # This is a generic hotspot for all controllers on Noosfero. | ||
215 | - # If any plugin wants to define filters to run on any controller, the name of | ||
216 | - # the hotspot must be in the following form: <underscored_controller_name>_filters. | ||
217 | - # Example: for ProfileController the hotspot is profile_controller_filters | ||
218 | - # | ||
219 | - # -> Adds a filter to a controller | ||
220 | - # returns = { :type => type, | ||
221 | - # :method_name => method_name, | ||
222 | - # :options => {:opt1 => opt1, :opt2 => opt2}, | ||
223 | - # :block => Proc or lambda block} | ||
224 | - # type = 'before_filter' or 'after_filter' | ||
225 | - # method_name = The name of the filter | ||
226 | - # option = Filter options, like :only or :except | ||
227 | - # block = Block that the filter will call | ||
228 | - def method_missing(method, *args, &block) | ||
229 | - if method.to_s =~ /^(.+)_controller_filters$/ | ||
230 | - [] | ||
231 | - else | ||
232 | - super | ||
233 | - end | ||
234 | - end | ||
235 | - | ||
236 | # This method will be called just before a comment is saved to the database. | 223 | # This method will be called just before a comment is saved to the database. |
237 | # | 224 | # |
238 | # It can modify the comment in several ways. In special, a plugin can call | 225 | # It can modify the comment in several ways. In special, a plugin can call |
@@ -333,4 +320,71 @@ class Noosfero::Plugin | @@ -333,4 +320,71 @@ class Noosfero::Plugin | ||
333 | nil | 320 | nil |
334 | end | 321 | end |
335 | 322 | ||
323 | + # -> Add an alternative authentication method. | ||
324 | + # Your plugin have to make the access control and return the logged user. | ||
325 | + # returns = User | ||
326 | + def alternative_authentication | ||
327 | + nil | ||
328 | + end | ||
329 | + | ||
330 | + # -> Adds adicional link to make the user authentication | ||
331 | + # returns = lambda block that creates html code | ||
332 | + def alternative_authentication_link | ||
333 | + nil | ||
334 | + end | ||
335 | + | ||
336 | + # -> Allow or not user registration | ||
337 | + # returns = boolean | ||
338 | + def allow_user_registration | ||
339 | + true | ||
340 | + end | ||
341 | + | ||
342 | + # -> Allow or not password recovery by users | ||
343 | + # returns = boolean | ||
344 | + def allow_password_recovery | ||
345 | + true | ||
346 | + end | ||
347 | + | ||
348 | + # -> Adds fields to the login form | ||
349 | + # returns = lambda block that creates html code | ||
350 | + def login_extra_contents | ||
351 | + nil | ||
352 | + end | ||
353 | + | ||
354 | + def method_missing(method, *args, &block) | ||
355 | + # This is a generic hotspot for all controllers on Noosfero. | ||
356 | + # If any plugin wants to define filters to run on any controller, the name of | ||
357 | + # the hotspot must be in the following form: <underscored_controller_name>_filters. | ||
358 | + # Example: for ProfileController the hotspot is profile_controller_filters | ||
359 | + # | ||
360 | + # -> Adds a filter to a controller | ||
361 | + # returns = { :type => type, | ||
362 | + # :method_name => method_name, | ||
363 | + # :options => {:opt1 => opt1, :opt2 => opt2}, | ||
364 | + # :block => Proc or lambda block} | ||
365 | + # type = 'before_filter' or 'after_filter' | ||
366 | + # method_name = The name of the filter | ||
367 | + # option = Filter options, like :only or :except | ||
368 | + # block = Block that the filter will call | ||
369 | + if method.to_s =~ /^(.+)_controller_filters$/ | ||
370 | + [] | ||
371 | + # -> Removes the action button from the content | ||
372 | + # returns = boolean | ||
373 | + elsif method.to_s =~ /^content_remove_(#{content_actions.join('|')})$/ | ||
374 | + nil | ||
375 | + # -> Expire the action button from the content | ||
376 | + # returns = string with reason of expiration | ||
377 | + elsif method.to_s =~ /^content_expire_(#{content_actions.join('|')})$/ | ||
378 | + nil | ||
379 | + else | ||
380 | + super | ||
381 | + end | ||
382 | + end | ||
383 | + | ||
384 | + private | ||
385 | + | ||
386 | + def content_actions | ||
387 | + %w[edit delete spread locale suggest home] | ||
388 | + end | ||
389 | + | ||
336 | end | 390 | end |
lib/noosfero/plugin/routes.rb
1 | -Dir.glob(File.join(Rails.root, 'config', 'plugins', '*', 'controllers')) do |dir| | ||
2 | - plugin_name = File.basename(File.dirname(dir)) | 1 | +plugins_root = Rails.env.test? ? 'plugins' : File.join('config', 'plugins') |
2 | + | ||
3 | +Dir.glob(File.join(Rails.root, plugins_root, '*', 'controllers')) do |controllers_dir| | ||
4 | + prefixes_by_folder = {'public' => 'plugin', | ||
5 | + 'profile' => 'profile/:profile/plugin', | ||
6 | + 'myprofile' => 'myprofile/:profile/plugin', | ||
7 | + 'admin' => 'admin/plugin'} | ||
8 | + | ||
9 | + controllers_by_folder = prefixes_by_folder.keys.inject({}) do |hash, folder| | ||
10 | + hash.merge!({folder => Dir.glob(File.join(controllers_dir, folder, '*')).map {|full_names| File.basename(full_names).gsub(/_controller.rb$/,'')}}) | ||
11 | + end | ||
12 | + | ||
13 | + plugin_name = File.basename(File.dirname(controllers_dir)) | ||
14 | + | ||
15 | + controllers_by_folder.each do |folder, controllers| | ||
16 | + controllers.each do |controller| | ||
17 | + controller_name = controller.gsub("#{plugin_name}_plugin_",'') | ||
18 | + map.connect "#{prefixes_by_folder[folder]}/#{plugin_name}/#{controller_name}/:action/:id", :controller => controller | ||
19 | + end | ||
20 | + end | ||
21 | + | ||
3 | map.connect 'plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin' | 22 | map.connect 'plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin' |
4 | - map.connect 'profile/:profile/plugins/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_profile' | 23 | + map.connect 'profile/:profile/plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_profile' |
5 | map.connect 'myprofile/:profile/plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_myprofile' | 24 | map.connect 'myprofile/:profile/plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_myprofile' |
6 | map.connect 'admin/plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_admin' | 25 | map.connect 'admin/plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_admin' |
7 | end | 26 | end |
8 | - |
plugins/custom_forms/controllers/custom_forms_plugin_myprofile_controller.rb
0 → 100644
@@ -0,0 +1,147 @@ | @@ -0,0 +1,147 @@ | ||
1 | +class CustomFormsPluginMyprofileController < MyProfileController | ||
2 | + | ||
3 | + protect 'post_content', :profile | ||
4 | + def index | ||
5 | + @forms = CustomFormsPlugin::Form.from(profile) | ||
6 | + end | ||
7 | + | ||
8 | + def create | ||
9 | + @form = CustomFormsPlugin::Form.new(:profile => profile) | ||
10 | + @fields = [] | ||
11 | + @empty_field = CustomFormsPlugin::Field.new | ||
12 | + if request.post? | ||
13 | + begin | ||
14 | + @form.update_attributes!(params[:form]) | ||
15 | + params[:fields] = format_kind(params[:fields]) | ||
16 | + params[:fields] = format_choices(params[:fields]) | ||
17 | + params[:fields] = set_form_id(params[:fields], @form.id) | ||
18 | + create_fields(new_fields(params)) | ||
19 | + session['notice'] = _('Form created') | ||
20 | + redirect_to :action => 'index' | ||
21 | + rescue Exception => exception | ||
22 | + logger.error(exception.to_s) | ||
23 | + session['notice'] = _('Form could not be created') | ||
24 | + end | ||
25 | + end | ||
26 | + end | ||
27 | + | ||
28 | + def edit | ||
29 | + @form = CustomFormsPlugin::Form.find(params[:id]) | ||
30 | + @fields = @form.fields | ||
31 | + @empty_field = CustomFormsPlugin::TextField.new | ||
32 | + if request.post? | ||
33 | + begin | ||
34 | + @form.update_attributes!(params[:form]) | ||
35 | + params[:fields] = format_kind(params[:fields]) | ||
36 | + params[:fields] = format_choices(params[:fields]) | ||
37 | + remove_fields(params, @form) | ||
38 | + create_fields(new_fields(params)) | ||
39 | + update_fields(edited_fields(params)) | ||
40 | + session['notice'] = _('Form updated') | ||
41 | + redirect_to :action => 'index' | ||
42 | + rescue Exception => exception | ||
43 | + logger.error(exception.to_s) | ||
44 | + session['notice'] = _('Form could not be updated') | ||
45 | + end | ||
46 | + end | ||
47 | + end | ||
48 | + | ||
49 | + def remove | ||
50 | + @form = CustomFormsPlugin::Form.find(params[:id]) | ||
51 | + begin | ||
52 | + @form.destroy | ||
53 | + session[:notice] = _('Form removed') | ||
54 | + rescue | ||
55 | + session[:notice] = _('Form could not be removed') | ||
56 | + end | ||
57 | + redirect_to :action => 'index' | ||
58 | + end | ||
59 | + | ||
60 | + def submissions | ||
61 | + @form = CustomFormsPlugin::Form.find(params[:id]) | ||
62 | + @submissions = @form.submissions | ||
63 | + end | ||
64 | + | ||
65 | + def show_submission | ||
66 | + @submission = CustomFormsPlugin::Submission.find(params[:id]) | ||
67 | + @form = @submission.form | ||
68 | + end | ||
69 | + | ||
70 | + private | ||
71 | + | ||
72 | + def new_fields(params) | ||
73 | + result = params[:fields].map {|id, hash| hash.has_key?(:real_id) ? nil : hash}.compact | ||
74 | + result.delete_if {|field| field[:name].blank?} | ||
75 | + result | ||
76 | + end | ||
77 | + | ||
78 | + def edited_fields(params) | ||
79 | + params[:fields].map {|id, hash| hash.has_key?(:real_id) ? hash : nil}.compact | ||
80 | + end | ||
81 | + | ||
82 | + def create_fields(fields) | ||
83 | + fields.each do |field| | ||
84 | + case field[:type] | ||
85 | + when 'text_field' | ||
86 | + CustomFormsPlugin::TextField.create!(field) | ||
87 | + when 'select_field' | ||
88 | + CustomFormsPlugin::SelectField.create!(field) | ||
89 | + else | ||
90 | + CustomFormsPlugin::Field.create!(field) | ||
91 | + end | ||
92 | + end | ||
93 | + end | ||
94 | + | ||
95 | + def update_fields(fields) | ||
96 | + fields.each do |field_attrs| | ||
97 | + field = CustomFormsPlugin::Field.find(field_attrs.delete(:real_id)) | ||
98 | + field.attributes = field_attrs | ||
99 | + field.save! if field.changed? | ||
100 | + end | ||
101 | + end | ||
102 | + | ||
103 | + def format_kind(fields) | ||
104 | + fields.each do |id, field| | ||
105 | + next if field[:kind].blank? | ||
106 | + kind = field.delete(:kind) | ||
107 | + case kind | ||
108 | + when 'radio' | ||
109 | + field[:list] = false | ||
110 | + field[:multiple] = false | ||
111 | + when 'check_box' | ||
112 | + field[:list] = false | ||
113 | + field[:multiple] = true | ||
114 | + when 'select' | ||
115 | + field[:list] = true | ||
116 | + field[:multiple] = false | ||
117 | + when 'multiple_select' | ||
118 | + field[:list] = true | ||
119 | + field[:multiple] = true | ||
120 | + end | ||
121 | + end | ||
122 | + fields | ||
123 | + end | ||
124 | + | ||
125 | + def format_choices(fields) | ||
126 | + fields.each do |id, field| | ||
127 | + next if !field.has_key?(:choices) | ||
128 | + field[:choices] = field[:choices].map {|key, value| value}.inject({}) do |result, choice| | ||
129 | + hash = (choice[:name].blank? || choice[:value].blank?) ? {} : {choice[:name] => choice[:value]} | ||
130 | + result.merge!(hash) | ||
131 | + end | ||
132 | + end | ||
133 | + fields | ||
134 | + end | ||
135 | + | ||
136 | + def remove_fields(params, form) | ||
137 | + present_fields = params[:fields].map{|id, value| value}.collect {|field| field[:real_id]}.compact | ||
138 | + form.fields.each {|field| field.destroy if !present_fields.include?(field.id.to_s) } | ||
139 | + end | ||
140 | + | ||
141 | + def set_form_id(fields, form_id) | ||
142 | + fields.each do |id, field| | ||
143 | + field[:form_id] = form_id | ||
144 | + end | ||
145 | + fields | ||
146 | + end | ||
147 | +end |
plugins/custom_forms/controllers/custom_forms_plugin_profile_controller.rb
0 → 100644
@@ -0,0 +1,45 @@ | @@ -0,0 +1,45 @@ | ||
1 | +class CustomFormsPluginProfileController < ProfileController | ||
2 | + | ||
3 | + before_filter :has_access, :show | ||
4 | + | ||
5 | + def show | ||
6 | + @form = CustomFormsPlugin::Form.find(params[:id]) | ||
7 | + if user | ||
8 | + @submission ||= CustomFormsPlugin::Submission.find_by_form_id_and_profile_id(@form.id,user.id) | ||
9 | + @submission ||= CustomFormsPlugin::Submission.new(:form_id => @form.id, :profile_id => user.id) | ||
10 | + else | ||
11 | + @submission ||= CustomFormsPlugin::Submission.new(:form_id => @form.id) | ||
12 | + end | ||
13 | + if request.post? | ||
14 | + begin | ||
15 | + extend(CustomFormsPlugin::Helper) | ||
16 | + answers = build_answers(params[:submission], @form) | ||
17 | + failed_answers = answers.select {|answer| !answer.valid? } | ||
18 | + if failed_answers.empty? | ||
19 | + if !user | ||
20 | + @submission.author_name = params[:author_name] | ||
21 | + @submission.author_email = params[:author_email] | ||
22 | + end | ||
23 | + @submission.save! | ||
24 | + answers.map {|answer| answer.submission = @submission; answer.save!} | ||
25 | + else | ||
26 | + @submission.valid? | ||
27 | + failed_answers.each do |answer| | ||
28 | + @submission.errors.add(answer.field.name.to_sym, answer.errors[answer.field.slug.to_sym]) | ||
29 | + end | ||
30 | + end | ||
31 | + session[:notice] = _('Submission saved') | ||
32 | + redirect_to :action => 'show' | ||
33 | + rescue | ||
34 | + session[:notice] = _('Submission could not be saved') | ||
35 | + end | ||
36 | + end | ||
37 | + end | ||
38 | + | ||
39 | + private | ||
40 | + | ||
41 | + def has_access | ||
42 | + form = CustomFormsPlugin::Form.find(params[:id]) | ||
43 | + render_access_denied if !form.accessible_to(user) | ||
44 | + end | ||
45 | +end |
plugins/custom_forms/db/migrate/20120727162444_create_custom_forms_plugin_forms.rb
0 → 100644
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +class CreateCustomFormsPluginForms < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + create_table :custom_forms_plugin_forms do |t| | ||
4 | + t.string :name | ||
5 | + t.string :slug | ||
6 | + t.text :description | ||
7 | + t.references :profile | ||
8 | + t.datetime :begining | ||
9 | + t.datetime :ending | ||
10 | + t.boolean :report_submissions, :default => false | ||
11 | + t.boolean :on_membership, :default => false | ||
12 | + t.string :access | ||
13 | + t.timestamps | ||
14 | + end | ||
15 | + end | ||
16 | + | ||
17 | + def self.down | ||
18 | + drop_table :custom_forms_plugin_forms | ||
19 | + end | ||
20 | +end |
plugins/custom_forms/db/migrate/20120727174506_create_custom_forms_plugin_fields.rb
0 → 100644
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +class CreateCustomFormsPluginFields < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + create_table :custom_forms_plugin_fields do |t| | ||
4 | + t.string :name | ||
5 | + t.string :slug | ||
6 | + t.string :type | ||
7 | + t.string :default_value | ||
8 | + t.string :choices | ||
9 | + t.float :minimum | ||
10 | + t.float :maximum | ||
11 | + t.references :form | ||
12 | + t.boolean :mandatory, :default => false | ||
13 | + t.boolean :multiple | ||
14 | + t.boolean :list | ||
15 | + end | ||
16 | + end | ||
17 | + | ||
18 | + def self.down | ||
19 | + drop_table :custom_forms_plugin_fields | ||
20 | + end | ||
21 | +end |
plugins/custom_forms/db/migrate/20120727175250_create_custom_forms_plugin_answers.rb
0 → 100644
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +class CreateCustomFormsPluginAnswers < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + create_table :custom_forms_plugin_answers do |t| | ||
4 | + t.text :value | ||
5 | + t.references :field | ||
6 | + t.references :submission | ||
7 | + end | ||
8 | + end | ||
9 | + | ||
10 | + def self.down | ||
11 | + drop_table :custom_forms_plugin_answers | ||
12 | + end | ||
13 | +end |
plugins/custom_forms/db/migrate/20120727180512_create_custom_forms_plugin_submissions.rb
0 → 100644
@@ -0,0 +1,15 @@ | @@ -0,0 +1,15 @@ | ||
1 | +class CreateCustomFormsPluginSubmissions < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + create_table :custom_forms_plugin_submissions do |t| | ||
4 | + t.string :author_name | ||
5 | + t.string :author_email | ||
6 | + t.references :profile | ||
7 | + t.references :form | ||
8 | + t.timestamps | ||
9 | + end | ||
10 | + end | ||
11 | + | ||
12 | + def self.down | ||
13 | + drop_table :custom_forms_plugin_submissions | ||
14 | + end | ||
15 | +end |
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +require 'ext/role_assignment_trigger' | ||
2 | + | ||
3 | +class CustomFormsPlugin < Noosfero::Plugin | ||
4 | + | ||
5 | + def self.plugin_name | ||
6 | + "Custom Forms" | ||
7 | + end | ||
8 | + | ||
9 | + def self.plugin_description | ||
10 | + _("Enables the creation of forms.") | ||
11 | + end | ||
12 | + | ||
13 | + def stylesheet? | ||
14 | + true | ||
15 | + end | ||
16 | + | ||
17 | + def control_panel_buttons | ||
18 | + {:title => _('Manage Forms'), :icon => 'custom-forms', :url => {:controller => 'custom_forms_plugin_myprofile'}} | ||
19 | + end | ||
20 | + | ||
21 | +end |
@@ -0,0 +1,14 @@ | @@ -0,0 +1,14 @@ | ||
1 | +class CustomFormsPlugin::Answer < Noosfero::Plugin::ActiveRecord | ||
2 | + belongs_to :field, :class_name => 'CustomFormsPlugin::Field' | ||
3 | + belongs_to :submission, :class_name => 'CustomFormsPlugin::Submission' | ||
4 | + | ||
5 | + validates_presence_of :field | ||
6 | + validate :value_mandatory, :if => 'field.present?' | ||
7 | + | ||
8 | + def value_mandatory | ||
9 | + if field.mandatory && value.blank? | ||
10 | + errors.add(field.slug.to_sym, _("is mandatory.").fix_i18n) | ||
11 | + end | ||
12 | + end | ||
13 | +end | ||
14 | + |
@@ -0,0 +1,16 @@ | @@ -0,0 +1,16 @@ | ||
1 | +class CustomFormsPlugin::Field < ActiveRecord::Base | ||
2 | + set_table_name :custom_forms_plugin_fields | ||
3 | + | ||
4 | + validates_presence_of :form, :name | ||
5 | + validates_uniqueness_of :slug, :scope => :form_id | ||
6 | + | ||
7 | + belongs_to :form, :class_name => 'CustomFormsPlugin::Form', :dependent => :destroy | ||
8 | + has_many :answers, :class_name => 'CustomFormsPlugin::Answer' | ||
9 | + | ||
10 | + serialize :choices, Hash | ||
11 | + | ||
12 | + before_validation do |field| | ||
13 | + field.slug = field.name.to_slug if field.name.present? | ||
14 | + end | ||
15 | +end | ||
16 | + |
@@ -0,0 +1,70 @@ | @@ -0,0 +1,70 @@ | ||
1 | +class CustomFormsPlugin::Form < Noosfero::Plugin::ActiveRecord | ||
2 | + belongs_to :profile | ||
3 | + | ||
4 | + has_many :fields, :class_name => 'CustomFormsPlugin::Field' | ||
5 | + has_many :submissions, :class_name => 'CustomFormsPlugin::Submission' | ||
6 | + | ||
7 | + serialize :access | ||
8 | + | ||
9 | + validates_presence_of :profile, :name | ||
10 | + validates_uniqueness_of :slug, :scope => :profile_id | ||
11 | + validate :access_format | ||
12 | + | ||
13 | + before_validation do |form| | ||
14 | + form.slug = form.name.to_slug if form.name.present? | ||
15 | + form.access = nil if form.access.blank? | ||
16 | + end | ||
17 | + | ||
18 | + named_scope :from, lambda {|profile| {:conditions => {:profile_id => profile.id}}} | ||
19 | + named_scope :on_memberships, {:conditions => {:on_membership => true}} | ||
20 | +=begin | ||
21 | + named_scope :accessible_to lambda do |profile| | ||
22 | + #TODO should verify is profile is associated with the form owner | ||
23 | + profile_associated = ??? | ||
24 | + {:conditions => [" | ||
25 | + access IS NULL OR | ||
26 | + (access='logged' AND :profile_present) OR | ||
27 | + (access='associated' AND :profile_associated) OR | ||
28 | + :profile_id in access | ||
29 | + ", {:profile_present => profile.present?, :profile_associated => ???, :profile_id => profile.id}]} | ||
30 | + end | ||
31 | +=end | ||
32 | + | ||
33 | + def expired? | ||
34 | + (begining.present? && Time.now < begining) || (ending.present? && Time.now > ending) | ||
35 | + end | ||
36 | + | ||
37 | + def accessible_to(target) | ||
38 | + return true if access.nil? || target == profile | ||
39 | + return false if target.nil? | ||
40 | + return true if access == 'logged' | ||
41 | + return true if access == 'associated' && ((profile.organization? && profile.members.include?(target)) || (profile.person? && profile.friends.include?(target))) | ||
42 | + return true if access.kind_of?(Integer) && target.id == access | ||
43 | + return true if access.kind_of?(Array) && access.include?(target.id) | ||
44 | + end | ||
45 | + | ||
46 | + private | ||
47 | + | ||
48 | + def access_format | ||
49 | + if access.present? | ||
50 | + if access.kind_of?(String) | ||
51 | + if access != 'logged' && access != 'associated' | ||
52 | + errors.add(:access, _('Invalid string format of access.')) | ||
53 | + end | ||
54 | + elsif access.kind_of?(Integer) | ||
55 | + if !Profile.exists?(access) | ||
56 | + errors.add(:access, _('There is no profile with the provided id.')) | ||
57 | + end | ||
58 | + elsif access.kind_of?(Array) | ||
59 | + access.each do |value| | ||
60 | + if !value.kind_of?(Integer) || !Profile.exists?(value) | ||
61 | + errors.add(:access, _('There is no profile with the provided id.')) | ||
62 | + break | ||
63 | + end | ||
64 | + end | ||
65 | + else | ||
66 | + errors.add(:access, _('Invalid type format of access.')) | ||
67 | + end | ||
68 | + end | ||
69 | + end | ||
70 | +end |
@@ -0,0 +1,119 @@ | @@ -0,0 +1,119 @@ | ||
1 | +module CustomFormsPlugin::Helper | ||
2 | + def access_text(form) | ||
3 | + return _('Public') if form.access.nil? | ||
4 | + return _('Logged users') if form.access == 'logged' | ||
5 | + if form.access == 'associated' | ||
6 | + return _('Members') if form.profile.organization? | ||
7 | + return _('Friends') if form.profile.person? | ||
8 | + end | ||
9 | + return _('Custom') | ||
10 | + end | ||
11 | + | ||
12 | + def period_range(form) | ||
13 | + if form.begining.blank? && form.ending.blank? | ||
14 | + _('Always') | ||
15 | + elsif form.begining.present? && form.ending.blank? | ||
16 | + ('From %s') % time_format(form.begining) | ||
17 | + elsif form.begining.blank? && form.ending.present? | ||
18 | + _('Until %s') % time_format(form.ending) | ||
19 | + elsif form.begining.present? && form.ending.present? | ||
20 | + _('From %s until %s') % [time_format(form.begining), time_format(form.ending)] | ||
21 | + end | ||
22 | + end | ||
23 | + | ||
24 | + def time_format(time) | ||
25 | + minutes = (time.min == 0) ? '' : ':%M' | ||
26 | + hour = (time.hour == 0 && minutes.blank?) ? '' : ' %H' | ||
27 | + h = hour.blank? ? '' : 'h' | ||
28 | + time.strftime("%Y-%m-%d#{hour+minutes+h}") | ||
29 | + end | ||
30 | + | ||
31 | + # TODO add the custom option that should offer the user the hability to | ||
32 | + # choose the profiles one by one, using something like tokeninput | ||
33 | + def access_options(profile) | ||
34 | + associated = profile.organization? ? _('Members') : _('Friends') | ||
35 | + [ | ||
36 | + [_('Public'), nil ], | ||
37 | + [_('Logged users'), 'logged' ], | ||
38 | + [ associated, 'associated'], | ||
39 | + ] | ||
40 | + end | ||
41 | + | ||
42 | + def type_options | ||
43 | + [ | ||
44 | + [_('Text'), 'text_field' ], | ||
45 | + [_('Select'), 'select_field'] | ||
46 | + ] | ||
47 | + end | ||
48 | + | ||
49 | + def type_to_label(type) | ||
50 | + map = { | ||
51 | + 'text_field' => _('Text'), | ||
52 | + 'select_field' => _('Select') | ||
53 | + } | ||
54 | + map[type_for_options(type)] | ||
55 | + end | ||
56 | + | ||
57 | + def type_for_options(type) | ||
58 | + type.to_s.split(':').last.underscore | ||
59 | + end | ||
60 | + | ||
61 | + def display_custom_field(field, submission, form) | ||
62 | + answer = submission.answers.select{|answer| answer.field == field}.first | ||
63 | + field_tag = send("display_#{type_for_options(field.class)}",field, answer, form) | ||
64 | + if field.mandatory? && !radio_button?(field) && !check_box?(field) && submission.id.nil? | ||
65 | + required(labelled_form_field(field.name, field_tag)) | ||
66 | + else | ||
67 | + labelled_form_field(field.name, field_tag) | ||
68 | + end | ||
69 | + end | ||
70 | + | ||
71 | + def display_text_field(field, answer, form) | ||
72 | + value = answer.present? ? answer.value : field.default_value | ||
73 | + text_field(form, field.name.to_slug, :value => value, :disabled => answer.present?) | ||
74 | + end | ||
75 | + | ||
76 | + def display_select_field(field, answer, form) | ||
77 | + if field.list && field.multiple | ||
78 | + selected = answer.present? ? answer.value.split(',') : [] | ||
79 | + select_tag "#{form}[#{field.name.to_slug}]", options_for_select(field.choices.to_a, selected), :multiple => true, :size => field.choices.size, :disabled => answer.present? | ||
80 | + elsif !field.list && field.multiple | ||
81 | + field.choices.map do |name, value| | ||
82 | + default = answer.present? ? answer.value.split(',').include?(value) : false | ||
83 | + labelled_check_box name, "#{form}[#{field.name.to_slug}][#{value}]", '1', default, :disabled => answer.present? | ||
84 | + end.join("\n") | ||
85 | + elsif field.list && !field.multiple | ||
86 | + selected = answer.present? ? answer.value.split(',') : [] | ||
87 | + select_tag "#{form}[#{field.name.to_slug}]", options_for_select([['','']] + field.choices.to_a, selected), :disabled => answer.present? | ||
88 | + elsif !field.list && !field.multiple | ||
89 | + field.choices.map do |name, value| | ||
90 | + default = answer.present? ? answer.value == value : true | ||
91 | + labelled_radio_button name, "#{form}[#{field.name.to_slug}]", value, default, :disabled => answer.present? | ||
92 | + end.join("\n") | ||
93 | + end | ||
94 | + end | ||
95 | + | ||
96 | + def radio_button?(field) | ||
97 | + type_for_options(field.class) == 'select_field' && !field.list && !field.multiple | ||
98 | + end | ||
99 | + | ||
100 | + def check_box?(field) | ||
101 | + type_for_options(field.class) == 'select_field' && !field.list && field.multiple | ||
102 | + end | ||
103 | + | ||
104 | + def build_answers(submission, form) | ||
105 | + answers = [] | ||
106 | + submission.each do |slug, value| | ||
107 | + field = form.fields.select {|field| field.slug==slug}.first | ||
108 | + if value.kind_of?(String) | ||
109 | + final_value = value | ||
110 | + elsif value.kind_of?(Array) | ||
111 | + final_value = value.join(',') | ||
112 | + elsif value.kind_of?(Hash) | ||
113 | + final_value = value.map {|option, present| present == '1' ? option : nil}.compact.join(',') | ||
114 | + end | ||
115 | + answers << CustomFormsPlugin::Answer.new(:field => field, :value => final_value) | ||
116 | + end | ||
117 | + answers | ||
118 | + end | ||
119 | +end |
plugins/custom_forms/lib/custom_forms_plugin/membership_survey.rb
0 → 100644
@@ -0,0 +1,47 @@ | @@ -0,0 +1,47 @@ | ||
1 | +class CustomFormsPlugin::MembershipSurvey < Task | ||
2 | + | ||
3 | + settings_items :form_id, :submission | ||
4 | + validates_presence_of :form_id | ||
5 | + | ||
6 | + include CustomFormsPlugin::Helper | ||
7 | + | ||
8 | + def perform | ||
9 | + form = CustomFormsPlugin::Form.find(form_id) | ||
10 | + answers = build_answers(submission, form) | ||
11 | + s = CustomFormsPlugin::Submission.create!(:form => form, :profile => target) | ||
12 | + answers.map {|answer| answer.submission = s; answer.save!} | ||
13 | + end | ||
14 | + | ||
15 | + def title | ||
16 | + _("Membership survey") | ||
17 | + end | ||
18 | + | ||
19 | + def subject | ||
20 | + nil | ||
21 | + end | ||
22 | + | ||
23 | + def linked_subject | ||
24 | + nil | ||
25 | + end | ||
26 | + | ||
27 | + def information | ||
28 | + {:message => _('%{requestor} wants you to fill in some information.')} | ||
29 | + end | ||
30 | + | ||
31 | + def accept_details | ||
32 | + true | ||
33 | + end | ||
34 | + | ||
35 | + def icon | ||
36 | + {:type => :profile_image, :profile => requestor, :url => requestor.url} | ||
37 | + end | ||
38 | + | ||
39 | + def target_notification_message | ||
40 | + _('After joining %{requestor}, the administrators of this organization | ||
41 | + wants you to fill in some further information.') % {:requestor => requestor.name} | ||
42 | + end | ||
43 | + | ||
44 | + def target_notification_description | ||
45 | + _('%{requestor} wants to fill in some further information.') % {:requestor => requestor.name} | ||
46 | + end | ||
47 | +end |
plugins/custom_forms/lib/custom_forms_plugin/select_field.rb
0 → 100644
plugins/custom_forms/lib/custom_forms_plugin/submission.rb
0 → 100644
@@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
1 | +class CustomFormsPlugin::Submission < Noosfero::Plugin::ActiveRecord | ||
2 | + belongs_to :form, :class_name => 'CustomFormsPlugin::Form' | ||
3 | + belongs_to :profile | ||
4 | + | ||
5 | + has_many :answers, :class_name => 'CustomFormsPlugin::Answer' | ||
6 | + | ||
7 | + validates_presence_of :form | ||
8 | + validates_presence_of :author_name, :author_email, :if => lambda {|submission| submission.profile.nil?} | ||
9 | + validates_uniqueness_of :author_email, :scope => :form_id, :allow_nil => true | ||
10 | + validates_format_of :author_email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda {|submission| !submission.author_email.blank?}) | ||
11 | +end | ||
12 | + |
plugins/custom_forms/lib/custom_forms_plugin/text_field.rb
0 → 100644
@@ -0,0 +1,30 @@ | @@ -0,0 +1,30 @@ | ||
1 | +module RoleAssignmentTrigger | ||
2 | + def self.included(base) | ||
3 | + base.class_eval do | ||
4 | + before_create do |ra| | ||
5 | + profile = ra.resource | ||
6 | + person = ra.accessor | ||
7 | + ok = !profile.nil? && !person.nil? && profile.environment.present? | ||
8 | + if ok && profile.environment.plugin_enabled?(CustomFormsPlugin) && !person.is_member_of?(profile) | ||
9 | + CustomFormsPlugin::Form.from(profile).on_memberships.each do |form| | ||
10 | + CustomFormsPlugin::MembershipSurvey.create!(:requestor => profile, :target => person, :form_id => form.id) | ||
11 | + end | ||
12 | + end | ||
13 | + end | ||
14 | + | ||
15 | + after_destroy do |ra| | ||
16 | + profile = ra.resource | ||
17 | + person = ra.accessor | ||
18 | + ok = !profile.nil? && !person.nil? && profile.environment.present? | ||
19 | + if ok && profile.environment.plugin_enabled?(CustomFormsPlugin) && !person.is_member_of?(profile) | ||
20 | + CustomFormsPlugin::Form.from(profile).on_memberships.each do |form| | ||
21 | + task = person.tasks.pending.select {|task| task.kind_of?(CustomFormsPlugin::MembershipSurvey) && task.form_id == form.id}.first | ||
22 | + task.cancel if task | ||
23 | + end | ||
24 | + end | ||
25 | + end | ||
26 | + end | ||
27 | + end | ||
28 | +end | ||
29 | + | ||
30 | +RoleAssignment.send :include, RoleAssignmentTrigger |
@@ -0,0 +1,141 @@ | @@ -0,0 +1,141 @@ | ||
1 | +jQuery('.icon-edit').live('click', function() { | ||
2 | + elem = this; | ||
3 | + jQuery.fn.colorbox({ | ||
4 | + overlayClose: false, | ||
5 | + escKey: false, | ||
6 | + inline: true, | ||
7 | + href: function(){ | ||
8 | + id = jQuery(elem).attr('field_id'); | ||
9 | + type = jQuery('#fields_'+id+'_type').val().split('_')[0]; | ||
10 | + selector = '#edit-'+type+'-'+id | ||
11 | + jQuery(selector).show(); | ||
12 | + return selector | ||
13 | + } | ||
14 | + }); | ||
15 | + return false; | ||
16 | +}); | ||
17 | + | ||
18 | +jQuery('.remove-field').live('click', function(){ | ||
19 | + id = jQuery(this).attr('field_id'); | ||
20 | + jQuery('#field-'+id).slideDown(function(){ | ||
21 | + jQuery('#field-'+id).remove(); | ||
22 | + }); | ||
23 | + return false | ||
24 | +}); | ||
25 | + | ||
26 | +jQuery('.remove-option').live('click', function(){ | ||
27 | + field_id = jQuery(this).attr('field_id'); | ||
28 | + option_id = jQuery(this).attr('option_id'); | ||
29 | + selector = '#field-'+field_id+'-option-'+option_id | ||
30 | + jQuery(selector).slideDown(function(){ | ||
31 | + jQuery(selector).remove(); | ||
32 | + jQuery.colorbox.resize(); | ||
33 | + }); | ||
34 | + return false | ||
35 | +}); | ||
36 | + | ||
37 | +function updateEditText(id){ | ||
38 | + new_id = id+1 | ||
39 | + jQuery('#edit-text-'+id).attr('id', 'edit-text-'+new_id); | ||
40 | + input = jQuery('#edit-text-'+new_id+' input'); | ||
41 | + jQuery('#edit-text-'+new_id+' .colorbox-ok-button').attr('div_id', 'edit-text-'+new_id); | ||
42 | + input.attr('id', input.attr('id').replace(id,new_id)); | ||
43 | + input.attr('name', input.attr('name').replace(id,new_id)); | ||
44 | + label = jQuery('#edit-text-'+new_id+' label'); | ||
45 | + label.attr('for', label.attr('for').replace(id,new_id)); | ||
46 | +} | ||
47 | + | ||
48 | +function updateEditSelect(id){ | ||
49 | + new_id = id+1 | ||
50 | + jQuery('#edit-select-'+id).attr('id', 'edit-select-'+new_id); | ||
51 | + jQuery('#edit-select-'+new_id+' .colorbox-ok-button').attr('div_id', 'edit-select-'+new_id); | ||
52 | + jQuery('tr[id^=field-'+id+'-option').each(function(id, element){ | ||
53 | + jQuery(element).attr('id', jQuery(element).attr('id').replace('field-'+id,'field-'+new_id)); | ||
54 | + }); | ||
55 | + jQuery('#edit-select-'+new_id+' label').each(function(index, element){ | ||
56 | + label = jQuery(element); | ||
57 | + label.attr('for', label.attr('for').replace(id,new_id)); | ||
58 | + }); | ||
59 | + jQuery('#edit-select-'+new_id+' input').each(function(index, element){ | ||
60 | + input = jQuery(element); | ||
61 | + input.attr('id', input.attr('id').replace(id,new_id)); | ||
62 | + input.attr('name', input.attr('name').replace(id,new_id)); | ||
63 | + }); | ||
64 | + jQuery('#edit-select-'+new_id+' .remove-option').each(function(index, element){ | ||
65 | + jQuery(element).attr('field_id',new_id); | ||
66 | + }); | ||
67 | + jQuery('#edit-select-'+new_id+' .new-option').attr('field_id',new_id); | ||
68 | + jQuery('#edit-select-'+new_id+' #empty-option-'+id).attr('id','empty-option-'+new_id); | ||
69 | +} | ||
70 | + | ||
71 | +function updateEmptyField(id){ | ||
72 | + id = parseInt(id); | ||
73 | + empty_field = jQuery('#empty-field'); | ||
74 | + empty_field.attr('last_id', (id + 1).toString()); | ||
75 | + jQuery('#empty-field input').each(function(index, element){ | ||
76 | + new_id = jQuery(element).attr('id').replace(id,id+1); | ||
77 | + jQuery(element).attr('id', new_id); | ||
78 | + new_name = jQuery(element).attr('name').replace(id,id+1); | ||
79 | + jQuery(element).attr('name', new_name); | ||
80 | + }); | ||
81 | + jQuery('#empty-field select').each(function(index, element){ | ||
82 | + new_id = jQuery(element).attr('id').replace(id,id+1); | ||
83 | + jQuery(element).attr('id', new_id); | ||
84 | + new_name = jQuery(element).attr('name').replace(id,id+1); | ||
85 | + jQuery(element).attr('name', new_name); | ||
86 | + }); | ||
87 | + jQuery('#empty-field a').each(function(index, element){ | ||
88 | + jQuery(element).attr('field_id', id+1); | ||
89 | + }); | ||
90 | + updateEditText(id); | ||
91 | + updateEditSelect(id); | ||
92 | +} | ||
93 | + | ||
94 | +function updateEmptyOption(field_id, option_id){ | ||
95 | + field_id = parseInt(field_id); | ||
96 | + option_id = parseInt(option_id); | ||
97 | + new_option_id = option_id+1; | ||
98 | + empty_option = jQuery('#empty-option-'+field_id); | ||
99 | + empty_option.attr('option_id',new_option_id); | ||
100 | + jQuery('#empty-option-'+field_id+' .remove-option').attr('option_id', new_option_id); | ||
101 | + | ||
102 | + name_id = ' #fields_'+field_id+'_choices_'+option_id+'_name'; | ||
103 | + jQuery('#empty-option-'+field_id+name_id).attr('name', 'fields['+field_id+'][choices]['+new_option_id+'][name]'); | ||
104 | + jQuery('#empty-option-'+field_id+name_id).attr('id', 'fields_'+field_id+'_choices_'+new_option_id+'_name'); | ||
105 | + | ||
106 | + value_id = ' #fields_'+field_id+'_choices_'+option_id+'_value'; | ||
107 | + jQuery('#empty-option-'+field_id+value_id).attr('name', 'fields['+field_id+'][choices]['+new_option_id+'][value]'); | ||
108 | + jQuery('#empty-option-'+field_id+value_id).attr('id', 'fields_'+field_id+'_choices_'+new_option_id+'_value'); | ||
109 | +} | ||
110 | + | ||
111 | +jQuery('#new-field').live('click', function(){ | ||
112 | + empty_field = jQuery('#empty-field'); | ||
113 | + id = empty_field.attr('last_id'); | ||
114 | + edit_text = jQuery('#edit-text-'+id); | ||
115 | + edit_select = jQuery('#edit-select-'+id); | ||
116 | + new_field = empty_field.clone(); | ||
117 | + new_field.attr('id','field-'+id); | ||
118 | + new_field.insertBefore(empty_field).slideDown(); | ||
119 | + edit_text.clone().insertAfter(edit_text); | ||
120 | + edit_select.clone().insertAfter(edit_select); | ||
121 | + updateEmptyField(id); | ||
122 | + return false | ||
123 | +}); | ||
124 | + | ||
125 | +jQuery('.new-option').live('click', function(){ | ||
126 | + field_id = jQuery(this).attr('field_id'); | ||
127 | + empty_option = jQuery('#empty-option-'+field_id); | ||
128 | + option_id = empty_option.attr('option_id'); | ||
129 | + new_option = empty_option.clone(); | ||
130 | + new_option.attr('id','field-'+field_id+'-option-'+option_id); | ||
131 | + new_option.insertBefore(empty_option).slideDown(); | ||
132 | + jQuery.colorbox.resize(); | ||
133 | + updateEmptyOption(field_id, option_id); | ||
134 | + return false | ||
135 | +}); | ||
136 | + | ||
137 | +jQuery('.colorbox-ok-button').live('click', function(){ | ||
138 | + jQuery('#'+jQuery(this).attr('div_id')).hide(); | ||
139 | + jQuery.colorbox.close(); | ||
140 | + return false | ||
141 | +}); |
4.05 KB
@@ -0,0 +1,30 @@ | @@ -0,0 +1,30 @@ | ||
1 | +.controller-profile_editor a.control-panel-custom-forms, | ||
2 | +.controller-profile_editor .msie6 a.control-panel-custom-forms { | ||
3 | + background-image: url(/plugins/custom_forms/icons/custom-forms.png) | ||
4 | +} | ||
5 | + | ||
6 | +.action-table { | ||
7 | + width: 100%; | ||
8 | + overflow: hidden; | ||
9 | +} | ||
10 | + | ||
11 | +.action-table th, | ||
12 | +.action-table td{ | ||
13 | + text-align: center; | ||
14 | +} | ||
15 | + | ||
16 | +.action-table td{ | ||
17 | + cursor: move; | ||
18 | +} | ||
19 | + | ||
20 | +.action-table .actions{ | ||
21 | + white-space: nowrap; | ||
22 | +} | ||
23 | + | ||
24 | +.action-table .new-item{ | ||
25 | + background-color: #EEE; | ||
26 | +} | ||
27 | + | ||
28 | +.edit-information { | ||
29 | + display: none; | ||
30 | +} |
plugins/custom_forms/test/functional/custom_forms_plugin_myprofile_controller_test.rb
0 → 100644
@@ -0,0 +1,114 @@ | @@ -0,0 +1,114 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | ||
2 | +require File.dirname(__FILE__) + '/../../controllers/custom_forms_plugin_myprofile_controller' | ||
3 | + | ||
4 | +# Re-raise errors caught by the controller. | ||
5 | +class CustomFormsPluginMyprofileController; def rescue_action(e) raise e end; end | ||
6 | + | ||
7 | +class CustomFormsPluginMyprofileControllerTest < ActionController::TestCase | ||
8 | + def setup | ||
9 | + @controller = CustomFormsPluginMyprofileController.new | ||
10 | + @request = ActionController::TestRequest.new | ||
11 | + @response = ActionController::TestResponse.new | ||
12 | + @profile = create_user('profile').person | ||
13 | + login_as(@profile.identifier) | ||
14 | + environment = Environment.default | ||
15 | + environment.enable_plugin(CustomFormsPlugin) | ||
16 | + end | ||
17 | + | ||
18 | + attr_reader :profile | ||
19 | + | ||
20 | + should 'list forms associated with profile' do | ||
21 | + another_profile = fast_create(Profile) | ||
22 | + f1 = CustomFormsPlugin::Form.create!(:profile => profile, :name => 'Free Software') | ||
23 | + f2 = CustomFormsPlugin::Form.create!(:profile => profile, :name => 'Open Source') | ||
24 | + f3 = CustomFormsPlugin::Form.create!(:profile => another_profile, :name => 'Open Source') | ||
25 | + | ||
26 | + get :index, :profile => profile.identifier | ||
27 | + | ||
28 | + assert_includes assigns(:forms), f1 | ||
29 | + assert_includes assigns(:forms), f2 | ||
30 | + assert_not_includes assigns(:forms), f3 | ||
31 | + end | ||
32 | + | ||
33 | + should 'destroy form' do | ||
34 | + form = CustomFormsPlugin::Form.create!(:profile => profile, :name => 'Free Software') | ||
35 | + assert CustomFormsPlugin::Form.exists?(form.id) | ||
36 | + post :remove, :profile => profile.identifier, :id => form.id | ||
37 | + assert !CustomFormsPlugin::Form.exists?(form.id) | ||
38 | + end | ||
39 | + | ||
40 | + should 'create a form' do | ||
41 | + format = '%Y-%m-%d %H:%M' | ||
42 | + begining = Time.now.strftime(format) | ||
43 | + ending = (Time.now + 1.day).strftime('%Y-%m-%d %H:%M') | ||
44 | + assert_difference CustomFormsPlugin::Form, :count, 1 do | ||
45 | + post :create, :profile => profile.identifier, | ||
46 | + :form => { | ||
47 | + :name => 'My Form', | ||
48 | + :access => 'logged', | ||
49 | + :begining => begining, | ||
50 | + :ending => ending, | ||
51 | + :description => 'Cool form'}, | ||
52 | + :fields => { | ||
53 | + 1 => { | ||
54 | + :name => 'Name', | ||
55 | + :default_value => 'Jack', | ||
56 | + :type => 'text_field' | ||
57 | + }, | ||
58 | + 2 => { | ||
59 | + :name => 'Color', | ||
60 | + :list => '1', | ||
61 | + :type => 'select_field', | ||
62 | + :choices => { | ||
63 | + 1 => {:name => 'Red', :value => 'red'}, | ||
64 | + 2 => {:name => 'Blue', :value => 'blue'}, | ||
65 | + 3 => {:name => 'Black', :value => 'black'} | ||
66 | + } | ||
67 | + } | ||
68 | + } | ||
69 | + end | ||
70 | + | ||
71 | + form = CustomFormsPlugin::Form.find_by_name('My Form') | ||
72 | + assert_equal 'logged', form.access | ||
73 | + assert_equal begining, form.begining.strftime(format) | ||
74 | + assert_equal ending, form.ending.strftime(format) | ||
75 | + assert_equal 'Cool form', form.description | ||
76 | + assert_equal 2, form.fields.count | ||
77 | + | ||
78 | + f1 = form.fields.first | ||
79 | + f2 = form.fields.last | ||
80 | + | ||
81 | + assert_equal 'Name', f1.name | ||
82 | + assert_equal 'Jack', f1.default_value | ||
83 | + assert f1.kind_of?(CustomFormsPlugin::TextField) | ||
84 | + | ||
85 | + assert_equal 'Color', f2.name | ||
86 | + assert_equal 'red', f2.choices['Red'] | ||
87 | + assert_equal 'blue', f2.choices['Blue'] | ||
88 | + assert_equal 'black', f2.choices['Black'] | ||
89 | + assert f2.list | ||
90 | + assert f2.kind_of?(CustomFormsPlugin::SelectField) | ||
91 | + end | ||
92 | + | ||
93 | + should 'edit a form' do | ||
94 | + form = CustomFormsPlugin::Form.create!(:profile => profile, :name => 'Free Software') | ||
95 | + field = CustomFormsPlugin::TextField.create!(:form => form, :name => 'License') | ||
96 | + format = '%Y-%m-%d %H:%M' | ||
97 | + begining = Time.now.strftime(format) | ||
98 | + ending = (Time.now + 1.day).strftime('%Y-%m-%d %H:%M') | ||
99 | + | ||
100 | + post :edit, :profile => profile.identifier, :id => form.id, | ||
101 | + :form => {:name => 'My Form', :access => 'logged', :begining => begining, :ending => ending, :description => 'Cool form'}, | ||
102 | + :fields => {1 => {:real_id => field.id.to_s, :name => 'Source'}} | ||
103 | + | ||
104 | + form.reload | ||
105 | + field.reload | ||
106 | + | ||
107 | + assert_equal 'logged', form.access | ||
108 | + assert_equal begining, form.begining.strftime(format) | ||
109 | + assert_equal ending, form.ending.strftime(format) | ||
110 | + assert_equal 'Cool form', form.description | ||
111 | + assert_equal 'Source', field.name | ||
112 | + end | ||
113 | +end | ||
114 | + |
plugins/custom_forms/test/unit/custom_forms_plugin/answer_test.rb
0 → 100644
@@ -0,0 +1,38 @@ | @@ -0,0 +1,38 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../../test/test_helper' | ||
2 | + | ||
3 | +class CustomFormsPlugin::AnswerTest < ActiveSupport::TestCase | ||
4 | + should 'validates presence of field' do | ||
5 | + answer = CustomFormsPlugin::Answer.new | ||
6 | + answer.valid? | ||
7 | + assert answer.errors.invalid?(:field) | ||
8 | + | ||
9 | + form = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => fast_create(Profile)) | ||
10 | + field = CustomFormsPlugin::Field.create!(:name => 'License', :form => form) | ||
11 | + answer.field = field | ||
12 | + answer.valid? | ||
13 | + assert !answer.errors.invalid?(:field) | ||
14 | + end | ||
15 | + | ||
16 | + should 'belong to a submission' do | ||
17 | + form = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => fast_create(Profile)) | ||
18 | + submission = CustomFormsPlugin::Submission.create!(:form => form, :profile => fast_create(Profile)) | ||
19 | + answer = CustomFormsPlugin::Answer.new | ||
20 | + answer.submission = submission | ||
21 | + | ||
22 | + assert_equal submission, answer.submission | ||
23 | + end | ||
24 | + | ||
25 | + should 'require presence of value if field is mandatory' do | ||
26 | + form = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => fast_create(Profile)) | ||
27 | + field = CustomFormsPlugin::Field.create!(:name => 'License', :form => form, :mandatory => true) | ||
28 | + answer = CustomFormsPlugin::Answer.new(:field => field) | ||
29 | + answer.valid? | ||
30 | + assert answer.errors.invalid?(field.slug.to_sym) | ||
31 | + | ||
32 | + answer.value = "GPL" | ||
33 | + answer.valid? | ||
34 | + assert !answer.errors.invalid?(field.slug.to_sym) | ||
35 | + end | ||
36 | + | ||
37 | +end | ||
38 | + |
plugins/custom_forms/test/unit/custom_forms_plugin/field_test.rb
0 → 100644
@@ -0,0 +1,64 @@ | @@ -0,0 +1,64 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../../test/test_helper' | ||
2 | + | ||
3 | +class CustomFormsPlugin::FieldTest < ActiveSupport::TestCase | ||
4 | + should 'validate presence of form' do | ||
5 | + field = CustomFormsPlugin::Field.new | ||
6 | + field.valid? | ||
7 | + assert field.errors.invalid?(:form) | ||
8 | + assert field.errors.invalid?(:name) | ||
9 | + | ||
10 | + form = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => fast_create(Profile)) | ||
11 | + field.form = form | ||
12 | + field.name = 'License' | ||
13 | + field.valid? | ||
14 | + assert !field.errors.invalid?(:form) | ||
15 | + assert !field.errors.invalid?(:name) | ||
16 | + end | ||
17 | + | ||
18 | + should 'set slug before validation based on name' do | ||
19 | + field = CustomFormsPlugin::Field.new(:name => 'Name') | ||
20 | + field.valid? | ||
21 | + assert_equal field.name.to_slug, field.slug | ||
22 | + end | ||
23 | + | ||
24 | + should 'validate uniqueness of slug scoped on the form' do | ||
25 | + form1 = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => fast_create(Profile)) | ||
26 | + form2 = CustomFormsPlugin::Form.create!(:name => 'Open Source', :profile => fast_create(Profile)) | ||
27 | + f1 = CustomFormsPlugin::Field.create!(:name => 'License', :form => form1) | ||
28 | + f2 = CustomFormsPlugin::Field.new(:name => 'License', :form => form1) | ||
29 | + f3 = CustomFormsPlugin::Field.new(:name => 'License', :form => form2) | ||
30 | + | ||
31 | + f2.valid? | ||
32 | + f3.valid? | ||
33 | + | ||
34 | + assert f2.errors.invalid?(:slug) | ||
35 | + assert !f3.errors.invalid?(:slug) | ||
36 | + end | ||
37 | + | ||
38 | + should 'set mandatory field as false by default' do | ||
39 | + field = CustomFormsPlugin::Field.new | ||
40 | + assert !field.mandatory | ||
41 | + end | ||
42 | + | ||
43 | + should 'have answers' do | ||
44 | + form = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => fast_create(Profile)) | ||
45 | + field = CustomFormsPlugin::Field.create!(:name => 'License', :form => form) | ||
46 | + a1 = CustomFormsPlugin::Answer.create!(:field => field) | ||
47 | + a2 = CustomFormsPlugin::Answer.create!(:field => field) | ||
48 | + | ||
49 | + assert_includes field.answers, a1 | ||
50 | + assert_includes field.answers, a2 | ||
51 | + end | ||
52 | + | ||
53 | + should 'serialize choices into a hash' do | ||
54 | + form = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => fast_create(Profile)) | ||
55 | + field = CustomFormsPlugin::Field.create!(:name => 'License', :form => form) | ||
56 | + field.choices = {'First' => 1, 'Second' => 2, 'Third' => 3} | ||
57 | + field.save! | ||
58 | + | ||
59 | + assert_equal 1, field.choices['First'] | ||
60 | + assert_equal 2, field.choices['Second'] | ||
61 | + assert_equal 3, field.choices['Third'] | ||
62 | + end | ||
63 | +end | ||
64 | + |
plugins/custom_forms/test/unit/custom_forms_plugin/form_test.rb
0 → 100644
@@ -0,0 +1,172 @@ | @@ -0,0 +1,172 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../../test/test_helper' | ||
2 | + | ||
3 | +class CustomFormsPlugin::FormTest < ActiveSupport::TestCase | ||
4 | + should 'validates presence of a profile and a name' do | ||
5 | + form = CustomFormsPlugin::Form.new | ||
6 | + form.valid? | ||
7 | + assert form.errors.invalid?(:profile) | ||
8 | + assert form.errors.invalid?(:name) | ||
9 | + | ||
10 | + form.profile = fast_create(Profile) | ||
11 | + form.name = 'Free Software' | ||
12 | + form.valid? | ||
13 | + assert !form.errors.invalid?(:profile) | ||
14 | + assert !form.errors.invalid?(:name) | ||
15 | + end | ||
16 | + | ||
17 | + should 'have many fields including fields subclasses' do | ||
18 | + form = CustomFormsPlugin::Form.create!(:profile => fast_create(Profile), :name => 'Free Software') | ||
19 | + f1 = CustomFormsPlugin::Field.create!(:form => form, :name => 'License') | ||
20 | + f2 = CustomFormsPlugin::Field.create!(:form => form, :name => 'Code') | ||
21 | + f3 = CustomFormsPlugin::TextField.create!(:form => form, :name => 'Developer') | ||
22 | + | ||
23 | + assert_includes form.fields, f1 | ||
24 | + assert_includes form.fields, f2 | ||
25 | + assert_includes form.fields, f3 | ||
26 | + end | ||
27 | + | ||
28 | + should 'have many submissions' do | ||
29 | + form = CustomFormsPlugin::Form.create!(:profile => fast_create(Profile), :name => 'Free Software') | ||
30 | + s1 = CustomFormsPlugin::Submission.create!(:form => form, :profile => fast_create(Profile)) | ||
31 | + s2 = CustomFormsPlugin::Submission.create!(:form => form, :profile => fast_create(Profile)) | ||
32 | + | ||
33 | + assert_includes form.submissions, s1 | ||
34 | + assert_includes form.submissions, s2 | ||
35 | + end | ||
36 | + | ||
37 | + should 'set slug before validation based on name' do | ||
38 | + form = CustomFormsPlugin::Form.new(:name => 'Name') | ||
39 | + form.valid? | ||
40 | + assert_equal form.name.to_slug, form.slug | ||
41 | + end | ||
42 | + | ||
43 | + should 'validates uniqueness of slug scoped on profile' do | ||
44 | + profile = fast_create(Profile) | ||
45 | + another_profile = fast_create(Profile) | ||
46 | + CustomFormsPlugin::Form.create!(:profile => profile, :name => 'Free Software') | ||
47 | + form = CustomFormsPlugin::Form.new(:profile => profile, :name => 'Free Software') | ||
48 | + form.valid? | ||
49 | + assert form.errors.invalid?(:slug) | ||
50 | + | ||
51 | + form.profile = another_profile | ||
52 | + form.valid? | ||
53 | + assert !form.errors.invalid?(:slug) | ||
54 | + end | ||
55 | + | ||
56 | + should 'define form expiration' do | ||
57 | + form = CustomFormsPlugin::Form.new | ||
58 | + assert !form.expired? | ||
59 | + | ||
60 | + form.begining = Time.now + 1.day | ||
61 | + assert form.expired? | ||
62 | + | ||
63 | + form.begining = Time.now - 1.day | ||
64 | + assert !form.expired? | ||
65 | + | ||
66 | + form.begining = nil | ||
67 | + form.ending = Time.now + 1.day | ||
68 | + assert !form.expired? | ||
69 | + | ||
70 | + form.ending = Time.now - 1.day | ||
71 | + assert form.expired? | ||
72 | + | ||
73 | + form.begining = Time.now - 1.day | ||
74 | + form.ending = Time.now + 1.day | ||
75 | + assert !form.expired? | ||
76 | + end | ||
77 | + | ||
78 | + should 'validates format of access' do | ||
79 | + form = CustomFormsPlugin::Form.new | ||
80 | + form.valid? | ||
81 | + assert !form.errors.invalid?(:access) | ||
82 | + | ||
83 | + form.access = 'bli' | ||
84 | + form.valid? | ||
85 | + assert form.errors.invalid?(:access) | ||
86 | + | ||
87 | + form.access = 'logged' | ||
88 | + form.valid? | ||
89 | + assert !form.errors.invalid?(:access) | ||
90 | + | ||
91 | + form.access = 'associated' | ||
92 | + form.valid? | ||
93 | + assert !form.errors.invalid?(:access) | ||
94 | + | ||
95 | + form.access = {:bli => 1} | ||
96 | + form.valid? | ||
97 | + assert form.errors.invalid?(:access) | ||
98 | + | ||
99 | + form.access = 999 | ||
100 | + form.valid? | ||
101 | + assert form.errors.invalid?(:access) | ||
102 | + | ||
103 | + p1 = fast_create(Profile) | ||
104 | + form.access = p1.id | ||
105 | + form.valid? | ||
106 | + assert !form.errors.invalid?(:access) | ||
107 | + | ||
108 | + p2 = fast_create(Profile) | ||
109 | + p3 = fast_create(Profile) | ||
110 | + form.access = [p1,p2,p3].map(&:id) | ||
111 | + form.valid? | ||
112 | + assert !form.errors.invalid?(:access) | ||
113 | + end | ||
114 | + | ||
115 | + should 'defines who is able to access the form' do | ||
116 | + owner = fast_create(Community) | ||
117 | + form = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => owner) | ||
118 | + assert form.accessible_to(nil) | ||
119 | + | ||
120 | + form.access = 'logged' | ||
121 | + assert !form.accessible_to(nil) | ||
122 | + person = fast_create(Person) | ||
123 | + assert form.accessible_to(person) | ||
124 | + | ||
125 | + form.access = 'associated' | ||
126 | + assert !form.accessible_to(person) | ||
127 | + owner.add_member(person) | ||
128 | + assert form.accessible_to(person) | ||
129 | + | ||
130 | + p1 = fast_create(Profile) | ||
131 | + form.access = p1.id | ||
132 | + assert !form.accessible_to(person) | ||
133 | + assert form.accessible_to(p1) | ||
134 | + | ||
135 | + p2 = fast_create(Profile) | ||
136 | + form.access = [person.id, p1.id] | ||
137 | + assert form.accessible_to(person) | ||
138 | + assert form.accessible_to(p1) | ||
139 | + assert !form.accessible_to(p2) | ||
140 | + form.access << p2.id | ||
141 | + assert form.accessible_to(p2) | ||
142 | + | ||
143 | + assert form.accessible_to(owner) | ||
144 | + end | ||
145 | + | ||
146 | + should 'have a named_scope that retrieve forms from a profile' do | ||
147 | + profile = fast_create(Profile) | ||
148 | + another_profile = fast_create(Profile) | ||
149 | + f1 = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => profile) | ||
150 | + f2 = CustomFormsPlugin::Form.create!(:name => 'Open Source', :profile => profile) | ||
151 | + f3 = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => another_profile) | ||
152 | + scope = CustomFormsPlugin::Form.from(profile) | ||
153 | + | ||
154 | + assert_equal ActiveRecord::NamedScope::Scope, scope.class | ||
155 | + assert_includes scope, f1 | ||
156 | + assert_includes scope, f2 | ||
157 | + assert_not_includes scope, f3 | ||
158 | + end | ||
159 | + | ||
160 | + should 'have a named_scope that retrieves all forms that are triggered on membership' do | ||
161 | + profile = fast_create(Profile) | ||
162 | + f1 = CustomFormsPlugin::Form.create!(:name => 'On membership 1', :profile => profile, :on_membership => true) | ||
163 | + f2 = CustomFormsPlugin::Form.create!(:name => 'On membership 2', :profile => profile, :on_membership => true) | ||
164 | + f3 = CustomFormsPlugin::Form.create!(:name => 'Not on memberhsip', :profile => profile, :on_membership => false) | ||
165 | + scope = CustomFormsPlugin::Form.from(profile).on_memberships | ||
166 | + | ||
167 | + assert_equal ActiveRecord::NamedScope::Scope, scope.class | ||
168 | + assert_includes scope, f1 | ||
169 | + assert_includes scope, f2 | ||
170 | + assert_not_includes scope, f3 | ||
171 | + end | ||
172 | +end |
plugins/custom_forms/test/unit/custom_forms_plugin/membership_survey_test.rb
0 → 100644
@@ -0,0 +1,31 @@ | @@ -0,0 +1,31 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../../test/test_helper' | ||
2 | + | ||
3 | +class CustomFormsPlugin::MembershipSurveyTest < ActiveSupport::TestCase | ||
4 | + should 'validates presence of form_id' do | ||
5 | + task = CustomFormsPlugin::MembershipSurvey.new | ||
6 | + task.valid? | ||
7 | + assert task.errors.invalid?(:form_id) | ||
8 | + | ||
9 | + task.form_id = 1 | ||
10 | + task.valid? | ||
11 | + assert !task.errors.invalid?(:form_id) | ||
12 | + end | ||
13 | + | ||
14 | + should 'create submission with answers on perform' do | ||
15 | + profile = fast_create(Profile) | ||
16 | + person = fast_create(Person) | ||
17 | + form = CustomFormsPlugin::Form.create!(:name => 'Simple Form', :profile => profile) | ||
18 | + field = CustomFormsPlugin::Field.create!(:name => 'Name', :form => form) | ||
19 | + task = CustomFormsPlugin::MembershipSurvey.create!(:form_id => form.id, :submission => {'name' => 'Jack'}, :target => person, :requestor => profile) | ||
20 | + | ||
21 | + assert_difference CustomFormsPlugin::Submission, :count, 1 do | ||
22 | + task.finish | ||
23 | + end | ||
24 | + | ||
25 | + submission = CustomFormsPlugin::Submission.last | ||
26 | + assert_equal submission.answers.count, 1 | ||
27 | + | ||
28 | + answer = submission.answers.first | ||
29 | + assert_equal answer.value, 'Jack' | ||
30 | + end | ||
31 | +end |
plugins/custom_forms/test/unit/custom_forms_plugin/select_field_test.rb
0 → 100644
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../../test/test_helper' | ||
2 | + | ||
3 | +class CustomFormsPlugin::SelectFieldTest < ActiveSupport::TestCase | ||
4 | + should 'validate presence of choices, multiple and list' do | ||
5 | + select = CustomFormsPlugin::SelectField.new | ||
6 | + select.valid? | ||
7 | + assert select.errors.invalid?(:choices) | ||
8 | + | ||
9 | + select.choices = {'label' => 'value'} | ||
10 | + select.valid? | ||
11 | + assert !select.errors.invalid?(:choices) | ||
12 | + end | ||
13 | +end |
plugins/custom_forms/test/unit/custom_forms_plugin/submission_test.rb
0 → 100644
@@ -0,0 +1,46 @@ | @@ -0,0 +1,46 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../../test/test_helper' | ||
2 | + | ||
3 | +class CustomFormsPlugin::SubmissionTest < ActiveSupport::TestCase | ||
4 | + should 'validates presence of form' do | ||
5 | + submission = CustomFormsPlugin::Submission.new | ||
6 | + submission.valid? | ||
7 | + assert submission.errors.invalid?(:form) | ||
8 | + | ||
9 | + form = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => fast_create(Profile)) | ||
10 | + submission.form = form | ||
11 | + submission.valid? | ||
12 | + assert !submission.errors.invalid?(:form) | ||
13 | + end | ||
14 | + | ||
15 | + should 'belong to a profile' do | ||
16 | + profile = fast_create(Profile) | ||
17 | + submission = CustomFormsPlugin::Submission.new | ||
18 | + submission.profile = profile | ||
19 | + assert_equal profile, submission.profile | ||
20 | + end | ||
21 | + | ||
22 | + should 'require presence of author name and email if profile is nil' do | ||
23 | + submission = CustomFormsPlugin::Submission.new | ||
24 | + submission.valid? | ||
25 | + assert submission.errors.invalid?(:author_name) | ||
26 | + assert submission.errors.invalid?(:author_email) | ||
27 | + | ||
28 | + submission.author_name = 'Jack Sparrow' | ||
29 | + submission.author_email = 'jack@black-pearl.com' | ||
30 | + submission.valid? | ||
31 | + assert !submission.errors.invalid?(:author_name) | ||
32 | + assert !submission.errors.invalid?(:author_email) | ||
33 | + end | ||
34 | + | ||
35 | + should 'have answers' do | ||
36 | + form = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => fast_create(Profile)) | ||
37 | + field = CustomFormsPlugin::Field.create!(:name => 'License', :form => form) | ||
38 | + submission = CustomFormsPlugin::Submission.create!(:form => form, :profile => fast_create(Profile)) | ||
39 | + a1 = CustomFormsPlugin::Answer.create!(:field => field, :submission => submission) | ||
40 | + a2 = CustomFormsPlugin::Answer.create!(:field => field, :submission => submission) | ||
41 | + | ||
42 | + assert_includes submission.answers, a1 | ||
43 | + assert_includes submission.answers, a2 | ||
44 | + end | ||
45 | +end | ||
46 | + |
plugins/custom_forms/test/unit/ext/role_assingment_test.rb
0 → 100644
@@ -0,0 +1,51 @@ | @@ -0,0 +1,51 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../../test/test_helper' | ||
2 | + | ||
3 | +class RoleAssignmentsTest < ActiveSupport::TestCase | ||
4 | + should 'create membership_surveys on membership creation' do | ||
5 | + environment = Environment.default | ||
6 | + environment.enable_plugin(CustomFormsPlugin) | ||
7 | + organization = fast_create(Organization) | ||
8 | + person = fast_create(Person) | ||
9 | + f1 = CustomFormsPlugin::Form.create!(:profile => organization, :name => 'Form 1', :on_membership => true) | ||
10 | + f2 = CustomFormsPlugin::Form.create!(:profile => organization, :name => 'Form 2', :on_membership => true) | ||
11 | + f3 = CustomFormsPlugin::Form.create!(:profile => organization, :name => 'Form 3', :on_membership => false) | ||
12 | + | ||
13 | + assert_difference CustomFormsPlugin::MembershipSurvey, :count, 2 do | ||
14 | + organization.add_member(person) | ||
15 | + end | ||
16 | + end | ||
17 | + | ||
18 | + should 'create membership_survey on membership creation with form accessible to members only' do | ||
19 | + environment = Environment.default | ||
20 | + environment.enable_plugin(CustomFormsPlugin) | ||
21 | + organization = fast_create(Organization) | ||
22 | + person = fast_create(Person) | ||
23 | + form = CustomFormsPlugin::Form.create!(:profile => organization, :name => 'Form', :on_membership => true, :access => 'associated') | ||
24 | + | ||
25 | + assert_difference CustomFormsPlugin::MembershipSurvey, :count, 1 do | ||
26 | + organization.add_member(person) | ||
27 | + end | ||
28 | + end | ||
29 | + | ||
30 | + should 'cancel membership_surveys if membership is undone and task is active' do | ||
31 | + environment = Environment.default | ||
32 | + environment.enable_plugin(CustomFormsPlugin) | ||
33 | + organization = fast_create(Organization) | ||
34 | + person = fast_create(Person) | ||
35 | + form = CustomFormsPlugin::Form.create!(:profile => organization, :name => 'Form', :on_membership => true) | ||
36 | + organization.add_member(person) | ||
37 | + | ||
38 | + assert_difference CustomFormsPlugin::MembershipSurvey.pending, :count, -1 do | ||
39 | + organization.remove_member(person) | ||
40 | + end | ||
41 | + | ||
42 | + organization.add_member(person) | ||
43 | + task = CustomFormsPlugin::MembershipSurvey.last | ||
44 | + task.status = Task::Status::FINISHED | ||
45 | + task.save! | ||
46 | + assert_no_difference CustomFormsPlugin::MembershipSurvey.finished, :count do | ||
47 | + organization.remove_member(person) | ||
48 | + end | ||
49 | + end | ||
50 | +end | ||
51 | + |
plugins/custom_forms/views/custom_forms_plugin_myprofile/_edit_select.html.erb
0 → 100644
@@ -0,0 +1,33 @@ | @@ -0,0 +1,33 @@ | ||
1 | +<% elem_id = "edit-select-#{counter}" %> | ||
2 | +<div id=<%= elem_id %> class='edit-information'> | ||
3 | + <h2><%= _('Options') %></h2> | ||
4 | + <table class='action-table' style='width: 420px'> | ||
5 | + <tr> | ||
6 | + <th style='width: 40%'><%= _('Name') %></th> | ||
7 | + <th style='width: 40%'><%= _('Value') %></th> | ||
8 | + <th style='width: 20%'><%= _('Actions') %></th> | ||
9 | + </tr> | ||
10 | + <% option_counter = 1 %> | ||
11 | + <% (field.choices || {}).each do |name, value| %> | ||
12 | + <%= render :partial => 'option', :locals => {:name => name, :value => value, :counter => counter, :option_counter => option_counter} %> | ||
13 | + <% option_counter += 1 %> | ||
14 | + <% end %> | ||
15 | + <%= render :partial => 'empty_option', :locals => {:counter => counter, :option_counter => option_counter} %> | ||
16 | + <tr class='new-item'> | ||
17 | + <td colspan='3'> | ||
18 | + <%= button(:add, _('Add a new option'), '#', :class => 'new-option', :field_id => counter)%> | ||
19 | + </td> | ||
20 | + </tr> | ||
21 | + </table> | ||
22 | + | ||
23 | + <h3><%= _('Type') %></h3> | ||
24 | + <%= labelled_radio_button 'Radio', "fields[#{counter}][kind]", 'radio', !field.multiple && !field.list %><br /> | ||
25 | + <%= labelled_radio_button 'Checkbox', "fields[#{counter}][kind]", 'check_box', field.multiple && !field.list %><br /> | ||
26 | + <%= labelled_radio_button 'Select', "fields[#{counter}][kind]", 'select', !field.multiple && field.list %><br /> | ||
27 | + <%= labelled_radio_button 'Multiple Select', "fields[#{counter}][kind]", 'multiple_select', field.multiple && field.list %><br /> | ||
28 | + | ||
29 | + <% button_bar do %> | ||
30 | + <%= button :ok, _('Ok'), '#', :class => 'colorbox-ok-button', :div_id => elem_id %> | ||
31 | + <% end %> | ||
32 | +</div> | ||
33 | + |
plugins/custom_forms/views/custom_forms_plugin_myprofile/_edit_text.html.erb
0 → 100644
@@ -0,0 +1,10 @@ | @@ -0,0 +1,10 @@ | ||
1 | +<% elem_id = "edit-text-#{counter}" %> | ||
2 | +<div id=<%= elem_id %> class='edit-information'> | ||
3 | + <h2><%= _('Default value') %></h2> | ||
4 | + <%= labelled_form_field _('Default value'), text_field("fields[#{counter}]", :default_value, :value => field.default_value) %> | ||
5 | + | ||
6 | + <% button_bar do %> | ||
7 | + <%= button :ok, _('Ok'), '#', :class => 'colorbox-ok-button', :div_id => elem_id %> | ||
8 | + <% end %> | ||
9 | +</div> | ||
10 | + |
plugins/custom_forms/views/custom_forms_plugin_myprofile/_empty_field.html.erb
0 → 100644
@@ -0,0 +1,10 @@ | @@ -0,0 +1,10 @@ | ||
1 | +<tr id="empty-field" style='display: none' last_id=<%= counter %>> | ||
2 | + <td style="text-align: left"><%= text_field "fields[#{counter}]", :name, :size => 25 %></td> | ||
3 | + <td><%= select "fields[#{counter}]", :type, type_options, :selected => type_for_options(field.class) %></td> | ||
4 | + <td><%= check_box "fields[#{counter}]", :mandatory %></td> | ||
5 | + <%= hidden_field "fields[#{counter}]", :form_id, :value => @form.id %> | ||
6 | + <td class='actions'> | ||
7 | + <%= button_without_text :edit, _('Edit'), '', :field_id => counter %> | ||
8 | + <%= button_without_text :remove, _('Remove'), '#', :class => 'remove-field', :field_id => counter, :confirm => _('Are you sure you want to remove this field?') %> | ||
9 | + </td> | ||
10 | +</tr> |
plugins/custom_forms/views/custom_forms_plugin_myprofile/_empty_option.html.erb
0 → 100644
@@ -0,0 +1,8 @@ | @@ -0,0 +1,8 @@ | ||
1 | +<tr id=<%= "empty-option-#{counter}" %> option_id=<%= option_counter %> style="display: none;"> | ||
2 | + <td><%= text_field_tag("fields[#{counter}][choices][#{option_counter}][name]") %></td> | ||
3 | + <td><%= text_field_tag("fields[#{counter}][choices][#{option_counter}][value]", nil, :size => 15) %></td> | ||
4 | + <td class='actions'> | ||
5 | + <%= button_without_text :remove, _('Remove'), '#', :class => 'remove-option', :field_id => counter, :option_id => option_counter, :confirm => _('Are you sure you want to remove this option?') %> | ||
6 | + </td> | ||
7 | +</tr> | ||
8 | + |
plugins/custom_forms/views/custom_forms_plugin_myprofile/_field.html.erb
0 → 100644
@@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
1 | +<tr id=<%= "field-#{counter}" %>> | ||
2 | + <td style="text-align: left"><%= text_field "fields[#{counter}]", :name, :value => field.name, :size => 25 %></td> | ||
3 | + <td><%= type_to_label(field.type) %></td> | ||
4 | + <%= hidden_field "fields[#{counter}]", :type, :value => type_for_options(field.class) %> | ||
5 | + <td><%= check_box "fields[#{counter}]", :mandatory, :checked => field.mandatory %></td> | ||
6 | + <%= hidden_field "fields[#{counter}]", :real_id, :value => field.id %> | ||
7 | + <%= hidden_field "fields[#{counter}]", :form_id, :value => @form.id %> | ||
8 | + <td class='actions'> | ||
9 | + <%= button_without_text :edit, _('Edit'), '#', :field_id => counter %> | ||
10 | + <%= button_without_text :remove, _('Remove'), '#', :class => 'remove-field', :field_id => counter, :confirm => _('Are you sure you want to remove this field?') %> | ||
11 | + </td> | ||
12 | +</tr> |
plugins/custom_forms/views/custom_forms_plugin_myprofile/_form.html.erb
0 → 100644
@@ -0,0 +1,56 @@ | @@ -0,0 +1,56 @@ | ||
1 | +<% self.extend(CustomFormsPlugin::Helper) %> | ||
2 | + | ||
3 | +<%= error_messages_for :form %> | ||
4 | + | ||
5 | +<% form_for :form, @form do |f| %> | ||
6 | + <%= required labelled_form_field _('Name'), f.text_field(:name) %> | ||
7 | + <%= labelled_form_field(_('Período'), ( | ||
8 | + date_range_field('form[begining]', 'form[ending]', @form.begining, @form.ending, | ||
9 | + '%Y-%m-%d %H:%M', | ||
10 | + { :time => true, :change_month => true, :change_year => true, | ||
11 | + :date_format => 'yy-mm-dd', :time_format => 'hh:mm' }, | ||
12 | + { :size => 14 }) | ||
13 | + )) %> | ||
14 | + <%= labelled_form_field _('Access'), f.select(:access, access_options(profile))%> | ||
15 | + <% if profile.organization? %> | ||
16 | + <%= labelled_form_field _('Triggered on membership'), f.check_box(:on_membership) %> | ||
17 | + <% end %> | ||
18 | + <%= labelled_form_field _('Description'), f.text_area(:description, :style => 'width: 100%') %> | ||
19 | + | ||
20 | + <h2><%= _('Fields') %></h2> | ||
21 | + <table class="action-table" id='fields-table'> | ||
22 | + <tr> | ||
23 | + <th style='width: 40%'><%= _('Name') %></th> | ||
24 | + <th style='width: 30%'><%= _('Type') %></th> | ||
25 | + <th style='width: 10%'><%= _('Mandatory') %></th> | ||
26 | + <th style='width: 20%'><%= _('Actions') %></th> | ||
27 | + </tr> | ||
28 | + <% counter = 1 %> | ||
29 | + <% @fields.each do |field| %> | ||
30 | + <%= render :partial => 'field', :locals => {:field => field, :counter => counter} %> | ||
31 | + <% counter += 1 %> | ||
32 | + <% end %> | ||
33 | + <%= render :partial => 'empty_field', :locals => {:field => @empty_field, :counter => counter} %> | ||
34 | + <tr class='new-item'> | ||
35 | + <td colspan='5'> | ||
36 | + <%= button(:add, _('Add a new field'), '#', :id => 'new-field')%> | ||
37 | + </td> | ||
38 | + </tr> | ||
39 | + </table> | ||
40 | + | ||
41 | + <% counter = 1 %> | ||
42 | + <% @fields.each do |field| %> | ||
43 | + <%= render :partial => 'edit_text', :locals => {:field => field, :counter => counter} %> | ||
44 | + <%= render :partial => 'edit_select', :locals => {:field => field, :counter => counter} %> | ||
45 | + <% counter += 1 %> | ||
46 | + <% end %> | ||
47 | + | ||
48 | + <%= render :partial => 'edit_text', :locals => {:field => @empty_field, :counter => counter} %> | ||
49 | + <%= render :partial => 'edit_select', :locals => {:field => @empty_field, :counter => counter} %> | ||
50 | + | ||
51 | + <% button_bar do %> | ||
52 | + <%= submit_button :save, _('Save'), :cancel => {:action => 'index'}%> | ||
53 | + <% end %> | ||
54 | +<% end %> | ||
55 | + | ||
56 | +<%= javascript_include_tag '../plugins/custom_forms/field' %> |
plugins/custom_forms/views/custom_forms_plugin_myprofile/_option.html.erb
0 → 100644
@@ -0,0 +1,7 @@ | @@ -0,0 +1,7 @@ | ||
1 | +<tr id=<%= "field-#{counter}-option-#{option_counter}" %> style="display: auto;"> | ||
2 | + <td><%= text_field_tag("fields[#{counter}][choices][#{option_counter}][name]", name) %></td> | ||
3 | + <td><%= text_field_tag("fields[#{counter}][choices][#{option_counter}][value]", value) %></td> | ||
4 | + <td class='actions'> | ||
5 | + <%= button_without_text :remove, _('Remove'), '#', :class => 'remove-option', :field_id => counter, :option_id => option_counter, :confirm => _('Are you sure you want to remove this option?') %> | ||
6 | + </td> | ||
7 | +</tr> |
plugins/custom_forms/views/custom_forms_plugin_myprofile/create.html.erb
0 → 100644
plugins/custom_forms/views/custom_forms_plugin_myprofile/edit.html.erb
0 → 100644
plugins/custom_forms/views/custom_forms_plugin_myprofile/index.html.erb
0 → 100644
@@ -0,0 +1,28 @@ | @@ -0,0 +1,28 @@ | ||
1 | +<% self.extend CustomFormsPlugin::Helper %> | ||
2 | + | ||
3 | +<h1><%= _('Manage forms') %></h1> | ||
4 | +<table class="action-table"> | ||
5 | + <tr> | ||
6 | + <th style='width: 40%'><%= _('Name') %></th> | ||
7 | + <th style='width: 30%'><%= _('Period') %></th> | ||
8 | + <th style='width: 10%'><%= _('Submissions') %></th> | ||
9 | + <th style='width: 10%'><%= _('Access') %></th> | ||
10 | + <th style='width: 10%'><%= _('Actions') %></th> | ||
11 | + </tr> | ||
12 | + <% @forms.each do |form| %> | ||
13 | + <tr> | ||
14 | + <td><%= link_to form.name, {:controller => 'custom_forms_plugin_profile', :action => 'show', :id => form.id} %></td> | ||
15 | + <td><%= period_range(form) %></td> | ||
16 | + <td><%= form.submissions.count > 0 ? link_to(form.submissions.count, {:action => 'submissions', :id => form.id}) : 0 %></td> | ||
17 | + <td><%= access_text(form) %></td> | ||
18 | + <td class="actions"> | ||
19 | + <%= button_without_text :edit, _('Edit'), :action => 'edit', :id => form.id %> | ||
20 | + <%= button_without_text :remove, _('Remove'), {:action => 'remove', :id => form.id}, :confirm => _('Are you sure you want to remove this form?') %></td> | ||
21 | + </tr> | ||
22 | + <% end %> | ||
23 | + <tr id="new-item"> | ||
24 | + <td colspan='5'> | ||
25 | + <%= button(:add, _('Add a new form'), :action => 'create')%> | ||
26 | + </td> | ||
27 | + </tr> | ||
28 | +</table> |
plugins/custom_forms/views/custom_forms_plugin_myprofile/show_submission.html.erb
0 → 100644
@@ -0,0 +1,10 @@ | @@ -0,0 +1,10 @@ | ||
1 | +<h1><%= @form.name %></h1> | ||
2 | +<p><%= @form.description %></p> | ||
3 | + | ||
4 | +<% fields_for :submission, @submission do |f| %> | ||
5 | + <%= render :partial => 'shared/form_submission', :locals => {:f => f} %> | ||
6 | +<% end %> | ||
7 | + | ||
8 | +<% button_bar do %> | ||
9 | + <%= button :back, _('Back to submissions'), :action => 'submissions', :id => @form.id %> | ||
10 | +<% end %> |
plugins/custom_forms/views/custom_forms_plugin_myprofile/submissions.html.erb
0 → 100644
@@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
1 | +<% self.extend CustomFormsPlugin::Helper %> | ||
2 | + | ||
3 | +<h1><%= _('Submissions for %s') % @form.name %></h1> | ||
4 | + | ||
5 | +<% if @form.submissions.empty? %> | ||
6 | + <%= _('There are no submissions for this form.') %> | ||
7 | +<% else %> | ||
8 | + <table class="action-table"> | ||
9 | + <tr> | ||
10 | + <th style='width: 50%'><%= _('Author') %></th> | ||
11 | + <th style='width: 50%'><%= _('Time') %></th> | ||
12 | + </tr> | ||
13 | + <% @submissions.each do |submission| %> | ||
14 | + <tr> | ||
15 | + <% author = submission.profile.present? ? submission.profile.name : submission.author_name %> | ||
16 | + <td><%= link_to(author, {:action => 'show_submission', :id => submission.id}) %></td> | ||
17 | + <td><%= time_format(submission.created_at) %></td> | ||
18 | + </tr> | ||
19 | + <% end %> | ||
20 | + </table> | ||
21 | +<% end %> | ||
22 | + | ||
23 | +<% button_bar do %> | ||
24 | + <%= button :back, _('Back to forms'), :action => 'index' %> | ||
25 | +<% end %> |
plugins/custom_forms/views/custom_forms_plugin_profile/show.html.erb
0 → 100644
@@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
1 | +<h1><%= @form.name %></h1> | ||
2 | +<p><%= @form.description %></p> | ||
3 | + | ||
4 | +<% if @submission.id.nil? %> | ||
5 | + <%= error_messages_for :submission %> | ||
6 | + | ||
7 | + <% form_for :submission, @submission do |f| %> | ||
8 | + <% if !user %> | ||
9 | + <%= required labelled_form_field _('Author name'), text_field_tag(:author_name, @submission.author_name) %> | ||
10 | + <%= required labelled_form_field _('Author email'), text_field_tag(:author_email, @submission.author_email) %> | ||
11 | + <% end %> | ||
12 | + | ||
13 | + <%= render :partial => 'shared/form_submission', :locals => {:f => f} %> | ||
14 | + | ||
15 | + <% button_bar do %> | ||
16 | + <%= submit_button :save, _('Save'), :cancel => {:action => 'index'} %> | ||
17 | + <% end %> | ||
18 | + <% end %> | ||
19 | +<% else %> | ||
20 | + <% fields_for :submission, @submission do |f| %> | ||
21 | + <%= render :partial => 'shared/form_submission', :locals => {:f => f} %> | ||
22 | + <% end %> | ||
23 | +<% end %> |
plugins/custom_forms/views/shared/_form_submission.html.erb
0 → 100644
plugins/custom_forms/views/tasks/_membership_survey_accept_details.html.erb
0 → 100644
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +<%= f.object_name.to_s %> |
plugins/custom_forms/views/tasks/custom_forms_plugin/_membership_survey_accept_details.html.erb
0 → 100644
@@ -0,0 +1,10 @@ | @@ -0,0 +1,10 @@ | ||
1 | +<% @form = CustomFormsPlugin::Form.find(task.form_id) %> | ||
2 | +<% @submission = CustomFormsPlugin::Submission.new(:form_id => @form.id, :profile_id => user.id) %> | ||
3 | + | ||
4 | +<h2><%= @form.name %></h2> | ||
5 | +<p><%= @form.description %></p> | ||
6 | + | ||
7 | +<% f.fields_for :submission do |fi| %> | ||
8 | + <%#= fi.error_messages_for :submission %> | ||
9 | + <%= render :partial => 'shared/form_submission', :locals => {:f => fi} %> | ||
10 | +<% end %> |
plugins/foo/controllers/admin/foo_plugin_admin_bar_controller.rb
0 → 100644
plugins/foo/controllers/myprofile/foo_plugin_myprofile_bar_controller.rb
0 → 100644
plugins/foo/controllers/profile/foo_plugin_profile_bar_controller.rb
0 → 100644
plugins/foo/controllers/public/foo_plugin_public_bar_controller.rb
0 → 100644
plugins/mezuro/controllers/mezuro_plugin_myprofile_controller.rb
@@ -2,7 +2,15 @@ class MezuroPluginMyprofileController < ProfileController | @@ -2,7 +2,15 @@ class MezuroPluginMyprofileController < ProfileController | ||
2 | 2 | ||
3 | append_view_path File.join(File.dirname(__FILE__) + '/../views') | 3 | append_view_path File.join(File.dirname(__FILE__) + '/../views') |
4 | 4 | ||
5 | - | 5 | + rescue_from Exception do |exception| |
6 | + message = URI.escape(CGI.escape(exception.message),'.') | ||
7 | + redirect_to_error_page message | ||
8 | + end | ||
9 | + | ||
10 | + def error_page | ||
11 | + @message = params[:message] | ||
12 | + end | ||
13 | + | ||
6 | def choose_base_tool | 14 | def choose_base_tool |
7 | @configuration_content = profile.articles.find(params[:id]) | 15 | @configuration_content = profile.articles.find(params[:id]) |
8 | @base_tools = Kalibro::BaseTool.all_names | 16 | @base_tools = Kalibro::BaseTool.all_names |
@@ -11,19 +19,23 @@ class MezuroPluginMyprofileController < ProfileController | @@ -11,19 +19,23 @@ class MezuroPluginMyprofileController < ProfileController | ||
11 | def choose_metric | 19 | def choose_metric |
12 | @configuration_content = profile.articles.find(params[:id]) | 20 | @configuration_content = profile.articles.find(params[:id]) |
13 | @base_tool = params[:base_tool] | 21 | @base_tool = params[:base_tool] |
14 | - @supported_metrics = Kalibro::BaseTool.find_by_name(@base_tool).supported_metrics | 22 | + base_tool = Kalibro::BaseTool.find_by_name(@base_tool) |
23 | + @supported_metrics = base_tool.nil? ? [] : base_tool.supported_metrics | ||
15 | end | 24 | end |
16 | - | 25 | + |
17 | def new_metric_configuration | 26 | def new_metric_configuration |
18 | @configuration_content = profile.articles.find(params[:id]) | 27 | @configuration_content = profile.articles.find(params[:id]) |
19 | @metric = Kalibro::BaseTool.find_by_name(params[:base_tool]).metric params[:metric_name] | 28 | @metric = Kalibro::BaseTool.find_by_name(params[:base_tool]).metric params[:metric_name] |
20 | end | 29 | end |
21 | - | 30 | + |
22 | def new_compound_metric_configuration | 31 | def new_compound_metric_configuration |
23 | @configuration_content = profile.articles.find(params[:id]) | 32 | @configuration_content = profile.articles.find(params[:id]) |
24 | @metric_configurations = @configuration_content.metric_configurations | 33 | @metric_configurations = @configuration_content.metric_configurations |
34 | + if configuration_content_has_errors? | ||
35 | + redirect_to_error_page @configuration_content.errors[:base] | ||
36 | + end | ||
25 | end | 37 | end |
26 | - | 38 | + |
27 | def edit_metric_configuration | 39 | def edit_metric_configuration |
28 | @configuration_content = profile.articles.find(params[:id]) | 40 | @configuration_content = profile.articles.find(params[:id]) |
29 | @metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, params[:metric_name]) | 41 | @metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, params[:metric_name]) |
@@ -36,19 +48,29 @@ class MezuroPluginMyprofileController < ProfileController | @@ -36,19 +48,29 @@ class MezuroPluginMyprofileController < ProfileController | ||
36 | @metric_configurations = @configuration_content.metric_configurations | 48 | @metric_configurations = @configuration_content.metric_configurations |
37 | @metric = @metric_configuration.metric | 49 | @metric = @metric_configuration.metric |
38 | end | 50 | end |
39 | - | 51 | + |
40 | def create_metric_configuration | 52 | def create_metric_configuration |
41 | id = params[:id] | 53 | id = params[:id] |
42 | metric_name = params[:metric_configuration][:metric][:name] | 54 | metric_name = params[:metric_configuration][:metric][:name] |
43 | - (Kalibro::MetricConfiguration.new(params[:metric_configuration])).save | ||
44 | - redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/edit_metric_configuration?id=#{id}&metric_name=#{metric_name.gsub(/\s/, '+')}" | 55 | + metric_configuration = Kalibro::MetricConfiguration.new(params[:metric_configuration]) |
56 | + metric_configuration.save | ||
57 | + if metric_configuration_has_errors? metric_configuration | ||
58 | + redirect_to_error_page metric_configuration.errors[0].message | ||
59 | + else | ||
60 | + redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/edit_metric_configuration?id=#{id}&metric_name=#{metric_name.gsub(/\s/, '+')}" | ||
61 | + end | ||
45 | end | 62 | end |
46 | - | 63 | + |
47 | def create_compound_metric_configuration | 64 | def create_compound_metric_configuration |
48 | id = params[:id] | 65 | id = params[:id] |
49 | metric_name = params[:metric_configuration][:metric][:name] | 66 | metric_name = params[:metric_configuration][:metric][:name] |
50 | - Kalibro::MetricConfiguration.new(params[:metric_configuration]).save | ||
51 | - redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/edit_compound_metric_configuration?id=#{id}&metric_name=#{metric_name.gsub(/\s/, '+')}" | 67 | + metric_configuration = Kalibro::MetricConfiguration.new(params[:metric_configuration]) |
68 | + metric_configuration.save | ||
69 | + if metric_configuration_has_errors? metric_configuration | ||
70 | + redirect_to_error_page metric_configuration.errors[0].message | ||
71 | + else | ||
72 | + redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/edit_compound_metric_configuration?id=#{id}&metric_name=#{metric_name.gsub(/\s/, '+')}" | ||
73 | + end | ||
52 | end | 74 | end |
53 | 75 | ||
54 | def update_metric_configuration | 76 | def update_metric_configuration |
@@ -56,7 +78,11 @@ class MezuroPluginMyprofileController < ProfileController | @@ -56,7 +78,11 @@ class MezuroPluginMyprofileController < ProfileController | ||
56 | metric_name = params[:metric_configuration][:metric][:name] | 78 | metric_name = params[:metric_configuration][:metric][:name] |
57 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name) | 79 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name) |
58 | metric_configuration.update_attributes params[:metric_configuration] | 80 | metric_configuration.update_attributes params[:metric_configuration] |
59 | - redirect_to "/#{profile.identifier}/#{@configuration_content.slug}" | 81 | + if metric_configuration_has_errors? metric_configuration |
82 | + redirect_to_error_page metric_configuration.errors[0].message | ||
83 | + else | ||
84 | + redirect_to "/#{profile.identifier}/#{@configuration_content.slug}" | ||
85 | + end | ||
60 | end | 86 | end |
61 | 87 | ||
62 | def update_compound_metric_configuration | 88 | def update_compound_metric_configuration |
@@ -64,7 +90,11 @@ class MezuroPluginMyprofileController < ProfileController | @@ -64,7 +90,11 @@ class MezuroPluginMyprofileController < ProfileController | ||
64 | metric_name = params[:metric_configuration][:metric][:name] | 90 | metric_name = params[:metric_configuration][:metric][:name] |
65 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name) | 91 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name) |
66 | metric_configuration.update_attributes params[:metric_configuration] | 92 | metric_configuration.update_attributes params[:metric_configuration] |
67 | - redirect_to "/#{profile.identifier}/#{@configuration_content.slug}" | 93 | + if metric_configuration_has_errors? metric_configuration |
94 | + redirect_to_error_page metric_configuration.errors[0].message | ||
95 | + else | ||
96 | + redirect_to "/#{profile.identifier}/#{@configuration_content.slug}" | ||
97 | + end | ||
68 | end | 98 | end |
69 | 99 | ||
70 | def remove_metric_configuration | 100 | def remove_metric_configuration |
@@ -72,32 +102,41 @@ class MezuroPluginMyprofileController < ProfileController | @@ -72,32 +102,41 @@ class MezuroPluginMyprofileController < ProfileController | ||
72 | metric_name = params[:metric_name] | 102 | metric_name = params[:metric_name] |
73 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(configuration_content.name, metric_name) | 103 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(configuration_content.name, metric_name) |
74 | metric_configuration.destroy | 104 | metric_configuration.destroy |
75 | - redirect_to "/#{profile.identifier}/#{configuration_content.slug}" | 105 | + if metric_configuration_has_errors? metric_configuration |
106 | + redirect_to_error_page metric_configuration.errors[0].message | ||
107 | + else | ||
108 | + redirect_to "/#{profile.identifier}/#{configuration_content.slug}" | ||
109 | + end | ||
76 | end | 110 | end |
77 | - | 111 | + |
78 | def new_range | 112 | def new_range |
79 | @configuration_content = profile.articles.find(params[:id]) | 113 | @configuration_content = profile.articles.find(params[:id]) |
80 | @metric_name = params[:metric_name] | 114 | @metric_name = params[:metric_name] |
81 | @range = Kalibro::Range.new | 115 | @range = Kalibro::Range.new |
116 | + @range_color = "#000000" | ||
82 | end | 117 | end |
83 | - | 118 | + |
84 | def edit_range | 119 | def edit_range |
85 | @configuration_content = profile.articles.find(params[:id]) | 120 | @configuration_content = profile.articles.find(params[:id]) |
86 | @metric_name = params[:metric_name] | 121 | @metric_name = params[:metric_name] |
87 | @beginning_id = params[:beginning_id] | 122 | @beginning_id = params[:beginning_id] |
88 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, @metric_name) | 123 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, @metric_name) |
89 | @range = metric_configuration.ranges.find{|range| range.beginning == @beginning_id.to_f || @beginning_id =="-INF" } | 124 | @range = metric_configuration.ranges.find{|range| range.beginning == @beginning_id.to_f || @beginning_id =="-INF" } |
125 | + @range_color = "#" + @range.color.to_s.gsub(/^ff/, "") | ||
90 | end | 126 | end |
91 | 127 | ||
92 | def create_range | 128 | def create_range |
93 | @configuration_content = profile.articles.find(params[:id]) | 129 | @configuration_content = profile.articles.find(params[:id]) |
94 | @range = Kalibro::Range.new params[:range] | 130 | @range = Kalibro::Range.new params[:range] |
95 | metric_name = params[:metric_name] | 131 | metric_name = params[:metric_name] |
96 | - metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name) | 132 | + metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name) |
97 | metric_configuration.add_range(@range) | 133 | metric_configuration.add_range(@range) |
98 | metric_configuration.save | 134 | metric_configuration.save |
135 | + if metric_configuration_has_errors? metric_configuration | ||
136 | + redirect_to_error_page metric_configuration.errors[0].message | ||
137 | + end | ||
99 | end | 138 | end |
100 | - | 139 | + |
101 | def update_range | 140 | def update_range |
102 | configuration_content = profile.articles.find(params[:id]) | 141 | configuration_content = profile.articles.find(params[:id]) |
103 | metric_name = params[:metric_name] | 142 | metric_name = params[:metric_name] |
@@ -106,8 +145,11 @@ class MezuroPluginMyprofileController < ProfileController | @@ -106,8 +145,11 @@ class MezuroPluginMyprofileController < ProfileController | ||
106 | index = metric_configuration.ranges.index{ |range| range.beginning == beginning_id.to_f || beginning_id == "-INF" } | 145 | index = metric_configuration.ranges.index{ |range| range.beginning == beginning_id.to_f || beginning_id == "-INF" } |
107 | metric_configuration.ranges[index] = Kalibro::Range.new params[:range] | 146 | metric_configuration.ranges[index] = Kalibro::Range.new params[:range] |
108 | metric_configuration.save | 147 | metric_configuration.save |
148 | + if metric_configuration_has_errors? metric_configuration | ||
149 | + redirect_to_error_page metric_configuration.errors[0].message | ||
150 | + end | ||
109 | end | 151 | end |
110 | - | 152 | + |
111 | def remove_range | 153 | def remove_range |
112 | configuration_content = profile.articles.find(params[:id]) | 154 | configuration_content = profile.articles.find(params[:id]) |
113 | metric_name = params[:metric_name] | 155 | metric_name = params[:metric_name] |
@@ -115,12 +157,31 @@ class MezuroPluginMyprofileController < ProfileController | @@ -115,12 +157,31 @@ class MezuroPluginMyprofileController < ProfileController | ||
115 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(configuration_content.name, metric_name) | 157 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(configuration_content.name, metric_name) |
116 | metric_configuration.ranges.delete_if { |range| range.beginning == beginning_id.to_f || beginning_id == "-INF" } | 158 | metric_configuration.ranges.delete_if { |range| range.beginning == beginning_id.to_f || beginning_id == "-INF" } |
117 | metric_configuration.save | 159 | metric_configuration.save |
118 | - formatted_metric_name = metric_name.gsub(/\s/, '+') | ||
119 | - if metric_configuration.metric.class == Kalibro::CompoundMetric | ||
120 | - redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/edit_compound_metric_configuration?id=#{configuration_content.id}&metric_name=#{formatted_metric_name}" | 160 | + if metric_configuration_has_errors? metric_configuration |
161 | + redirect_to_error_page metric_configuration.errors[0].message | ||
121 | else | 162 | else |
122 | - redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/edit_metric_configuration?id=#{configuration_content.id}&metric_name=#{formatted_metric_name}" | 163 | + formatted_metric_name = metric_name.gsub(/\s/, '+') |
164 | + if metric_configuration.metric.class == Kalibro::CompoundMetric | ||
165 | + redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/edit_compound_metric_configuration?id=#{configuration_content.id}&metric_name=#{formatted_metric_name}" | ||
166 | + else | ||
167 | + redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/edit_metric_configuration?id=#{configuration_content.id}&metric_name=#{formatted_metric_name}" | ||
168 | + end | ||
123 | end | 169 | end |
124 | end | 170 | end |
125 | 171 | ||
172 | + private | ||
173 | + | ||
174 | + def redirect_to_error_page(message) | ||
175 | + message = URI.escape(CGI.escape(message),'.') | ||
176 | + redirect_to "/myprofile/#{profile.identifier}/plugin/mezuro/error_page?message=#{message}" | ||
177 | + end | ||
178 | + | ||
179 | + def configuration_content_has_errors? | ||
180 | + not @configuration_content.errors[:base].nil? | ||
181 | + end | ||
182 | + | ||
183 | + def metric_configuration_has_errors? metric_configuration | ||
184 | + not metric_configuration.errors.empty? | ||
185 | + end | ||
186 | + | ||
126 | end | 187 | end |
plugins/mezuro/controllers/mezuro_plugin_profile_controller.rb
1 | class MezuroPluginProfileController < ProfileController | 1 | class MezuroPluginProfileController < ProfileController |
2 | 2 | ||
3 | append_view_path File.join(File.dirname(__FILE__) + '/../views') | 3 | append_view_path File.join(File.dirname(__FILE__) + '/../views') |
4 | - | 4 | + |
5 | + def error_page | ||
6 | + @message = params[:message] | ||
7 | + end | ||
8 | + | ||
5 | def project_state | 9 | def project_state |
6 | @content = profile.articles.find(params[:id]) | 10 | @content = profile.articles.find(params[:id]) |
7 | project = @content.project | 11 | project = @content.project |
8 | - state = project.error.nil? ? project.state : "ERROR" | ||
9 | - render :text => state | 12 | + if project_content_has_errors? |
13 | + redirect_to_error_page(@content.errors[:base]) | ||
14 | + else | ||
15 | + state = project.kalibro_error.nil? ? project.state : "ERROR" | ||
16 | + render :text => state | ||
17 | + end | ||
10 | end | 18 | end |
11 | 19 | ||
12 | def project_error | 20 | def project_error |
13 | @content = profile.articles.find(params[:id]) | 21 | @content = profile.articles.find(params[:id]) |
14 | @project = @content.project | 22 | @project = @content.project |
15 | - render :partial => 'content_viewer/project_error' | 23 | + if project_content_has_errors? |
24 | + redirect_to_error_page(@content.errors[:base]) | ||
25 | + else | ||
26 | + render :partial => 'content_viewer/project_error' | ||
27 | + end | ||
16 | end | 28 | end |
17 | 29 | ||
18 | def project_result | 30 | def project_result |
19 | @content = profile.articles.find(params[:id]) | 31 | @content = profile.articles.find(params[:id]) |
20 | date = params[:date] | 32 | date = params[:date] |
21 | @project_result = date.nil? ? @content.project_result : @content.project_result_with_date(date) | 33 | @project_result = date.nil? ? @content.project_result : @content.project_result_with_date(date) |
22 | - render :partial => 'content_viewer/project_result' | ||
23 | - end | 34 | + if project_content_has_errors? |
35 | + redirect_to_error_page(@content.errors[:base]) | ||
36 | + else | ||
37 | + render :partial => 'content_viewer/project_result' | ||
38 | + end | ||
39 | + end | ||
24 | 40 | ||
25 | def module_result | 41 | def module_result |
26 | @content = profile.articles.find(params[:id]) | 42 | @content = profile.articles.find(params[:id]) |
27 | @module_result = @content.module_result(params) | 43 | @module_result = @content.module_result(params) |
28 | - render :partial => 'content_viewer/module_result' | 44 | + @module = @module_result.module |
45 | + @module_label = "#{@module.name} (#{@module.granularity})" | ||
46 | + if project_content_has_errors? | ||
47 | + redirect_to_error_page(@content.errors[:base]) | ||
48 | + else | ||
49 | + render :partial => 'content_viewer/module_result' | ||
50 | + end | ||
29 | end | 51 | end |
30 | 52 | ||
31 | def project_tree | 53 | def project_tree |
32 | @content = profile.articles.find(params[:id]) | 54 | @content = profile.articles.find(params[:id]) |
33 | date = params[:date] | 55 | date = params[:date] |
34 | project_result = date.nil? ? @content.project_result : @content.project_result_with_date(date) | 56 | project_result = date.nil? ? @content.project_result : @content.project_result_with_date(date) |
35 | - @project_name = @content.project.name | ||
36 | - @source_tree = project_result.node_of(params[:module_name]) | ||
37 | - render :partial =>'content_viewer/source_tree' | 57 | + @project_name = @content.project.name if not @content.project.nil? |
58 | + if project_content_has_errors? | ||
59 | + redirect_to_error_page(@content.errors[:base]) | ||
60 | + else | ||
61 | + @source_tree = project_result.node(params[:module_name]) | ||
62 | + render :partial =>'content_viewer/source_tree' | ||
63 | + end | ||
38 | end | 64 | end |
39 | 65 | ||
40 | def module_metrics_history | 66 | def module_metrics_history |
41 | metric_name = params[:metric_name] | 67 | metric_name = params[:metric_name] |
42 | @content = profile.articles.find(params[:id]) | 68 | @content = profile.articles.find(params[:id]) |
43 | module_history = @content.result_history(params[:module_name]) | 69 | module_history = @content.result_history(params[:module_name]) |
44 | - @score_history = filtering_metric_history(metric_name, module_history) | ||
45 | - render :partial => 'content_viewer/score_history' | 70 | + if project_content_has_errors? |
71 | + redirect_to_error_page(@content.errors[:base]) | ||
72 | + else | ||
73 | + @score_history = filtering_metric_history(metric_name, module_history) | ||
74 | + render :partial => 'content_viewer/score_history' | ||
75 | + end | ||
46 | end | 76 | end |
47 | 77 | ||
48 | def module_grade_history | 78 | def module_grade_history |
49 | @content = profile.articles.find(params[:id]) | 79 | @content = profile.articles.find(params[:id]) |
50 | modules_results = @content.result_history(params[:module_name]) | 80 | modules_results = @content.result_history(params[:module_name]) |
51 | - @score_history = modules_results.collect { |module_result| module_result.grade } | ||
52 | - render :partial => 'content_viewer/score_history' | 81 | + if project_content_has_errors? |
82 | + redirect_to_error_page(@content.errors[:base]) | ||
83 | + else | ||
84 | + @score_history = modules_results.map do |module_result| | ||
85 | + [module_result.grade, format_date_to_simple_form(module_result.date)] | ||
86 | + end | ||
87 | + render :partial => 'content_viewer/score_history' | ||
88 | + end | ||
53 | end | 89 | end |
54 | - | 90 | + |
55 | private | 91 | private |
56 | - | 92 | + |
57 | def filtering_metric_history(metric_name, module_history) | 93 | def filtering_metric_history(metric_name, module_history) |
58 | metrics_history = module_history.map do |module_result| | 94 | metrics_history = module_history.map do |module_result| |
59 | - module_result.metric_results | 95 | + [module_result.metric_results, format_date_to_simple_form(module_result.date)] |
60 | end | 96 | end |
61 | - metric_history = metrics_history.map do |array_of_metric_result| | ||
62 | - (array_of_metric_result.select do |metric_result| | 97 | + metric_history = metrics_history.map do |metric_results_with_date| |
98 | + [(metric_results_with_date.first.select do |metric_result| | ||
63 | metric_result.metric.name.delete("() ") == metric_name | 99 | metric_result.metric.name.delete("() ") == metric_name |
64 | - end).first | 100 | + end).first, metric_results_with_date.last] |
65 | end | 101 | end |
66 | - metric_history.map do |metric_result| | ||
67 | - metric_result.value | 102 | + metric_history.map do |metric_result_with_date| |
103 | + [metric_result_with_date.first.value, metric_result_with_date.last] | ||
68 | end | 104 | end |
69 | end | 105 | end |
106 | + | ||
107 | + def redirect_to_error_page(message) | ||
108 | + message = URI.escape(CGI.escape(message),'.') | ||
109 | + redirect_to "/profile/#{profile.identifier}/plugins/mezuro/error_page?message=#{message}" | ||
110 | + end | ||
111 | + | ||
112 | + def project_content_has_errors? | ||
113 | + not @content.errors[:base].nil? | ||
114 | + end | ||
115 | + | ||
116 | + def format_date_to_simple_form date | ||
117 | + date.to_s[0..9] | ||
118 | + end | ||
119 | + | ||
70 | end | 120 | end |
plugins/mezuro/lib/kalibro/configuration.rb
@@ -7,11 +7,7 @@ class Kalibro::Configuration < Kalibro::Model | @@ -7,11 +7,7 @@ class Kalibro::Configuration < Kalibro::Model | ||
7 | end | 7 | end |
8 | 8 | ||
9 | def metric_configurations | 9 | def metric_configurations |
10 | - if @metric_configuration != nil | ||
11 | - @metric_configuration | ||
12 | - else | ||
13 | - [] | ||
14 | - end | 10 | + @metric_configuration.nil? ? [] : @metric_configuration |
15 | end | 11 | end |
16 | 12 | ||
17 | def metric_configurations=(metric_configurations) | 13 | def metric_configurations=(metric_configurations) |
@@ -19,19 +15,11 @@ class Kalibro::Configuration < Kalibro::Model | @@ -19,19 +15,11 @@ class Kalibro::Configuration < Kalibro::Model | ||
19 | end | 15 | end |
20 | 16 | ||
21 | def self.find_by_name(configuration_name) | 17 | def self.find_by_name(configuration_name) |
22 | - begin | ||
23 | - new request("Configuration", :get_configuration, {:configuration_name => configuration_name})[:configuration] | ||
24 | - rescue Exception => error | ||
25 | - nil | ||
26 | - end | 18 | + new request("Configuration", :get_configuration, {:configuration_name => configuration_name})[:configuration] |
27 | end | 19 | end |
28 | 20 | ||
29 | def self.all_names | 21 | def self.all_names |
30 | - begin | ||
31 | - request("Configuration", :get_configuration_names)[:configuration_name] | ||
32 | - rescue Exception | ||
33 | - [] | ||
34 | - end | 22 | + request("Configuration", :get_configuration_names)[:configuration_name] |
35 | end | 23 | end |
36 | 24 | ||
37 | def update_attributes(attributes={}) | 25 | def update_attributes(attributes={}) |
plugins/mezuro/lib/kalibro/metric_configuration.rb
@@ -49,10 +49,14 @@ class Kalibro::MetricConfiguration < Kalibro::Model | @@ -49,10 +49,14 @@ class Kalibro::MetricConfiguration < Kalibro::Model | ||
49 | end | 49 | end |
50 | 50 | ||
51 | def destroy | 51 | def destroy |
52 | - self.class.request("MetricConfiguration", :remove_metric_configuration, { | 52 | + begin |
53 | + self.class.request("MetricConfiguration", :remove_metric_configuration, { | ||
53 | :configuration_name => configuration_name, | 54 | :configuration_name => configuration_name, |
54 | :metric_name=> metric.name | 55 | :metric_name=> metric.name |
55 | }) | 56 | }) |
57 | + rescue Exception => exception | ||
58 | + add_error exception | ||
59 | + end | ||
56 | end | 60 | end |
57 | 61 | ||
58 | def to_hash | 62 | def to_hash |
plugins/mezuro/lib/kalibro/model.rb
1 | class Kalibro::Model | 1 | class Kalibro::Model |
2 | 2 | ||
3 | + attr_accessor :errors | ||
4 | + | ||
3 | def initialize(attributes={}) | 5 | def initialize(attributes={}) |
4 | attributes.each { |field, value| send("#{field}=", value) if self.class.is_valid?(field) } | 6 | attributes.each { |field, value| send("#{field}=", value) if self.class.is_valid?(field) } |
7 | + @errors = [] | ||
5 | end | 8 | end |
6 | 9 | ||
7 | def to_hash(options={}) | 10 | def to_hash(options={}) |
8 | hash = Hash.new | 11 | hash = Hash.new |
9 | excepts = !options[:except].nil? ? options[:except] : [] | 12 | excepts = !options[:except].nil? ? options[:except] : [] |
13 | + excepts << :errors | ||
10 | fields.each do |field| | 14 | fields.each do |field| |
11 | if(!excepts.include?(field)) | 15 | if(!excepts.include?(field)) |
12 | field_value = send(field) | 16 | field_value = send(field) |
@@ -46,15 +50,17 @@ class Kalibro::Model | @@ -46,15 +50,17 @@ class Kalibro::Model | ||
46 | begin | 50 | begin |
47 | self.class.request(save_endpoint, save_action, save_params) | 51 | self.class.request(save_endpoint, save_action, save_params) |
48 | true | 52 | true |
49 | - rescue Exception => error | ||
50 | - false | 53 | + rescue Exception => exception |
54 | + add_error exception | ||
55 | + false | ||
51 | end | 56 | end |
52 | end | 57 | end |
53 | 58 | ||
54 | def destroy | 59 | def destroy |
55 | begin | 60 | begin |
56 | self.class.request(destroy_endpoint, destroy_action, destroy_params) | 61 | self.class.request(destroy_endpoint, destroy_action, destroy_params) |
57 | - rescue Exception | 62 | + rescue Exception => exception |
63 | + add_error exception | ||
58 | end | 64 | end |
59 | end | 65 | end |
60 | 66 | ||
@@ -123,4 +129,9 @@ class Kalibro::Model | @@ -123,4 +129,9 @@ class Kalibro::Model | ||
123 | {"#{class_name.underscore}_name".to_sym => self.name} | 129 | {"#{class_name.underscore}_name".to_sym => self.name} |
124 | end | 130 | end |
125 | 131 | ||
132 | + def add_error(exception) | ||
133 | + @errors << exception | ||
134 | + end | ||
135 | + | ||
126 | end | 136 | end |
137 | + |
plugins/mezuro/lib/kalibro/project.rb
1 | class Kalibro::Project < Kalibro::Model | 1 | class Kalibro::Project < Kalibro::Model |
2 | 2 | ||
3 | - attr_accessor :name, :license, :description, :repository, :configuration_name, :state, :error | 3 | + attr_accessor :name, :license, :description, :repository, :configuration_name, :state, :kalibro_error |
4 | 4 | ||
5 | def self.all_names | 5 | def self.all_names |
6 | request("Project", :get_project_names)[:project_name] | 6 | request("Project", :get_project_names)[:project_name] |
@@ -15,23 +15,35 @@ class Kalibro::Project < Kalibro::Model | @@ -15,23 +15,35 @@ class Kalibro::Project < Kalibro::Model | ||
15 | end | 15 | end |
16 | 16 | ||
17 | def error=(value) | 17 | def error=(value) |
18 | - @error = Kalibro::Error.to_object value | 18 | + @kalibro_error = Kalibro::Error.to_object value |
19 | end | 19 | end |
20 | 20 | ||
21 | def process_project(days = '0') | 21 | def process_project(days = '0') |
22 | - if days.to_i.zero? | ||
23 | - self.class.request("Kalibro", :process_project, {:project_name => name}) | ||
24 | - else | ||
25 | - self.class.request("Kalibro", :process_periodically, {:project_name => name, :period_in_days => days}) | ||
26 | - end | 22 | + begin |
23 | + if days.to_i.zero? | ||
24 | + self.class.request("Kalibro", :process_project, {:project_name => name}) | ||
25 | + else | ||
26 | + self.class.request("Kalibro", :process_periodically, {:project_name => name, :period_in_days => days}) | ||
27 | + end | ||
28 | + rescue Exception => exception | ||
29 | + add_error exception | ||
30 | + end | ||
27 | end | 31 | end |
28 | 32 | ||
29 | def process_period | 33 | def process_period |
30 | - self.class.request("Kalibro", :get_process_period, {:project_name => name})[:period] | 34 | + begin |
35 | + self.class.request("Kalibro", :get_process_period, {:project_name => name})[:period] | ||
36 | + rescue Exception => exception | ||
37 | + add_error exception | ||
38 | + end | ||
31 | end | 39 | end |
32 | 40 | ||
33 | def cancel_periodic_process | 41 | def cancel_periodic_process |
34 | - self.class.request("Kalibro", :cancel_periodic_process, {:project_name => name}) | 42 | + begin |
43 | + self.class.request("Kalibro", :cancel_periodic_process, {:project_name => name}) | ||
44 | + rescue Exception => exception | ||
45 | + add_error exception | ||
46 | + end | ||
35 | end | 47 | end |
36 | 48 | ||
37 | end | 49 | end |
plugins/mezuro/lib/kalibro/project_result.rb
@@ -75,21 +75,17 @@ class Kalibro::ProjectResult < Kalibro::Model | @@ -75,21 +75,17 @@ class Kalibro::ProjectResult < Kalibro::Model | ||
75 | ('%2d' % amount).sub(/\s/, '0') | 75 | ('%2d' % amount).sub(/\s/, '0') |
76 | end | 76 | end |
77 | 77 | ||
78 | - def node_of(module_name) | 78 | + def node(module_name) |
79 | if module_name.nil? or module_name == project.name | 79 | if module_name.nil? or module_name == project.name |
80 | node = source_tree | 80 | node = source_tree |
81 | else | 81 | else |
82 | - node = get_node(module_name) | ||
83 | - end | ||
84 | - end | ||
85 | - | ||
86 | - def get_node(module_name) | ||
87 | - path = Kalibro::Module.parent_names(module_name) | ||
88 | - parent = @source_tree | ||
89 | - path.each do |node_name| | ||
90 | - parent = get_leaf_from(parent, node_name) | ||
91 | - end | ||
92 | - return parent | 82 | + path = Kalibro::Module.parent_names(module_name) |
83 | + parent = @source_tree | ||
84 | + path.each do |node_name| | ||
85 | + parent = get_leaf_from(parent, node_name) | ||
86 | + end | ||
87 | + parent | ||
88 | + end | ||
93 | end | 89 | end |
94 | 90 | ||
95 | private | 91 | private |
plugins/mezuro/lib/kalibro/range.rb
@@ -34,4 +34,12 @@ class Kalibro::Range < Kalibro::Model | @@ -34,4 +34,12 @@ class Kalibro::Range < Kalibro::Model | ||
34 | @grade = value.to_f | 34 | @grade = value.to_f |
35 | end | 35 | end |
36 | 36 | ||
37 | + def mezuro_color | ||
38 | + @color.nil? ? "#e4ca2d" : @color.gsub(/^ff/, "#") | ||
39 | + end | ||
40 | + | ||
41 | + def color=(new_color) | ||
42 | + @color = new_color.gsub(/^#/, "ff") | ||
43 | + end | ||
44 | + | ||
37 | end | 45 | end |
plugins/mezuro/lib/mezuro_plugin/configuration_content.rb
@@ -3,8 +3,8 @@ class MezuroPlugin::ConfigurationContent < Article | @@ -3,8 +3,8 @@ class MezuroPlugin::ConfigurationContent < Article | ||
3 | 3 | ||
4 | settings_items :description, :configuration_to_clone_name | 4 | settings_items :description, :configuration_to_clone_name |
5 | 5 | ||
6 | - after_save :send_configuration_to_service | ||
7 | - after_destroy :remove_configuration_from_service | 6 | + after_save :send_kalibro_configuration_to_service |
7 | + after_destroy :remove_kalibro_configuration_from_service | ||
8 | 8 | ||
9 | def self.short_description | 9 | def self.short_description |
10 | 'Kalibro configuration' | 10 | 'Kalibro configuration' |
@@ -21,54 +21,60 @@ class MezuroPlugin::ConfigurationContent < Article | @@ -21,54 +21,60 @@ class MezuroPlugin::ConfigurationContent < Article | ||
21 | end | 21 | end |
22 | end | 22 | end |
23 | 23 | ||
24 | - def configuration | ||
25 | - @configuration ||= Kalibro::Configuration.find_by_name(self.name) | ||
26 | - if @configuration.nil? | ||
27 | - errors.add_to_base("Kalibro Configuration not found") | 24 | + def kalibro_configuration |
25 | + begin | ||
26 | + @kalibro_configuration ||= Kalibro::Configuration.find_by_name(self.name) | ||
27 | + rescue Exception => exception | ||
28 | + errors.add_to_base(exception.message) | ||
28 | end | 29 | end |
29 | - @configuration | 30 | + @kalibro_configuration |
30 | end | 31 | end |
31 | 32 | ||
32 | def metric_configurations | 33 | def metric_configurations |
33 | - configuration.metric_configurations | 34 | + kalibro_configuration.metric_configurations |
34 | end | 35 | end |
35 | 36 | ||
36 | - def configuration_names | ||
37 | - ["None"] + Kalibro::Configuration.all_names.sort | 37 | + def kalibro_configuration_names |
38 | + begin | ||
39 | + ["None"] + Kalibro::Configuration.all_names.sort | ||
40 | + rescue Exception => exception | ||
41 | + errors.add_to_base(exception.message) | ||
42 | + ["None"] | ||
43 | + end | ||
38 | end | 44 | end |
39 | 45 | ||
40 | private | 46 | private |
41 | 47 | ||
42 | def validate_kalibro_configuration_name | 48 | def validate_kalibro_configuration_name |
43 | - existing = configuration_names.map { |a| a.downcase} | 49 | + existing = kalibro_configuration_names.map { |a| a.downcase} |
44 | 50 | ||
45 | if existing.include?(name.downcase) | 51 | if existing.include?(name.downcase) |
46 | errors.add_to_base("Configuration name already exists in Kalibro") | 52 | errors.add_to_base("Configuration name already exists in Kalibro") |
47 | end | 53 | end |
48 | end | 54 | end |
49 | 55 | ||
50 | - def send_configuration_to_service | ||
51 | - if editing_configuration? | ||
52 | - configuration.update_attributes({:description => description}) | 56 | + def send_kalibro_configuration_to_service |
57 | + if editing_kalibro_configuration? | ||
58 | + kalibro_configuration.update_attributes({:description => description}) | ||
53 | else | 59 | else |
54 | create_kalibro_configuration | 60 | create_kalibro_configuration |
55 | end | 61 | end |
56 | end | 62 | end |
57 | 63 | ||
58 | - def remove_configuration_from_service | ||
59 | - configuration.destroy | 64 | + def remove_kalibro_configuration_from_service |
65 | + kalibro_configuration.destroy unless kalibro_configuration.nil? | ||
60 | end | 66 | end |
61 | 67 | ||
62 | def create_kalibro_configuration | 68 | def create_kalibro_configuration |
63 | attributes = {:name => name, :description => description} | 69 | attributes = {:name => name, :description => description} |
64 | - if cloning_configuration? | 70 | + if cloning_kalibro_configuration? |
65 | attributes[:metric_configuration] = configuration_to_clone.metric_configurations_hash | 71 | attributes[:metric_configuration] = configuration_to_clone.metric_configurations_hash |
66 | end | 72 | end |
67 | Kalibro::Configuration.create attributes | 73 | Kalibro::Configuration.create attributes |
68 | end | 74 | end |
69 | 75 | ||
70 | - def editing_configuration? | ||
71 | - configuration.present? | 76 | + def editing_kalibro_configuration? |
77 | + kalibro_configuration.present? | ||
72 | end | 78 | end |
73 | 79 | ||
74 | def configuration_to_clone | 80 | def configuration_to_clone |
@@ -76,10 +82,10 @@ class MezuroPlugin::ConfigurationContent < Article | @@ -76,10 +82,10 @@ class MezuroPlugin::ConfigurationContent < Article | ||
76 | end | 82 | end |
77 | 83 | ||
78 | def find_configuration_to_clone | 84 | def find_configuration_to_clone |
79 | - configuration_to_clone_name.nil? ? nil : Kalibro::Configuration.find_by_name(configuration_to_clone_name) | 85 | + (configuration_to_clone_name == "None") ? nil : Kalibro::Configuration.find_by_name(configuration_to_clone_name) |
80 | end | 86 | end |
81 | 87 | ||
82 | - def cloning_configuration? | 88 | + def cloning_kalibro_configuration? |
83 | configuration_to_clone.present? | 89 | configuration_to_clone.present? |
84 | end | 90 | end |
85 | 91 |
plugins/mezuro/lib/mezuro_plugin/helpers/content_viewer_helper.rb
1 | class MezuroPlugin::Helpers::ContentViewerHelper | 1 | class MezuroPlugin::Helpers::ContentViewerHelper |
2 | + | ||
3 | + MAX_NUMBER_OF_LABELS = 5 | ||
4 | + | ||
2 | def self.format_grade(grade) | 5 | def self.format_grade(grade) |
3 | sprintf("%.2f", grade.to_f) | 6 | sprintf("%.2f", grade.to_f) |
4 | end | 7 | end |
@@ -6,15 +9,31 @@ class MezuroPlugin::Helpers::ContentViewerHelper | @@ -6,15 +9,31 @@ class MezuroPlugin::Helpers::ContentViewerHelper | ||
6 | def self.create_periodicity_options | 9 | def self.create_periodicity_options |
7 | [["Not Periodically", 0], ["1 day", 1], ["2 days", 2], ["Weekly", 7], ["Biweeky", 15], ["Monthly", 30]] | 10 | [["Not Periodically", 0], ["1 day", 1], ["2 days", 2], ["Weekly", 7], ["Biweeky", 15], ["Monthly", 30]] |
8 | end | 11 | end |
9 | - | ||
10 | - def self.generate_chart(values) | ||
11 | - Gchart.line( | 12 | + |
13 | + def self.create_license_options | ||
14 | + options = YAML.load_file("#{RAILS_ROOT}/plugins/mezuro/licenses.yaml") | ||
15 | + options = options.split(";") | ||
16 | + formated_options = [] | ||
17 | + options.each { |option| formated_options << [option, option] } | ||
18 | + formated_options | ||
19 | + end | ||
20 | + | ||
21 | + def self.generate_chart(score_history) | ||
22 | + values = [] | ||
23 | + labels = [] | ||
24 | + score_history.each do |score_data| | ||
25 | + values << score_data.first | ||
26 | + labels << score_data.last | ||
27 | + end | ||
28 | + labels = discretize_array labels | ||
29 | + Gchart.line( | ||
12 | :title_color => 'FF0000', | 30 | :title_color => 'FF0000', |
13 | :size => '600x180', | 31 | :size => '600x180', |
14 | :bg => {:color => 'efefef', :type => 'stripes'}, | 32 | :bg => {:color => 'efefef', :type => 'stripes'}, |
15 | :line_colors => 'c4a000', | 33 | :line_colors => 'c4a000', |
16 | :data => values, | 34 | :data => values, |
17 | - :axis_with_labels => 'y', | 35 | + :labels => labels, |
36 | + :axis_with_labels => ['y','x'], | ||
18 | :max_value => values.max, | 37 | :max_value => values.max, |
19 | :min_value => values.min | 38 | :min_value => values.min |
20 | ) | 39 | ) |
@@ -25,8 +44,33 @@ class MezuroPlugin::Helpers::ContentViewerHelper | @@ -25,8 +44,33 @@ class MezuroPlugin::Helpers::ContentViewerHelper | ||
25 | selected_option = options.find { |option| option.last == index.to_i } | 44 | selected_option = options.find { |option| option.last == index.to_i } |
26 | selected_option.first | 45 | selected_option.first |
27 | end | 46 | end |
28 | - | 47 | + |
29 | def self.format_name(metric_result) | 48 | def self.format_name(metric_result) |
30 | metric_result.metric.name.delete("() ") | 49 | metric_result.metric.name.delete("() ") |
31 | end | 50 | end |
51 | + | ||
52 | + def self.get_license_option(selected) | ||
53 | + options = YAML.load_file("#{RAILS_ROOT}/plugins/mezuro/licenses.yaml") | ||
54 | + options.split(";") | ||
55 | + selected_option = options.find { |license| license == selected } | ||
56 | + end | ||
57 | + | ||
58 | + private | ||
59 | + | ||
60 | + def self.discretize_array(array) | ||
61 | + if array.size > MAX_NUMBER_OF_LABELS | ||
62 | + range_array.map { |i| discrete_element(array, i)} | ||
63 | + else | ||
64 | + array | ||
65 | + end | ||
66 | + end | ||
67 | + | ||
68 | + def self.range_array | ||
69 | + (0..(MAX_NUMBER_OF_LABELS - 1)).to_a | ||
70 | + end | ||
71 | + | ||
72 | + def self.discrete_element(array, i) | ||
73 | + array[(i*(array.size - 1))/(MAX_NUMBER_OF_LABELS - 1)] | ||
74 | + end | ||
75 | + | ||
32 | end | 76 | end |
plugins/mezuro/lib/mezuro_plugin/project_content.rb
1 | class MezuroPlugin::ProjectContent < Article | 1 | class MezuroPlugin::ProjectContent < Article |
2 | include ActionView::Helpers::TagHelper | 2 | include ActionView::Helpers::TagHelper |
3 | 3 | ||
4 | - settings_items :license, :description, :repository_type, :repository_url, :configuration_name, :periodicity_in_days | 4 | + settings_items :project_license, :description, :repository_type, :repository_url, :configuration_name, :periodicity_in_days |
5 | 5 | ||
6 | validate_on_create :validate_kalibro_project_name | 6 | validate_on_create :validate_kalibro_project_name |
7 | validate_on_create :validate_repository_url | 7 | validate_on_create :validate_repository_url |
@@ -26,6 +26,7 @@ class MezuroPlugin::ProjectContent < Article | @@ -26,6 +26,7 @@ class MezuroPlugin::ProjectContent < Article | ||
26 | rescue Exception => error | 26 | rescue Exception => error |
27 | errors.add_to_base(error.message) | 27 | errors.add_to_base(error.message) |
28 | end | 28 | end |
29 | + @project | ||
29 | end | 30 | end |
30 | 31 | ||
31 | def project_result | 32 | def project_result |
@@ -34,6 +35,7 @@ class MezuroPlugin::ProjectContent < Article | @@ -34,6 +35,7 @@ class MezuroPlugin::ProjectContent < Article | ||
34 | rescue Exception => error | 35 | rescue Exception => error |
35 | errors.add_to_base(error.message) | 36 | errors.add_to_base(error.message) |
36 | end | 37 | end |
38 | + @project_result | ||
37 | end | 39 | end |
38 | 40 | ||
39 | def project_result_with_date(date) | 41 | def project_result_with_date(date) |
@@ -43,6 +45,7 @@ Kalibro::ProjectResult.first_result_after(name, date) | @@ -43,6 +45,7 @@ Kalibro::ProjectResult.first_result_after(name, date) | ||
43 | rescue Exception => error | 45 | rescue Exception => error |
44 | errors.add_to_base(error.message) | 46 | errors.add_to_base(error.message) |
45 | end | 47 | end |
48 | + @project_result | ||
46 | end | 49 | end |
47 | 50 | ||
48 | def module_result(attributes) | 51 | def module_result(attributes) |
@@ -53,6 +56,7 @@ Kalibro::ProjectResult.first_result_after(name, date) | @@ -53,6 +56,7 @@ Kalibro::ProjectResult.first_result_after(name, date) | ||
53 | rescue Exception => error | 56 | rescue Exception => error |
54 | errors.add_to_base(error.message) | 57 | errors.add_to_base(error.message) |
55 | end | 58 | end |
59 | + @module_result | ||
56 | end | 60 | end |
57 | 61 | ||
58 | def result_history(module_name) | 62 | def result_history(module_name) |
@@ -73,6 +77,7 @@ Kalibro::ProjectResult.first_result_after(name, date) | @@ -73,6 +77,7 @@ Kalibro::ProjectResult.first_result_after(name, date) | ||
73 | existing = Kalibro::Project.all_names | 77 | existing = Kalibro::Project.all_names |
74 | rescue Exception => error | 78 | rescue Exception => error |
75 | errors.add_to_base(error.message) | 79 | errors.add_to_base(error.message) |
80 | + existing = [] | ||
76 | end | 81 | end |
77 | 82 | ||
78 | if existing.any?{|existing_name| existing_name.casecmp(name)==0} # existing.include?(name) + case insensitive | 83 | if existing.any?{|existing_name| existing_name.casecmp(name)==0} # existing.include?(name) + case insensitive |
@@ -94,7 +99,7 @@ Kalibro::ProjectResult.first_result_after(name, date) | @@ -94,7 +99,7 @@ Kalibro::ProjectResult.first_result_after(name, date) | ||
94 | def create_kalibro_project | 99 | def create_kalibro_project |
95 | Kalibro::Project.create( | 100 | Kalibro::Project.create( |
96 | :name => name, | 101 | :name => name, |
97 | - :license => license, | 102 | + :license => project_license, |
98 | :description => description, | 103 | :description => description, |
99 | :repository => { | 104 | :repository => { |
100 | :type => repository_type, | 105 | :type => repository_type, |
@@ -105,7 +110,7 @@ Kalibro::ProjectResult.first_result_after(name, date) | @@ -105,7 +110,7 @@ Kalibro::ProjectResult.first_result_after(name, date) | ||
105 | end | 110 | end |
106 | 111 | ||
107 | def destroy_project_from_service | 112 | def destroy_project_from_service |
108 | - project.destroy | 113 | + project.destroy unless project.nil? |
109 | end | 114 | end |
110 | 115 | ||
111 | end | 116 | end |
@@ -0,0 +1,69 @@ | @@ -0,0 +1,69 @@ | ||
1 | +Academic Free License 3.0 (AFL-3.0); | ||
2 | +Affero GNU Public License (AGPL-3.0); | ||
3 | +Adaptive Public License (APL-1.0); | ||
4 | +Apache License 2.0 (Apache-2.0); | ||
5 | +Apple Public Source License (APSL-2.0); | ||
6 | +Artistic license 2.0 (Artistic-2.0); | ||
7 | +Attribution Assurance Licenses (AAL); | ||
8 | +BSD 3-Clause "New" or "Revised" License (BSD-3-Clause); | ||
9 | +BSD 2-Clause "Simplified" or "FreeBSD" License (BSD-2-Clause); | ||
10 | +Boost Software License (BSL-1.0); | ||
11 | +Computer Associates Trusted Open Source License 1.1 (CATOSL-1.1); | ||
12 | +Common Development and Distribution License 1.0 (CDDL-1.0); | ||
13 | +Common Public Attribution License 1.0 (CPAL-1.0); | ||
14 | +CUA Office Public License Version 1.0 (CUA-OPL-1.0); | ||
15 | +EU DataGrid Software License (EUDatagrid); | ||
16 | +Eclipse Public License 1.0 (EPL-1.0); | ||
17 | +Educational Community License, Version 2.0 (ECL-2.0); | ||
18 | +Eiffel Forum License V2.0 (EFL-2.0); | ||
19 | +Entessa Public License (Entessa); | ||
20 | +European Union Public License, Version 1.1 (EUPL-1.1); | ||
21 | +Fair License (FAIR); | ||
22 | +Frameworx License (Frameworx-1.0); | ||
23 | +GNU Affero General Public License v3 (AGPL-3.0); | ||
24 | +GNU General Public License version 2.0 (GPL-2.0); | ||
25 | +GNU General Public License version 3.0 (GPL-3.0); | ||
26 | +GNU Library or "Lesser" General Public License version 2.1 (LGPL-2.1); | ||
27 | +GNU Library or "Lesser" General Public License version 3.0 (LGPL-3.0); | ||
28 | +Historical Permission Notice and Disclaimer (HPND); | ||
29 | +IBM Public License 1.0 (IPL-1.0); | ||
30 | +IPA Font License (IPA); | ||
31 | +ISC License (ISC); | ||
32 | +LaTeX Project Public License 1.3c (LPPL-1.3c); | ||
33 | +Lucent Public License Version 1.02 (LPL-1.02); | ||
34 | +MirOS Licence (MirOS); | ||
35 | +Microsoft Public License (Ms-PL); | ||
36 | +Microsoft Reciprocal License (Ms-RL); | ||
37 | +MIT license (MIT); | ||
38 | +Motosoto License (Motosoto); | ||
39 | +Mozilla Public License 2.0 (MPL-2.0); | ||
40 | +Multics License (Multics); | ||
41 | +NASA Open Source Agreement 1.3 (NASA 1.3); | ||
42 | +NTP License (NTP); | ||
43 | +Naumen Public License (Naumen); | ||
44 | +Nethack General Public License (NGPL); | ||
45 | +Nokia Open Source License (Nokia); | ||
46 | +Non-Profit Open Software License 3.0 (NPOSL-3.0); | ||
47 | +OCLC Research Public License 2.0 (OCLC-2.0); | ||
48 | +Open Font License 1.1 (OFL 1.1); | ||
49 | +Open Group Test Suite License (OGTSL); | ||
50 | +Open Software License 3.0 (OSL-3.0); | ||
51 | +PHP License 3.0 (PHP-3.0); | ||
52 | +The PostgreSQL License (PostgreSQL); | ||
53 | +Python License (Python-2.0); | ||
54 | +CNRI Python license (CNRI-Python); | ||
55 | +Q Public License (QPL-1.0); | ||
56 | +RealNetworks Public Source License V1.0 (RPSL-1.0); | ||
57 | +Reciprocal Public License 1.5 (RPL-1.5); | ||
58 | +Ricoh Source Code Public License (RSCPL); | ||
59 | +Simple Public License 2.0 (SimPL-2.0); | ||
60 | +Sleepycat License (Sleepycat); | ||
61 | +Sun Public License 1.0 (SPL-1.0); | ||
62 | +Sybase Open Watcom Public License 1.0 (Watcom-1.0); | ||
63 | +University of Illinois/NCSA Open Source License (NCSA); | ||
64 | +Vovida Software License v. 1.0 (VSL-1.0); | ||
65 | +W3C License (W3C); | ||
66 | +wxWindows Library License (WXwindows); | ||
67 | +X.Net License (Xnet); | ||
68 | +Zope Public License 2.0 (ZPL-2.0); | ||
69 | +zlib/libpng license (Zlib); |
@@ -0,0 +1,31 @@ | @@ -0,0 +1,31 @@ | ||
1 | +div.colorPicker-picker { | ||
2 | + height: 16px; | ||
3 | + width: 16px; | ||
4 | + padding: 0 !important; | ||
5 | + border: 1px solid #ccc; | ||
6 | + background: url(arrow.gif) no-repeat top right; | ||
7 | + cursor: pointer; | ||
8 | + line-height: 16px; | ||
9 | +} | ||
10 | + | ||
11 | +div.colorPicker-palette { | ||
12 | + width: 110px; | ||
13 | + position: absolute; | ||
14 | + border: 1px solid #598FEF; | ||
15 | + background-color: #EFEFEF; | ||
16 | + padding: 2px; | ||
17 | + z-index: 9999; | ||
18 | +} | ||
19 | + div.colorPicker_hexWrap {width: 100%; float:left } | ||
20 | + div.colorPicker_hexWrap label {font-size: 95%; color: #2F2F2F; margin: 5px 2px; width: 25%} | ||
21 | + div.colorPicker_hexWrap input {margin: 5px 2px; padding: 0; font-size: 95%; border: 1px solid #000; width: 65%; } | ||
22 | + | ||
23 | +div.colorPicker-swatch { | ||
24 | + height: 12px; | ||
25 | + width: 12px; | ||
26 | + border: 1px solid #000; | ||
27 | + margin: 2px; | ||
28 | + float: left; | ||
29 | + cursor: pointer; | ||
30 | + line-height: 12px; | ||
31 | +} |
@@ -0,0 +1,22 @@ | @@ -0,0 +1,22 @@ | ||
1 | +Copyright (c) 2012 Lakshan Perera | ||
2 | + | ||
3 | +Permission is hereby granted, free of charge, to any person | ||
4 | +obtaining a copy of this software and associated documentation | ||
5 | +files (the "Software"), to deal in the Software without | ||
6 | +restriction, including without limitation the rights to use, | ||
7 | +copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
8 | +copies of the Software, and to permit persons to whom the | ||
9 | +Software is furnished to do so, subject to the following | ||
10 | +conditions: | ||
11 | + | ||
12 | +The above copyright notice and this permission notice shall be | ||
13 | +included in all copies or substantial portions of the Software. | ||
14 | + | ||
15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
16 | +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
17 | +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
18 | +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | ||
19 | +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
20 | +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
21 | +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
22 | +OTHER DEALINGS IN THE SOFTWARE. |
plugins/mezuro/public/javascripts/colorPicker/jquery.colorPicker.js
0 → 100644
@@ -0,0 +1,328 @@ | @@ -0,0 +1,328 @@ | ||
1 | +/** | ||
2 | + * Really Simple Color Picker in jQuery | ||
3 | + * | ||
4 | + * Licensed under the MIT (MIT-LICENSE.txt) licenses. | ||
5 | + * | ||
6 | + * Copyright (c) 2008-2012 | ||
7 | + * Lakshan Perera (www.laktek.com) & Daniel Lacy (daniellacy.com) | ||
8 | + * | ||
9 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
10 | + * of this software and associated documentation files (the "Software"), to | ||
11 | + * deal in the Software without restriction, including without limitation the | ||
12 | + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
13 | + * sell copies of the Software, and to permit persons to whom the Software is | ||
14 | + * furnished to do so, subject to the following conditions: | ||
15 | + * | ||
16 | + * The above copyright notice and this permission notice shall be included in | ||
17 | + * all copies or substantial portions of the Software. | ||
18 | + * | ||
19 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
20 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
21 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
22 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
23 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
24 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
25 | + * IN THE SOFTWARE. | ||
26 | + */ | ||
27 | + | ||
28 | +(function ($) { | ||
29 | + /** | ||
30 | + * Create a couple private variables. | ||
31 | + **/ | ||
32 | + var selectorOwner, | ||
33 | + activePalette, | ||
34 | + cItterate = 0, | ||
35 | + templates = { | ||
36 | + control : $('<div class="colorPicker-picker"> </div>'), | ||
37 | + palette : $('<div id="colorPicker_palette" class="colorPicker-palette" />'), | ||
38 | + swatch : $('<div class="colorPicker-swatch"> </div>'), | ||
39 | + hexLabel: $('<label for="colorPicker_hex">Hex</label>'), | ||
40 | + hexField: $('<input type="text" id="colorPicker_hex" />') | ||
41 | + }, | ||
42 | + transparent = "transparent", | ||
43 | + lastColor; | ||
44 | + | ||
45 | + /** | ||
46 | + * Create our colorPicker function | ||
47 | + **/ | ||
48 | + $.fn.colorPicker = function (options) { | ||
49 | + | ||
50 | + return this.each(function () { | ||
51 | + // Setup time. Clone new elements from our templates, set some IDs, make shortcuts, jazzercise. | ||
52 | + var element = $(this), | ||
53 | + opts = $.extend({}, $.fn.colorPicker.defaults, options), | ||
54 | + defaultColor = $.fn.colorPicker.toHex( | ||
55 | + (element.val().length > 0) ? element.val() : opts.pickerDefault | ||
56 | + ), | ||
57 | + newControl = templates.control.clone(), | ||
58 | + newPalette = templates.palette.clone().attr('id', 'colorPicker_palette-' + cItterate), | ||
59 | + newHexLabel = templates.hexLabel.clone(), | ||
60 | + newHexField = templates.hexField.clone(), | ||
61 | + paletteId = newPalette[0].id, | ||
62 | + swatch; | ||
63 | + | ||
64 | + | ||
65 | + /** | ||
66 | + * Build a color palette. | ||
67 | + **/ | ||
68 | + $.each(opts.colors, function (i) { | ||
69 | + swatch = templates.swatch.clone(); | ||
70 | + | ||
71 | + if (opts.colors[i] === transparent) { | ||
72 | + swatch.addClass(transparent).text('X'); | ||
73 | + $.fn.colorPicker.bindPalette(newHexField, swatch, transparent); | ||
74 | + } else { | ||
75 | + swatch.css("background-color", "#" + this); | ||
76 | + $.fn.colorPicker.bindPalette(newHexField, swatch); | ||
77 | + } | ||
78 | + swatch.appendTo(newPalette); | ||
79 | + }); | ||
80 | + | ||
81 | + newHexLabel.attr('for', 'colorPicker_hex-' + cItterate); | ||
82 | + | ||
83 | + newHexField.attr({ | ||
84 | + 'id' : 'colorPicker_hex-' + cItterate, | ||
85 | + 'value' : defaultColor | ||
86 | + }); | ||
87 | + | ||
88 | + newHexField.bind("keydown", function (event) { | ||
89 | + if (event.keyCode === 13) { | ||
90 | + var hexColor = $.fn.colorPicker.toHex($(this).val()); | ||
91 | + $.fn.colorPicker.changeColor(hexColor ? hexColor : element.val()); | ||
92 | + } | ||
93 | + if (event.keyCode === 27) { | ||
94 | + $.fn.colorPicker.hidePalette(); | ||
95 | + } | ||
96 | + }); | ||
97 | + | ||
98 | + newHexField.bind("keyup", function (event) { | ||
99 | + var hexColor = $.fn.colorPicker.toHex($(event.target).val()); | ||
100 | + $.fn.colorPicker.previewColor(hexColor ? hexColor : element.val()); | ||
101 | + }); | ||
102 | + | ||
103 | + $('<div class="colorPicker_hexWrap" />').append(newHexLabel).appendTo(newPalette); | ||
104 | + | ||
105 | + newPalette.find('.colorPicker_hexWrap').append(newHexField); | ||
106 | + | ||
107 | + $("body").append(newPalette); | ||
108 | + | ||
109 | + newPalette.hide(); | ||
110 | + | ||
111 | + | ||
112 | + /** | ||
113 | + * Build replacement interface for original color input. | ||
114 | + **/ | ||
115 | + newControl.css("background-color", defaultColor); | ||
116 | + | ||
117 | + newControl.bind("click", function () { | ||
118 | + $.fn.colorPicker.togglePalette($('#' + paletteId), $(this)); | ||
119 | + }); | ||
120 | + | ||
121 | + if( options && options.onColorChange ) { | ||
122 | + newControl.data('onColorChange', options.onColorChange); | ||
123 | + } else { | ||
124 | + newControl.data('onColorChange', function() {} ); | ||
125 | + } | ||
126 | + element.after(newControl); | ||
127 | + | ||
128 | + element.bind("change", function () { | ||
129 | + element.next(".colorPicker-picker").css( | ||
130 | + "background-color", $.fn.colorPicker.toHex($(this).val()) | ||
131 | + ); | ||
132 | + }); | ||
133 | + | ||
134 | + // Hide the original input. | ||
135 | + element.val(defaultColor).hide(); | ||
136 | + | ||
137 | + cItterate++; | ||
138 | + }); | ||
139 | + }; | ||
140 | + | ||
141 | + /** | ||
142 | + * Extend colorPicker with... all our functionality. | ||
143 | + **/ | ||
144 | + $.extend(true, $.fn.colorPicker, { | ||
145 | + /** | ||
146 | + * Return a Hex color, convert an RGB value and return Hex, or return false. | ||
147 | + * | ||
148 | + * Inspired by http://code.google.com/p/jquery-color-utils | ||
149 | + **/ | ||
150 | + toHex : function (color) { | ||
151 | + // If we have a standard or shorthand Hex color, return that value. | ||
152 | + if (color.match(/[0-9A-F]{6}|[0-9A-F]{3}$/i)) { | ||
153 | + return (color.charAt(0) === "#") ? color : ("#" + color); | ||
154 | + | ||
155 | + // Alternatively, check for RGB color, then convert and return it as Hex. | ||
156 | + } else if (color.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/)) { | ||
157 | + var c = ([parseInt(RegExp.$1, 10), parseInt(RegExp.$2, 10), parseInt(RegExp.$3, 10)]), | ||
158 | + pad = function (str) { | ||
159 | + if (str.length < 2) { | ||
160 | + for (var i = 0, len = 2 - str.length; i < len; i++) { | ||
161 | + str = '0' + str; | ||
162 | + } | ||
163 | + } | ||
164 | + | ||
165 | + return str; | ||
166 | + }; | ||
167 | + | ||
168 | + if (c.length === 3) { | ||
169 | + var r = pad(c[0].toString(16)), | ||
170 | + g = pad(c[1].toString(16)), | ||
171 | + b = pad(c[2].toString(16)); | ||
172 | + | ||
173 | + return '#' + r + g + b; | ||
174 | + } | ||
175 | + | ||
176 | + // Otherwise we wont do anything. | ||
177 | + } else { | ||
178 | + return false; | ||
179 | + | ||
180 | + } | ||
181 | + }, | ||
182 | + | ||
183 | + /** | ||
184 | + * Check whether user clicked on the selector or owner. | ||
185 | + **/ | ||
186 | + checkMouse : function (event, paletteId) { | ||
187 | + var selector = activePalette, | ||
188 | + selectorParent = $(event.target).parents("#" + selector.attr('id')).length; | ||
189 | + | ||
190 | + if (event.target === $(selector)[0] || event.target === selectorOwner[0] || selectorParent > 0) { | ||
191 | + return; | ||
192 | + } | ||
193 | + | ||
194 | + $.fn.colorPicker.hidePalette(); | ||
195 | + }, | ||
196 | + | ||
197 | + /** | ||
198 | + * Hide the color palette modal. | ||
199 | + **/ | ||
200 | + hidePalette : function () { | ||
201 | + $(document).unbind("mousedown", $.fn.colorPicker.checkMouse); | ||
202 | + | ||
203 | + $('.colorPicker-palette').hide(); | ||
204 | + }, | ||
205 | + | ||
206 | + /** | ||
207 | + * Show the color palette modal. | ||
208 | + **/ | ||
209 | + showPalette : function (palette) { | ||
210 | + var hexColor = selectorOwner.prev("input").val(); | ||
211 | + | ||
212 | + palette.css({ | ||
213 | + top: selectorOwner.offset().top + (selectorOwner.outerHeight()), | ||
214 | + left: selectorOwner.offset().left | ||
215 | + }); | ||
216 | + | ||
217 | + $("#color_value").val(hexColor); | ||
218 | + | ||
219 | + palette.show(); | ||
220 | + | ||
221 | + $(document).bind("mousedown", $.fn.colorPicker.checkMouse); | ||
222 | + }, | ||
223 | + | ||
224 | + /** | ||
225 | + * Toggle visibility of the colorPicker palette. | ||
226 | + **/ | ||
227 | + togglePalette : function (palette, origin) { | ||
228 | + // selectorOwner is the clicked .colorPicker-picker. | ||
229 | + if (origin) { | ||
230 | + selectorOwner = origin; | ||
231 | + } | ||
232 | + | ||
233 | + activePalette = palette; | ||
234 | + | ||
235 | + if (activePalette.is(':visible')) { | ||
236 | + $.fn.colorPicker.hidePalette(); | ||
237 | + | ||
238 | + } else { | ||
239 | + $.fn.colorPicker.showPalette(palette); | ||
240 | + | ||
241 | + } | ||
242 | + }, | ||
243 | + | ||
244 | + /** | ||
245 | + * Update the input with a newly selected color. | ||
246 | + **/ | ||
247 | + changeColor : function (value) { | ||
248 | + selectorOwner.css("background-color", value); | ||
249 | + selectorOwner.prev("input").val(value).change(); | ||
250 | + | ||
251 | + $.fn.colorPicker.hidePalette(); | ||
252 | + | ||
253 | + selectorOwner.data('onColorChange').call(selectorOwner, $(selectorOwner).prev("input").attr("id"), value); | ||
254 | + }, | ||
255 | + | ||
256 | + | ||
257 | + /** | ||
258 | + * Preview the input with a newly selected color. | ||
259 | + **/ | ||
260 | + previewColor : function (value) { | ||
261 | + selectorOwner.css("background-color", value); | ||
262 | + }, | ||
263 | + | ||
264 | + /** | ||
265 | + * Bind events to the color palette swatches. | ||
266 | + */ | ||
267 | + bindPalette : function (paletteInput, element, color) { | ||
268 | + color = color ? color : $.fn.colorPicker.toHex(element.css("background-color")); | ||
269 | + | ||
270 | + element.bind({ | ||
271 | + click : function (ev) { | ||
272 | + lastColor = color; | ||
273 | + | ||
274 | + $.fn.colorPicker.changeColor(color); | ||
275 | + }, | ||
276 | + mouseover : function (ev) { | ||
277 | + lastColor = paletteInput.val(); | ||
278 | + | ||
279 | + $(this).css("border-color", "#598FEF"); | ||
280 | + | ||
281 | + paletteInput.val(color); | ||
282 | + | ||
283 | + $.fn.colorPicker.previewColor(color); | ||
284 | + }, | ||
285 | + mouseout : function (ev) { | ||
286 | + $(this).css("border-color", "#000"); | ||
287 | + | ||
288 | + paletteInput.val(selectorOwner.css("background-color")); | ||
289 | + | ||
290 | + paletteInput.val(lastColor); | ||
291 | + | ||
292 | + $.fn.colorPicker.previewColor(lastColor); | ||
293 | + } | ||
294 | + }); | ||
295 | + } | ||
296 | + }); | ||
297 | + | ||
298 | + /** | ||
299 | + * Default colorPicker options. | ||
300 | + * | ||
301 | + * These are publibly available for global modification using a setting such as: | ||
302 | + * | ||
303 | + * $.fn.colorPicker.defaults.colors = ['151337', '111111'] | ||
304 | + * | ||
305 | + * They can also be applied on a per-bound element basis like so: | ||
306 | + * | ||
307 | + * $('#element1').colorPicker({pickerDefault: 'efefef', transparency: true}); | ||
308 | + * $('#element2').colorPicker({pickerDefault: '333333', colors: ['333333', '111111']}); | ||
309 | + * | ||
310 | + **/ | ||
311 | + $.fn.colorPicker.defaults = { | ||
312 | + // colorPicker default selected color. | ||
313 | + pickerDefault : "FFFFFF", | ||
314 | + | ||
315 | + // Default color set. | ||
316 | + colors : [ | ||
317 | + '000000', '993300', '333300', '000080', '333399', '333333', '800000', 'FF6600', | ||
318 | + '808000', '008000', '008080', '0000FF', '666699', '808080', 'FF0000', 'FF9900', | ||
319 | + '99CC00', '339966', '33CCCC', '3366FF', '800080', '999999', 'FF00FF', 'FFCC00', | ||
320 | + 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0', 'FF99CC', 'FFCC99', | ||
321 | + 'FFFF99', 'CCFFFF', '99CCFF', 'FFFFFF' | ||
322 | + ], | ||
323 | + | ||
324 | + // If we want to simply add more colors to the default set, use addColors. | ||
325 | + addColors : [] | ||
326 | + }; | ||
327 | + | ||
328 | +})(jQuery); |
plugins/mezuro/public/javascripts/colorPicker/jquery.colorPicker.min.js
0 → 100644
@@ -0,0 +1,26 @@ | @@ -0,0 +1,26 @@ | ||
1 | +/** | ||
2 | + * Really Simple Color Picker in jQuery | ||
3 | + * | ||
4 | + * Licensed under the MIT (MIT-LICENSE.txt) licenses. | ||
5 | + * | ||
6 | + * Copyright (c) 2008-2012 | ||
7 | + * Lakshan Perera (www.laktek.com) & Daniel Lacy (daniellacy.com) | ||
8 | + * | ||
9 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
10 | + * of this software and associated documentation files (the "Software"), to | ||
11 | + * deal in the Software without restriction, including without limitation the | ||
12 | + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
13 | + * sell copies of the Software, and to permit persons to whom the Software is | ||
14 | + * furnished to do so, subject to the following conditions: | ||
15 | + * | ||
16 | + * The above copyright notice and this permission notice shall be included in | ||
17 | + * all copies or substantial portions of the Software. | ||
18 | + * | ||
19 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
20 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
21 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
22 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
23 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
24 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
25 | + * IN THE SOFTWARE. | ||
26 | + */(function(a){var b,c,d=0,e={control:a('<div class="colorPicker-picker"> </div>'),palette:a('<div id="colorPicker_palette" class="colorPicker-palette" />'),swatch:a('<div class="colorPicker-swatch"> </div>'),hexLabel:a('<label for="colorPicker_hex">Hex</label>'),hexField:a('<input type="text" id="colorPicker_hex" />')},f="transparent",g;a.fn.colorPicker=function(b){return this.each(function(){var c=a(this),g=a.extend({},a.fn.colorPicker.defaults,b),h=a.fn.colorPicker.toHex(c.val().length>0?c.val():g.pickerDefault),i=e.control.clone(),j=e.palette.clone().attr("id","colorPicker_palette-"+d),k=e.hexLabel.clone(),l=e.hexField.clone(),m=j[0].id,n;a.each(g.colors,function(b){n=e.swatch.clone(),g.colors[b]===f?(n.addClass(f).text("X"),a.fn.colorPicker.bindPalette(l,n,f)):(n.css("background-color","#"+this),a.fn.colorPicker.bindPalette(l,n)),n.appendTo(j)}),k.attr("for","colorPicker_hex-"+d),l.attr({id:"colorPicker_hex-"+d,value:h}),l.bind("keydown",function(b){if(b.keyCode===13){var d=a.fn.colorPicker.toHex(a(this).val());a.fn.colorPicker.changeColor(d?d:c.val())}b.keyCode===27&&a.fn.colorPicker.hidePalette()}),l.bind("keyup",function(b){var d=a.fn.colorPicker.toHex(a(b.target).val());a.fn.colorPicker.previewColor(d?d:c.val())}),a('<div class="colorPicker_hexWrap" />').append(k).appendTo(j),j.find(".colorPicker_hexWrap").append(l),a("body").append(j),j.hide(),i.css("background-color",h),i.bind("click",function(){a.fn.colorPicker.togglePalette(a("#"+m),a(this))}),b&&b.onColorChange?i.data("onColorChange",b.onColorChange):i.data("onColorChange",function(){}),c.after(i),c.bind("change",function(){c.next(".colorPicker-picker").css("background-color",a.fn.colorPicker.toHex(a(this).val()))}),c.val(h).hide(),d++})},a.extend(!0,a.fn.colorPicker,{toHex:function(a){if(a.match(/[0-9A-F]{6}|[0-9A-F]{3}$/i))return a.charAt(0)==="#"?a:"#"+a;if(!a.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/))return!1;var b=[parseInt(RegExp.$1,10),parseInt(RegExp.$2,10),parseInt(RegExp.$3,10)],c=function(a){if(a.length<2)for(var b=0,c=2-a.length;b<c;b++)a="0"+a;return a};if(b.length===3){var d=c(b[0].toString(16)),e=c(b[1].toString(16)),f=c(b[2].toString(16));return"#"+d+e+f}},checkMouse:function(d,e){var f=c,g=a(d.target).parents("#"+f.attr("id")).length;if(d.target===a(f)[0]||d.target===b[0]||g>0)return;a.fn.colorPicker.hidePalette()},hidePalette:function(){a(document).unbind("mousedown",a.fn.colorPicker.checkMouse),a(".colorPicker-palette").hide()},showPalette:function(c){var d=b.prev("input").val();c.css({top:b.offset().top+b.outerHeight(),left:b.offset().left}),a("#color_value").val(d),c.show(),a(document).bind("mousedown",a.fn.colorPicker.checkMouse)},togglePalette:function(d,e){e&&(b=e),c=d,c.is(":visible")?a.fn.colorPicker.hidePalette():a.fn.colorPicker.showPalette(d)},changeColor:function(c){b.css("background-color",c),b.prev("input").val(c).change(),a.fn.colorPicker.hidePalette(),b.data("onColorChange").call(b,a(b).prev("input").attr("id"),c)},previewColor:function(a){b.css("background-color",a)},bindPalette:function(c,d,e){e=e?e:a.fn.colorPicker.toHex(d.css("background-color")),d.bind({click:function(b){g=e,a.fn.colorPicker.changeColor(e)},mouseover:function(b){g=c.val(),a(this).css("border-color","#598FEF"),c.val(e),a.fn.colorPicker.previewColor(e)},mouseout:function(d){a(this).css("border-color","#000"),c.val(b.css("background-color")),c.val(g),a.fn.colorPicker.previewColor(g)}})}}),a.fn.colorPicker.defaults={pickerDefault:"FFFFFF",colors:["000000","993300","333300","000080","333399","333333","800000","FF6600","808000","008000","008080","0000FF","666699","808080","FF0000","FF9900","99CC00","339966","33CCCC","3366FF","800080","999999","FF00FF","FFCC00","FFFF00","00FF00","00FFFF","00CCFF","993366","C0C0C0","FF99CC","FFCC99","FFFF99","CCFFFF","99CCFF","FFFFFF"],addColors:[]}})(jQuery) | ||
0 | \ No newline at end of file | 27 | \ No newline at end of file |
plugins/mezuro/public/javascripts/project_content.js
@@ -2,7 +2,6 @@ var processingTree = false; | @@ -2,7 +2,6 @@ var processingTree = false; | ||
2 | var metricName; | 2 | var metricName; |
3 | jQuery(function (){ | 3 | jQuery(function (){ |
4 | jQuery('.source-tree-link').live("click", reloadModule); | 4 | jQuery('.source-tree-link').live("click", reloadModule); |
5 | - jQuery('[data-show]').live("click", toggle_mezuro); | ||
6 | jQuery('[show-metric-history]').live("click", display_metric_history); | 5 | jQuery('[show-metric-history]').live("click", display_metric_history); |
7 | jQuery('[show-grade-history]').live("click", display_grade_history); | 6 | jQuery('[show-grade-history]').live("click", display_grade_history); |
8 | jQuery('#project_date_submit').live("click", reloadProjectWithDate); | 7 | jQuery('#project_date_submit').live("click", reloadProjectWithDate); |
@@ -16,7 +15,8 @@ function showProjectContent() { | @@ -16,7 +15,8 @@ function showProjectContent() { | ||
16 | 15 | ||
17 | function display_metric_history() { | 16 | function display_metric_history() { |
18 | var module_name = jQuery(this).attr('data-module-name'); | 17 | var module_name = jQuery(this).attr('data-module-name'); |
19 | - var metric_name = jQuery(this).attr('data-metric-name'); | 18 | + var metric_name = jQuery(this).attr('show-metric-history'); |
19 | + toggle_mezuro("." + metric_name); | ||
20 | metricName = metric_name; | 20 | metricName = metric_name; |
21 | callAction('module_metrics_history', {module_name: module_name, metric_name: metric_name}, show_metrics); | 21 | callAction('module_metrics_history', {module_name: module_name, metric_name: metric_name}, show_metrics); |
22 | return false; | 22 | return false; |
@@ -24,6 +24,7 @@ function display_metric_history() { | @@ -24,6 +24,7 @@ function display_metric_history() { | ||
24 | 24 | ||
25 | function display_grade_history() { | 25 | function display_grade_history() { |
26 | var module_name = jQuery(this).attr('data-module-name'); | 26 | var module_name = jQuery(this).attr('data-module-name'); |
27 | + toggle_mezuro("#historical-grade"); | ||
27 | callAction('module_grade_history', {module_name: module_name}, show_grades); | 28 | callAction('module_grade_history', {module_name: module_name}, show_grades); |
28 | return false; | 29 | return false; |
29 | } | 30 | } |
@@ -36,8 +37,7 @@ function show_grades(content) { | @@ -36,8 +37,7 @@ function show_grades(content) { | ||
36 | jQuery('#historical-grade').html(content); | 37 | jQuery('#historical-grade').html(content); |
37 | } | 38 | } |
38 | 39 | ||
39 | -function toggle_mezuro(){ | ||
40 | - var element = jQuery(this).attr('data-show'); | 40 | +function toggle_mezuro(element){ |
41 | jQuery(element).toggle(); | 41 | jQuery(element).toggle(); |
42 | return false; | 42 | return false; |
43 | } | 43 | } |
@@ -51,23 +51,8 @@ function reloadModule(){ | @@ -51,23 +51,8 @@ function reloadModule(){ | ||
51 | return false; | 51 | return false; |
52 | } | 52 | } |
53 | 53 | ||
54 | -function reloadProjectWithDate(){ | ||
55 | - var day = jQuery("#project_date_day").val(); | ||
56 | - var month = jQuery("#project_date_month").val(); | ||
57 | - var year = jQuery("#project_date_year").val(); | ||
58 | - | ||
59 | - if(day.length == 1) | ||
60 | - day = "0" + day; | ||
61 | - if(month.length == 1) | ||
62 | - month = "0" + month; | ||
63 | - | ||
64 | - var date = new Date(year + "-" + month + "-" + day + "T00:00:00+00:00"); | ||
65 | - | ||
66 | - if(isNaN(date)){ | ||
67 | - alert("Invalid date! " + date); | ||
68 | - return false; | ||
69 | - } | ||
70 | - reloadProject(date); | 54 | +function reloadProjectWithDate(date){ |
55 | + reloadProject(date + "T00:00:00+00:00"); | ||
71 | return false; | 56 | return false; |
72 | } | 57 | } |
73 | 58 |
plugins/mezuro/public/javascripts/validations.js
@@ -3,7 +3,9 @@ jQuery(function (){ | @@ -3,7 +3,9 @@ jQuery(function (){ | ||
3 | jQuery('#metric_configuration_submit').live("click", validate_metric_configuration); | 3 | jQuery('#metric_configuration_submit').live("click", validate_metric_configuration); |
4 | }); | 4 | }); |
5 | 5 | ||
6 | - | 6 | +function validate_code(code){ |
7 | + return true; | ||
8 | +} | ||
7 | 9 | ||
8 | function validate_metric_configuration(){ | 10 | function validate_metric_configuration(){ |
9 | var code = jQuery('#metric_configuration_code').val(); | 11 | var code = jQuery('#metric_configuration_code').val(); |
@@ -37,14 +39,6 @@ function IsNotInfinite(value){ | @@ -37,14 +39,6 @@ function IsNotInfinite(value){ | ||
37 | return true; | 39 | return true; |
38 | } | 40 | } |
39 | 41 | ||
40 | -function IsNotHexadecimal(value){ | ||
41 | - if(value.match(/^[0-9a-fA-F]{1,8}$/)) | ||
42 | - { | ||
43 | - return false; | ||
44 | - } | ||
45 | - return true; | ||
46 | -} | ||
47 | - | ||
48 | function validate_new_range_configuration(event){ | 42 | function validate_new_range_configuration(event){ |
49 | var label = jQuery("#range_label").val(); | 43 | var label = jQuery("#range_label").val(); |
50 | var beginning = jQuery("#range_beginning").val(); | 44 | var beginning = jQuery("#range_beginning").val(); |
@@ -54,24 +48,20 @@ function validate_new_range_configuration(event){ | @@ -54,24 +48,20 @@ function validate_new_range_configuration(event){ | ||
54 | 48 | ||
55 | if (is_null(label) || is_null(beginning) || is_null(end) || is_null(color) || is_null(grade)) | 49 | if (is_null(label) || is_null(beginning) || is_null(end) || is_null(color) || is_null(grade)) |
56 | { | 50 | { |
57 | - alert("Please fill all fields marked with (*)"); | 51 | + alert("Please fill all fields marked with (*)."); |
58 | return false; | 52 | return false; |
59 | } | 53 | } |
60 | if ( (IsNotNumeric(beginning) && IsNotInfinite(beginning)) || (IsNotNumeric(end) && IsNotInfinite(end)) || IsNotNumeric(grade)) | 54 | if ( (IsNotNumeric(beginning) && IsNotInfinite(beginning)) || (IsNotNumeric(end) && IsNotInfinite(end)) || IsNotNumeric(grade)) |
61 | { | 55 | { |
62 | - alert("Beginning, End and Grade must be numeric values"); | 56 | + alert("Beginning, End and Grade must be numeric values."); |
63 | return false; | 57 | return false; |
64 | } | 58 | } |
65 | if (parseInt(beginning) > parseInt(end)) | 59 | if (parseInt(beginning) > parseInt(end)) |
66 | { | 60 | { |
67 | if(IsNotInfinite(beginning) && IsNotInfinite(end)){ | 61 | if(IsNotInfinite(beginning) && IsNotInfinite(end)){ |
68 | - alert("End must be greater than Beginning"); | 62 | + alert("End must be greater than Beginning."); |
69 | return false; | 63 | return false; |
70 | } | 64 | } |
71 | } | 65 | } |
72 | - if (IsNotHexadecimal(color)){ | ||
73 | - alert("Color must be an hexadecimal value"); | ||
74 | - return false; | ||
75 | - } | ||
76 | return true; | 66 | return true; |
77 | } | 67 | } |
plugins/mezuro/public/style.css
plugins/mezuro/test/fixtures/project_fixtures.rb
@@ -28,7 +28,7 @@ class ProjectFixtures | @@ -28,7 +28,7 @@ class ProjectFixtures | ||
28 | def self.project_content | 28 | def self.project_content |
29 | content = MezuroPlugin::ProjectContent.new | 29 | content = MezuroPlugin::ProjectContent.new |
30 | content.name = 'Qt-Calculator' | 30 | content.name = 'Qt-Calculator' |
31 | - content.license = 'GPL' | 31 | + content.project_license = 'GPL' |
32 | content.description = 'Calculator for Qt' | 32 | content.description = 'Calculator for Qt' |
33 | content.repository_type = RepositoryFixtures.repository_hash[:type] | 33 | content.repository_type = RepositoryFixtures.repository_hash[:type] |
34 | content.repository_url = RepositoryFixtures.repository_hash[:address] | 34 | content.repository_url = RepositoryFixtures.repository_hash[:address] |
plugins/mezuro/test/functional/mezuro_plugin_myprofile_controller_test.rb
@@ -27,7 +27,7 @@ class MezuroPluginMyprofileControllerTest < ActionController::TestCase | @@ -27,7 +27,7 @@ class MezuroPluginMyprofileControllerTest < ActionController::TestCase | ||
27 | 27 | ||
28 | Kalibro::Configuration.expects(:all_names).returns([]) | 28 | Kalibro::Configuration.expects(:all_names).returns([]) |
29 | @content = MezuroPlugin::ConfigurationContent.new(:profile => @profile, :name => @configuration.name) | 29 | @content = MezuroPlugin::ConfigurationContent.new(:profile => @profile, :name => @configuration.name) |
30 | - @content.expects(:send_configuration_to_service).returns(nil) | 30 | + @content.expects(:send_kalibro_configuration_to_service).returns(nil) |
31 | @content.stubs(:solr_save) | 31 | @content.stubs(:solr_save) |
32 | @content.save | 32 | @content.save |
33 | 33 | ||
@@ -39,7 +39,7 @@ class MezuroPluginMyprofileControllerTest < ActionController::TestCase | @@ -39,7 +39,7 @@ class MezuroPluginMyprofileControllerTest < ActionController::TestCase | ||
39 | @range = RangeFixtures.range_excellent | 39 | @range = RangeFixtures.range_excellent |
40 | @range_hash = RangeFixtures.range_excellent_hash | 40 | @range_hash = RangeFixtures.range_excellent_hash |
41 | end | 41 | end |
42 | - | 42 | + |
43 | should 'test choose base tool' do | 43 | should 'test choose base tool' do |
44 | Kalibro::BaseTool.expects(:request).with("BaseTool", :get_base_tool_names).returns({:base_tool_name => @base_tool.name}) | 44 | Kalibro::BaseTool.expects(:request).with("BaseTool", :get_base_tool_names).returns({:base_tool_name => @base_tool.name}) |
45 | get :choose_base_tool, :profile => @profile.identifier, :id => @content.id | 45 | get :choose_base_tool, :profile => @profile.identifier, :id => @content.id |
plugins/mezuro/test/functional/mezuro_plugin_profile_controller_test.rb
@@ -25,14 +25,14 @@ class MezuroPluginProfileControllerTest < ActionController::TestCase | @@ -25,14 +25,14 @@ class MezuroPluginProfileControllerTest < ActionController::TestCase | ||
25 | @content.save | 25 | @content.save |
26 | end | 26 | end |
27 | 27 | ||
28 | - should 'test project state without error' do | 28 | + should 'test project state without kalibro_error' do |
29 | Kalibro::Project.expects(:request).with("Project", :get_project, :project_name => @project.name).returns({:project => @project.to_hash}) | 29 | Kalibro::Project.expects(:request).with("Project", :get_project, :project_name => @project.name).returns({:project => @project.to_hash}) |
30 | get :project_state, :profile => @profile.identifier, :id => @content.id | 30 | get :project_state, :profile => @profile.identifier, :id => @content.id |
31 | assert_response 200 | 31 | assert_response 200 |
32 | assert_equal @content, assigns(:content) | 32 | assert_equal @content, assigns(:content) |
33 | end | 33 | end |
34 | 34 | ||
35 | - should 'test project state with error' do | 35 | + should 'test project state with kalibro_error' do |
36 | Kalibro::Project.expects(:request).with("Project", :get_project, :project_name => @project.name).returns({:project => @project.to_hash.merge({:error => ErrorFixtures.error_hash})}) | 36 | Kalibro::Project.expects(:request).with("Project", :get_project, :project_name => @project.name).returns({:project => @project.to_hash.merge({:error => ErrorFixtures.error_hash})}) |
37 | get :project_state, :profile => @profile.identifier, :id => @content.id | 37 | get :project_state, :profile => @profile.identifier, :id => @content.id |
38 | assert_response 200 | 38 | assert_response 200 |
@@ -126,7 +126,7 @@ class MezuroPluginProfileControllerTest < ActionController::TestCase | @@ -126,7 +126,7 @@ class MezuroPluginProfileControllerTest < ActionController::TestCase | ||
126 | get :module_metrics_history, :profile => @profile.identifier, :id => @content.id, :module_name => @project.name, | 126 | get :module_metrics_history, :profile => @profile.identifier, :id => @content.id, :module_name => @project.name, |
127 | :metric_name => @module_result.metric_result.first.metric.name.delete("() ") | 127 | :metric_name => @module_result.metric_result.first.metric.name.delete("() ") |
128 | assert_equal @content, assigns(:content) | 128 | assert_equal @content, assigns(:content) |
129 | - assert_equal [@module_result.metric_result[0].value], assigns(:score_history) | 129 | + assert_equal [[@module_result.metric_result[0].value, @module_result.date.to_s[0..9]]], assigns(:score_history) |
130 | assert_response 200 | 130 | assert_response 200 |
131 | end | 131 | end |
132 | 132 | ||
@@ -134,7 +134,7 @@ class MezuroPluginProfileControllerTest < ActionController::TestCase | @@ -134,7 +134,7 @@ class MezuroPluginProfileControllerTest < ActionController::TestCase | ||
134 | Kalibro::ModuleResult.expects(:request).with("ModuleResult", :get_result_history, {:project_name => @project.name, :module_name => @project.name}).returns({:module_result => @module_result}) | 134 | Kalibro::ModuleResult.expects(:request).with("ModuleResult", :get_result_history, {:project_name => @project.name, :module_name => @project.name}).returns({:module_result => @module_result}) |
135 | get :module_grade_history, :profile => @profile.identifier, :id => @content.id, :module_name => @project.name | 135 | get :module_grade_history, :profile => @profile.identifier, :id => @content.id, :module_name => @project.name |
136 | assert_equal @content, assigns(:content) | 136 | assert_equal @content, assigns(:content) |
137 | - assert_equal [@module_result.grade], assigns(:score_history) | 137 | + assert_equal [[@module_result.grade, @module_result.date.to_s[0..9]]], assigns(:score_history) |
138 | assert_response 200 | 138 | assert_response 200 |
139 | end | 139 | end |
140 | 140 |
plugins/mezuro/test/unit/kalibro/configuration_test.rb
@@ -44,7 +44,9 @@ class ConfigurationTest < ActiveSupport::TestCase | @@ -44,7 +44,9 @@ class ConfigurationTest < ActiveSupport::TestCase | ||
44 | should 'return nil when configuration doesnt exist' do | 44 | should 'return nil when configuration doesnt exist' do |
45 | request_body = {:configuration_name => @configuration.name} | 45 | request_body = {:configuration_name => @configuration.name} |
46 | Kalibro::Configuration.expects(:request).with("Configuration", :get_configuration, request_body).raises(Exception.new) | 46 | Kalibro::Configuration.expects(:request).with("Configuration", :get_configuration, request_body).raises(Exception.new) |
47 | - assert_nil Kalibro::Configuration.find_by_name(@configuration.name) | 47 | + assert_raise Exception do |
48 | + Kalibro::Configuration.find_by_name(@configuration.name) | ||
49 | + end | ||
48 | end | 50 | end |
49 | 51 | ||
50 | should 'destroy configuration by name' do | 52 | should 'destroy configuration by name' do |
plugins/mezuro/test/unit/kalibro/project_result_test.rb
@@ -69,26 +69,20 @@ class ProjectResultTest < ActiveSupport::TestCase | @@ -69,26 +69,20 @@ class ProjectResultTest < ActiveSupport::TestCase | ||
69 | assert_equal '00:00:01', @project_result.formatted_analysis_time | 69 | assert_equal '00:00:01', @project_result.formatted_analysis_time |
70 | end | 70 | end |
71 | 71 | ||
72 | - should 'retrieve module node' do | ||
73 | - node = @project_result.get_node("main") | ||
74 | - assert_equal @hash[:source_tree][:child][2], node.to_hash | ||
75 | - end | ||
76 | - | ||
77 | should 'retrive complex module' do | 72 | should 'retrive complex module' do |
78 | - node = @project_result.get_node("org.Window") | ||
79 | - assert_equal @hash[:source_tree][:child][0][:child].first, node.to_hash | 73 | + assert_equal @hash[:source_tree][:child][0][:child].first, @project_result.node("org.Window").to_hash |
80 | end | 74 | end |
81 | 75 | ||
82 | should 'return source tree node when nil is given' do | 76 | should 'return source tree node when nil is given' do |
83 | - assert_equal @hash[:source_tree], @project_result.node_of(nil).to_hash | 77 | + assert_equal @hash[:source_tree], @project_result.node(nil).to_hash |
84 | end | 78 | end |
85 | 79 | ||
86 | should 'return source tree node when project name is given' do | 80 | should 'return source tree node when project name is given' do |
87 | - assert_equal @hash[:source_tree], @project_result.node_of(@project_result.project.name).to_hash | 81 | + assert_equal @hash[:source_tree], @project_result.node(@project_result.project.name).to_hash |
88 | end | 82 | end |
89 | 83 | ||
90 | should 'return correct node when module name is given' do | 84 | should 'return correct node when module name is given' do |
91 | - assert_equal @hash[:source_tree][:child][2], @project_result.node_of("main").to_hash | 85 | + assert_equal @hash[:source_tree][:child][2], @project_result.node("main").to_hash |
92 | end | 86 | end |
93 | 87 | ||
94 | end | 88 | end |
plugins/mezuro/test/unit/kalibro/range_test.rb
@@ -17,4 +17,16 @@ class RangeTest < ActiveSupport::TestCase | @@ -17,4 +17,16 @@ class RangeTest < ActiveSupport::TestCase | ||
17 | assert_equal @hash, @range.to_hash | 17 | assert_equal @hash, @range.to_hash |
18 | end | 18 | end |
19 | 19 | ||
20 | + should 'create a default color for new range' do | ||
21 | + assert_equal "#e4ca2d", Kalibro::Range.new.mezuro_color | ||
22 | + end | ||
23 | + | ||
24 | + should "convert color from 'ff' to '#'" do | ||
25 | + assert_equal "#ff0000", @range.mezuro_color | ||
26 | + end | ||
27 | + | ||
28 | + should "convert color from '#' to 'ff' when creating a new range" do | ||
29 | + assert_equal "ffff0000", Kalibro::Range.new({:color => '#ff0000'}).color | ||
30 | + end | ||
31 | + | ||
20 | end | 32 | end |
plugins/mezuro/test/unit/mezuro_plugin/configuration_content_test.rb
@@ -6,9 +6,7 @@ class ConfigurationContentTest < ActiveSupport::TestCase | @@ -6,9 +6,7 @@ class ConfigurationContentTest < ActiveSupport::TestCase | ||
6 | 6 | ||
7 | def setup | 7 | def setup |
8 | @configuration = ConfigurationFixtures.configuration | 8 | @configuration = ConfigurationFixtures.configuration |
9 | - @content = MezuroPlugin::ConfigurationContent.new | ||
10 | - @content.name = @configuration.name | ||
11 | - @content.description = @configuration.description | 9 | + @content = ConfigurationFixtures.configuration_content("None") |
12 | end | 10 | end |
13 | 11 | ||
14 | should 'be an article' do | 12 | should 'be an article' do |
@@ -35,19 +33,19 @@ class ConfigurationContentTest < ActiveSupport::TestCase | @@ -35,19 +33,19 @@ class ConfigurationContentTest < ActiveSupport::TestCase | ||
35 | 33 | ||
36 | should 'get configuration from service' do | 34 | should 'get configuration from service' do |
37 | Kalibro::Configuration.expects(:find_by_name).with(@content.name).returns(@configuration) | 35 | Kalibro::Configuration.expects(:find_by_name).with(@content.name).returns(@configuration) |
38 | - assert_equal @configuration, @content.configuration | 36 | + assert_equal @configuration, @content.kalibro_configuration |
39 | end | 37 | end |
40 | 38 | ||
41 | should 'send configuration to service after saving' do | 39 | should 'send configuration to service after saving' do |
42 | - @content.expects :send_configuration_to_service | 40 | + @content.expects :send_kalibro_configuration_to_service |
43 | @content.stubs(:solr_save) | 41 | @content.stubs(:solr_save) |
44 | @content.run_callbacks :after_save | 42 | @content.run_callbacks :after_save |
45 | end | 43 | end |
46 | 44 | ||
47 | should 'create new configuration' do | 45 | should 'create new configuration' do |
48 | Kalibro::Configuration.expects(:create).with(:name => @content.name, :description => @content.description) | 46 | Kalibro::Configuration.expects(:create).with(:name => @content.name, :description => @content.description) |
49 | - Kalibro::Configuration.expects(:find_by_name).with(@content.name).returns(nil) | ||
50 | - @content.send :send_configuration_to_service | 47 | + Kalibro::Configuration.expects(:find_by_name).with(@content.name) |
48 | + @content.send :send_kalibro_configuration_to_service | ||
51 | end | 49 | end |
52 | 50 | ||
53 | should 'clone configuration' do | 51 | should 'clone configuration' do |
@@ -55,25 +53,25 @@ class ConfigurationContentTest < ActiveSupport::TestCase | @@ -55,25 +53,25 @@ class ConfigurationContentTest < ActiveSupport::TestCase | ||
55 | Kalibro::Configuration.expects(:create).with(:name => @content.name, :description => @content.description, :metric_configuration => @configuration.metric_configurations_hash) | 53 | Kalibro::Configuration.expects(:create).with(:name => @content.name, :description => @content.description, :metric_configuration => @configuration.metric_configurations_hash) |
56 | Kalibro::Configuration.expects(:find_by_name).with(@content.name).returns(nil) | 54 | Kalibro::Configuration.expects(:find_by_name).with(@content.name).returns(nil) |
57 | Kalibro::Configuration.expects(:find_by_name).with('clone name').returns(@configuration) | 55 | Kalibro::Configuration.expects(:find_by_name).with('clone name').returns(@configuration) |
58 | - @content.send :send_configuration_to_service | 56 | + @content.send :send_kalibro_configuration_to_service |
59 | end | 57 | end |
60 | 58 | ||
61 | should 'edit configuration' do | 59 | should 'edit configuration' do |
62 | Kalibro::Configuration.expects(:find_by_name).with(@content.name).returns(@configuration) | 60 | Kalibro::Configuration.expects(:find_by_name).with(@content.name).returns(@configuration) |
63 | @configuration.expects(:update_attributes).with(:description => @content.description) | 61 | @configuration.expects(:update_attributes).with(:description => @content.description) |
64 | - @content.send :send_configuration_to_service | 62 | + @content.send :send_kalibro_configuration_to_service |
65 | end | 63 | end |
66 | 64 | ||
67 | should 'send correct configuration to service but comunication fails' do | 65 | should 'send correct configuration to service but comunication fails' do |
68 | Kalibro::Configuration.expects(:find_by_name).with(@content.name).returns(@configuration) | 66 | Kalibro::Configuration.expects(:find_by_name).with(@content.name).returns(@configuration) |
69 | @configuration.expects(:save).returns(false) | 67 | @configuration.expects(:save).returns(false) |
70 | - @content.send :send_configuration_to_service | 68 | + @content.send :send_kalibro_configuration_to_service |
71 | end | 69 | end |
72 | 70 | ||
73 | should 'remove configuration from service' do | 71 | should 'remove configuration from service' do |
74 | Kalibro::Configuration.expects(:find_by_name).with(@content.name).returns(@configuration) | 72 | Kalibro::Configuration.expects(:find_by_name).with(@content.name).returns(@configuration) |
75 | @configuration.expects(:destroy) | 73 | @configuration.expects(:destroy) |
76 | - @content.send :remove_configuration_from_service | 74 | + @content.send :remove_kalibro_configuration_from_service |
77 | end | 75 | end |
78 | 76 | ||
79 | end | 77 | end |
plugins/mezuro/views/cms/mezuro_plugin/_configuration_content.html.erb
1 | <h1> <%= _(MezuroPlugin::ConfigurationContent.short_description) %> </h1> | 1 | <h1> <%= _(MezuroPlugin::ConfigurationContent.short_description) %> </h1> |
2 | 2 | ||
3 | <% | 3 | <% |
4 | - begin | ||
5 | - configuration = @article.title.nil? ? nil : @article.configuration | ||
6 | - rescue | ||
7 | - configuration = nil | ||
8 | - end | 4 | + kalibro_configuration = @article.title.nil? ? nil : @article.kalibro_configuration |
5 | + kalibro_configuration_names = @article.kalibro_configuration_names | ||
9 | %> | 6 | %> |
10 | 7 | ||
11 | <%= error_messages_for 'kalibro_configuration' %> | 8 | <%= error_messages_for 'kalibro_configuration' %> |
@@ -13,20 +10,19 @@ | @@ -13,20 +10,19 @@ | ||
13 | <%= hidden_field_tag 'kalibro_configuration[profile_id]', profile.id %> | 10 | <%= hidden_field_tag 'kalibro_configuration[profile_id]', profile.id %> |
14 | <%= hidden_field_tag 'id', @article.id %> | 11 | <%= hidden_field_tag 'id', @article.id %> |
15 | 12 | ||
16 | -<% configuration_names = @article.configuration_names %> | ||
17 | - | ||
18 | -<% selected = (configuration.nil? ? "None" : @article.configuration_to_clone_name) %> | 13 | + |
14 | +<% selected = (kalibro_configuration.nil? ? "None" : @article.configuration_to_clone_name) %> | ||
19 | 15 | ||
20 | <%= required_fields_message %> | 16 | <%= required_fields_message %> |
21 | 17 | ||
22 | <%= required labelled_form_field _('Clone Configuration'), | 18 | <%= required labelled_form_field _('Clone Configuration'), |
23 | -if !configuration.nil? && !@article.id.nil? | ||
24 | - f.select(:configuration_to_clone_name, configuration_names, {:selected => selected}, :disabled => 'true') | 19 | +if !kalibro_configuration.nil? && !@article.id.nil? |
20 | + f.select(:configuration_to_clone_name, kalibro_configuration_names, {:selected => selected}, :disabled => 'true') | ||
25 | else | 21 | else |
26 | - f.select(:configuration_to_clone_name, configuration_names, {:selected => selected}) | 22 | + f.select(:configuration_to_clone_name, kalibro_configuration_names, {:selected => selected}) |
27 | end %> | 23 | end %> |
28 | <br/> | 24 | <br/> |
29 | 25 | ||
30 | -<%= required f.text_field(:name, :disabled => !(configuration.nil? || @article.id.nil?)) %> | 26 | +<%= required f.text_field(:name, :disabled => !(kalibro_configuration.nil? || @article.id.nil?)) %> |
31 | 27 | ||
32 | <%= f.text_field :description %><br/> | 28 | <%= f.text_field :description %><br/> |
plugins/mezuro/views/cms/mezuro_plugin/_project_content.html.erb
1 | <h1> <%= _(MezuroPlugin::ProjectContent.short_description) %> </h1> | 1 | <h1> <%= _(MezuroPlugin::ProjectContent.short_description) %> </h1> |
2 | 2 | ||
3 | <% | 3 | <% |
4 | + @project = @article.title.nil? ? nil : @article.project | ||
4 | begin | 5 | begin |
5 | - @project = @article.title.nil? ? nil : Kalibro::Project.find_by_name(@article.title) | ||
6 | - rescue | ||
7 | - @project = nil | 6 | + @repository_types = Kalibro::Repository.repository_types.sort |
7 | + @configuration_names = Kalibro::Configuration.all_names.sort | ||
8 | + rescue Exception => exception | ||
9 | + @article.errors.add_to_base(exception.message) | ||
10 | + @repository_types = [] | ||
11 | + @configuration_names = [] | ||
8 | end | 12 | end |
9 | %> | 13 | %> |
10 | 14 | ||
@@ -20,18 +24,18 @@ | @@ -20,18 +24,18 @@ | ||
20 | <%= required f.text_field(:name) %> | 24 | <%= required f.text_field(:name) %> |
21 | <% end %> | 25 | <% end %> |
22 | 26 | ||
23 | -<%= f.text_field :license %><br/> | 27 | +<% selected = (@project.nil? ? "" : @project.license) %> |
28 | +<%= required labelled_form_field _('License'), | ||
29 | + f.select(:project_license, MezuroPlugin::Helpers::ContentViewerHelper.create_license_options ,{:selected => selected}) %><br/> | ||
24 | 30 | ||
25 | <%= f.text_field :description %><br/> | 31 | <%= f.text_field :description %><br/> |
26 | 32 | ||
27 | -<% @repository_types = Kalibro::Repository.repository_types.sort %> | ||
28 | <% @selected = (@project.nil? ? @repository_types : @project.repository.type) %> | 33 | <% @selected = (@project.nil? ? @repository_types : @project.repository.type) %> |
29 | <%= required labelled_form_field _('Repository type'), | 34 | <%= required labelled_form_field _('Repository type'), |
30 | f.select(:repository_type, @repository_types, {:selected => @selected}) %><br/> | 35 | f.select(:repository_type, @repository_types, {:selected => @selected}) %><br/> |
31 | 36 | ||
32 | <%= required f.text_field(:repository_url) %><br/> | 37 | <%= required f.text_field(:repository_url) %><br/> |
33 | 38 | ||
34 | -<% @configuration_names = Kalibro::Configuration.all_names.sort %> | ||
35 | <% @selected = (@project.nil? ? @configuration_names[0] : @project.configuration_name) %> | 39 | <% @selected = (@project.nil? ? @configuration_names[0] : @project.configuration_name) %> |
36 | 40 | ||
37 | <% if !@project.nil? && !@article.id.nil? %> | 41 | <% if !@project.nil? && !@article.id.nil? %> |
plugins/mezuro/views/content_viewer/_module_result.rhtml
1 | -<% unless @content.errors[:base].nil? %> | ||
2 | - <%= @content.errors[:base] %> | ||
3 | -<% else %> | ||
4 | - <% the_module = @module_result.module %> | ||
5 | - <% module_label = "#{the_module.name} (#{the_module.granularity})" %> | 1 | +<h5><%= _('Metric results for: ') + @module_label %> </h5> |
6 | 2 | ||
7 | - <h5><%= _('Metric results for: ') + module_label %> </h5> | ||
8 | - | ||
9 | - <hr/> | ||
10 | - <div class="zoomable-image"> | ||
11 | - <table> | ||
12 | - <thead> | ||
13 | - <tr> | ||
14 | - <th>Metric</th> | ||
15 | - <th>Value</th> | ||
16 | - <th>Weight</th> | ||
17 | - <th>Threshold</th> | ||
18 | - </tr> | ||
19 | - </thead> | ||
20 | - <tbody> | ||
21 | - <% @module_result.metric_results.each do |metric_result| %> | ||
22 | - <% range = metric_result.range %> | ||
23 | - <% if !range.nil? %> | ||
24 | - <tr> | ||
25 | - <td><a href="#" data-show=".<%= MezuroPlugin::Helpers::ContentViewerHelper.format_name(metric_result) %>"><%= metric_result.metric.name %></a></td> | ||
26 | - <td><%= MezuroPlugin::Helpers::ContentViewerHelper.format_grade(metric_result.value) %></td> | ||
27 | - <td><%= metric_result.weight %></td> | ||
28 | - <td style="background-color: #<%= range.color[2..-1] %>"><%= range.label %></td> | ||
29 | - </tr> | ||
30 | - <tr class="<%= MezuroPlugin::Helpers::ContentViewerHelper.format_name(metric_result) %>" style="display: none;"> | ||
31 | - <td colspan="3"> | ||
32 | - <div id='historical-<%= MezuroPlugin::Helpers::ContentViewerHelper.format_name(metric_result) %>'> | ||
33 | - <a href="#" show-metric-history="<%= MezuroPlugin::Helpers::ContentViewerHelper.format_name(metric_result) %>" data-module-name="<%= the_module.name %>" data-metric-name="<%= MezuroPlugin::Helpers::ContentViewerHelper.format_name(metric_result) %>"> <p style="text-indent: 3em;"> Get Historical Values </p> </a> | ||
34 | - </div> | ||
35 | - </td> | ||
36 | - <td align="right"> | ||
37 | - <%= range.comments.nil? ? '' : range.comments %> | ||
38 | - </td> | ||
39 | - </tr> | ||
40 | - <% end %> | 3 | +<hr/> |
4 | +<div class="zoomable-image"> | ||
5 | +<table> | ||
6 | + <thead> | ||
7 | + <tr> | ||
8 | + <th>Metric</th> | ||
9 | + <th>Value</th> | ||
10 | + <th>Weight</th> | ||
11 | + <th>Threshold</th> | ||
12 | + </tr> | ||
13 | + </thead> | ||
14 | + <tbody> | ||
15 | + <% @module_result.metric_results.each do |metric_result| %> | ||
16 | + <% range = metric_result.range %> | ||
17 | + <% if !range.nil? %> | ||
18 | + <tr> | ||
19 | + <td><a href="#" show-metric-history="<%= MezuroPlugin::Helpers::ContentViewerHelper.format_name(metric_result) %>" data-module-name="<%= @module.name %>"><%= metric_result.metric.name %></a></td> | ||
20 | + <td><%= MezuroPlugin::Helpers::ContentViewerHelper.format_grade(metric_result.value) %></td> | ||
21 | + <td><%= metric_result.weight %></td> | ||
22 | + <td style="background-color: #<%= range.color[2..-1] %>"><%= range.label %></td> | ||
23 | + </tr> | ||
24 | + <tr class="<%= MezuroPlugin::Helpers::ContentViewerHelper.format_name(metric_result) %>" style="display: none;"> | ||
25 | + <td colspan="3"> | ||
26 | + <div id='historical-<%= MezuroPlugin::Helpers::ContentViewerHelper.format_name(metric_result) %>'> | ||
27 | + </div> | ||
28 | + </td> | ||
29 | + <td align="right"> | ||
30 | + <%= range.comments.nil? ? '' : range.comments %> | ||
31 | + </td> | ||
32 | + </tr> | ||
41 | <% end %> | 33 | <% end %> |
42 | - </tbody> | ||
43 | - <tfoot> | ||
44 | - <tr> | ||
45 | - <td colspan = "3"> | ||
46 | - <div id='historical-grade'></div> | ||
47 | - </td> | ||
48 | - <td align = "right"> | ||
49 | - <a href="#" show-grade-history="<%= @module_result.module.name %>" data-module-name="<%= the_module.name%>" > | ||
50 | - <strong> | ||
51 | - <%= _('Grade:') %> | ||
52 | - <%= "%.02f" % @module_result.grade %> | ||
53 | - </strong> | ||
54 | - </a> | ||
55 | - </td> | ||
56 | - </tr> | ||
57 | - </tfoot> | ||
58 | - </table> | ||
59 | - </div> | ||
60 | -<% end %> | 34 | + <% end %> |
35 | + </tbody> | ||
36 | + <tfoot> | ||
37 | + <tr> | ||
38 | + <td colspan = "3"> | ||
39 | + <div id='historical-grade' style="display: none;"></div> | ||
40 | + </td> | ||
41 | + <td align = "right"> | ||
42 | + <a href="#" show-grade-history="<%= @module_result.module.name %>" data-module-name="<%= @module.name%>" > | ||
43 | + <strong> | ||
44 | + <%= _('Grade:') %> | ||
45 | + <%= "%.02f" % @module_result.grade %> | ||
46 | + </strong> | ||
47 | + </a> | ||
48 | + </td> | ||
49 | + </tr> | ||
50 | + </tfoot> | ||
51 | +</table> | ||
52 | +</div> |
plugins/mezuro/views/content_viewer/_project_error.rhtml
@@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
2 | <p> | 2 | <p> |
3 | <%= "State when error ocurred: #{@project.state}" %> | 3 | <%= "State when error ocurred: #{@project.state}" %> |
4 | <br/> | 4 | <br/> |
5 | - <% error = @project.error %> | 5 | + <% error = @project.kalibro_error %> |
6 | <%= error.message %> | 6 | <%= error.message %> |
7 | <ul> | 7 | <ul> |
8 | <% error.stack_trace.each do |trace| %> | 8 | <% error.stack_trace.each do |trace| %> |
plugins/mezuro/views/content_viewer/_project_result.rhtml
1 | <% unless @content.errors[:base].nil? %> | 1 | <% unless @content.errors[:base].nil? %> |
2 | <%= @content.errors[:base] %> | 2 | <%= @content.errors[:base] %> |
3 | <% else %> | 3 | <% else %> |
4 | - <% form_for :project_date, :html=>{:id=>"project_history_date"} do |f| %> | ||
5 | - <%= f.label :day, "Choose project date:" %> | ||
6 | - | ||
7 | - <table> | ||
8 | - <tr> | ||
9 | - <td> | ||
10 | - Day | ||
11 | - </td> | ||
12 | - <td> | ||
13 | - Month | ||
14 | - </td> | ||
15 | - <td> | ||
16 | - Year | ||
17 | - </td> | ||
18 | - </tr> | ||
19 | - <tr> | ||
20 | - <td> | ||
21 | - <%= f.text_field :day, :size => 1, :maxlength => 2, :placeholder =>"dd" %> | ||
22 | - </td> | ||
23 | - <td> | ||
24 | - <%= f.text_field :month, :size => 1, :maxlength => 2, :placeholder =>"mm" %> | ||
25 | - </td> | ||
26 | - <td> | ||
27 | - <%= f.text_field :year, :size => 1, :maxlength => 4, :placeholder =>"yyyy" %> | ||
28 | - </td> | ||
29 | - </tr> | ||
30 | - </table> | ||
31 | - <%= f.submit "Refresh" %> | ||
32 | - <% end %> | ||
33 | - | 4 | + <p> Choose a date to see specific project results: </p> |
5 | + <div id="datepicker" data-date="<%= @project_result.date %>"> | ||
6 | + <input id="datepicker_field" style="display:none"/> | ||
7 | + </div> | ||
34 | 8 | ||
35 | <h4><%= _('Last Result') %></h4> | 9 | <h4><%= _('Last Result') %></h4> |
36 | 10 | ||
@@ -48,4 +22,18 @@ | @@ -48,4 +22,18 @@ | ||
48 | <td><%= @project_result.formatted_analysis_time %></td> | 22 | <td><%= @project_result.formatted_analysis_time %></td> |
49 | </tr> | 23 | </tr> |
50 | </table> | 24 | </table> |
25 | + | ||
26 | + | ||
27 | + <script> | ||
28 | + jQuery(document).ready(function($) { | ||
29 | + $("#datepicker").datepicker({ altField: '#datepicker_field', showOn: 'button', dateFormat: "yy-mm-dd", | ||
30 | + buttonImageOnly: true, buttonImage: '/images/calendar_date_select/calendar.png', | ||
31 | + onSelect: function(dateText, inst) { | ||
32 | + reloadProjectWithDate(dateText) } }); | ||
33 | + var date = jQuery("#datepicker").attr('data-date').substr(0,10); | ||
34 | + $("#datepicker").datepicker( "setDate" , date ); | ||
35 | + | ||
36 | + }); | ||
37 | + </script> | ||
38 | + | ||
51 | <% end %> | 39 | <% end %> |
plugins/mezuro/views/content_viewer/show_configuration.rhtml
1 | <% @configuration_content = @page | 1 | <% @configuration_content = @page |
2 | -@configuration = @page.configuration %> | 2 | +@kalibro_configuration = @page.kalibro_configuration %> |
3 | +<% unless @page.errors[:base].nil? %> | ||
4 | + <% if @page.errors[:base] =~ /There is no configuration named/ %> | ||
5 | + <h3>Warning:</h3> | ||
6 | + <p>This Configuration doesn't exist on the Web Service. Do you want to <a href="/myprofile/<%= @page.profile.name %>/cms/destroy/<%= @page.id%>">delete</a> or <a href="/myprofile/<%= @page.profile.name %>/cms/edit/<%= @page.id%>">save it again</a>?</p> | ||
7 | + <% else %> | ||
8 | + <%= @page.errors[:base] %> | ||
9 | + <% end %> | ||
10 | +<% else %> | ||
3 | 11 | ||
4 | -<table id="project_info"> | ||
5 | - <tr> | ||
6 | - <td><%= _('Name') %></td> | ||
7 | - <td><%= @configuration.name %></td> | ||
8 | - </tr> | ||
9 | - <tr> | ||
10 | - <td><%= _('Description') %></td> | ||
11 | - <td><%= @configuration.description %></td> | ||
12 | - </tr> | ||
13 | -</table> | 12 | + <table id="project_info"> |
13 | + <tr> | ||
14 | + <td><%= _('Name') %></td> | ||
15 | + <td><%= @kalibro_configuration.name %></td> | ||
16 | + </tr> | ||
17 | + <tr> | ||
18 | + <td><%= _('Description') %></td> | ||
19 | + <td><%= @kalibro_configuration.description %></td> | ||
20 | + </tr> | ||
21 | + </table> | ||
14 | 22 | ||
15 | -<br/> | 23 | + <br/> |
16 | 24 | ||
17 | -<%= link_to "#{image_tag ('/plugins/mezuro/images/plus.png')}Add Metric", :controller => "mezuro_plugin_myprofile", | ||
18 | -:action => "choose_base_tool", :params => { :id => @configuration_content.id } %><br/> | 25 | + <%= link_to "#{image_tag ('/plugins/mezuro/images/plus.png')}Add Metric", :controller => "mezuro_plugin_myprofile", |
26 | + :action => "choose_base_tool", :params => { :id => @configuration_content.id } %><br/> | ||
19 | 27 | ||
20 | -<table> | ||
21 | - <tr class="titles"> | ||
22 | - <td><h5>Metric Name</h5></td> | ||
23 | - <td><h5>Collector Name</h5></td> | ||
24 | - <td><h5>Metric Code</h5></td> | ||
25 | - <td/><td/> | ||
26 | - </tr> | ||
27 | - <% @configuration.metric_configurations.each do |metric_configuration| %> | ||
28 | - <tr class="metric"> | ||
29 | - <td><%= metric_configuration.metric.name %></td> | ||
30 | - <% if metric_configuration.metric.instance_of? Kalibro::NativeMetric %> | ||
31 | - <td> | ||
32 | - <%= metric_configuration.metric.origin %> | ||
33 | - </td> | ||
34 | - <td><%= metric_configuration.code %></td> | ||
35 | - <td><%= link_to "Edit", :controller => "mezuro_plugin_myprofile", :action => "edit_metric_configuration", :params => | ||
36 | - {:metric_name => metric_configuration.metric.name, :id => @configuration_content.id} %></td> | ||
37 | - <% else %> | ||
38 | - <td> | ||
39 | - Compound Metric | ||
40 | - </td> | ||
41 | - <td><%= metric_configuration.code %></td> | ||
42 | - <td><%= link_to "Edit", :controller => "mezuro_plugin_myprofile", :action => "edit_compound_metric_configuration", :params => | ||
43 | - {:metric_name => metric_configuration.metric.name, :id => @configuration_content.id} %></td> | ||
44 | - <% end %> | ||
45 | - | ||
46 | - <td><%= link_to "Remove", :controller => "mezuro_plugin_myprofile", :action => "remove_metric_configuration", :params => | ||
47 | - {:metric_name => metric_configuration.metric.name, :id => @configuration_content.id} %></td> | 28 | + <table> |
29 | + <tr class="titles"> | ||
30 | + <td><h5>Metric Name</h5></td> | ||
31 | + <td><h5>Collector Name</h5></td> | ||
32 | + <td><h5>Metric Code</h5></td> | ||
33 | + <td/><td/> | ||
48 | </tr> | 34 | </tr> |
49 | - <% end %> | ||
50 | -</table> | 35 | + <% @kalibro_configuration.metric_configurations.each do |metric_configuration| %> |
36 | + <tr class="metric"> | ||
37 | + <td><%= metric_configuration.metric.name %></td> | ||
38 | + <% if metric_configuration.metric.instance_of? Kalibro::NativeMetric %> | ||
39 | + <td> | ||
40 | + <%= metric_configuration.metric.origin %> | ||
41 | + </td> | ||
42 | + <td><%= metric_configuration.code %></td> | ||
43 | + <td><%= link_to "Edit", :controller => "mezuro_plugin_myprofile", :action => "edit_metric_configuration", :params => | ||
44 | + {:metric_name => metric_configuration.metric.name, :id => @configuration_content.id} %></td> | ||
45 | + <% else %> | ||
46 | + <td> | ||
47 | + Compound Metric | ||
48 | + </td> | ||
49 | + <td><%= metric_configuration.code %></td> | ||
50 | + <td><%= link_to "Edit", :controller => "mezuro_plugin_myprofile", :action => "edit_compound_metric_configuration", :params => | ||
51 | + {:metric_name => metric_configuration.metric.name, :id => @configuration_content.id} %></td> | ||
52 | + <% end %> | ||
53 | + | ||
54 | + <td><%= link_to "Remove", :controller => "mezuro_plugin_myprofile", :action => "remove_metric_configuration", :params => | ||
55 | + {:metric_name => metric_configuration.metric.name, :id => @configuration_content.id} %></td> | ||
56 | + </tr> | ||
57 | + <% end %> | ||
58 | + </table> | ||
59 | +<% end %> |
plugins/mezuro/views/content_viewer/show_project.rhtml
@@ -2,60 +2,58 @@ | @@ -2,60 +2,58 @@ | ||
2 | 2 | ||
3 | <% @project = @page.project %> | 3 | <% @project = @page.project %> |
4 | <% unless @page.errors[:base].nil? %> | 4 | <% unless @page.errors[:base].nil? %> |
5 | - <%= @page.errors[:base] %> | ||
6 | -<% else %> | ||
7 | - | ||
8 | - <% if (@project==nil) %> | 5 | + <% if @page.errors[:base] =~ /There is no project named/ %> |
9 | <h3>Warning:</h3> | 6 | <h3>Warning:</h3> |
10 | <p>This project doesn't exist on the Web Service. Do you want to <a href="/myprofile/<%= @page.profile.name %>/cms/destroy/<%= @page.id%>">delete</a> or <a href="/myprofile/<%= @page.profile.name %>/cms/edit/<%= @page.id%>">save it again</a>?</p> | 7 | <p>This project doesn't exist on the Web Service. Do you want to <a href="/myprofile/<%= @page.profile.name %>/cms/destroy/<%= @page.id%>">delete</a> or <a href="/myprofile/<%= @page.profile.name %>/cms/edit/<%= @page.id%>">save it again</a>?</p> |
11 | <% else %> | 8 | <% else %> |
9 | + <%= @page.errors[:base] %> | ||
10 | + <% end %> | ||
11 | +<% else %> | ||
12 | 12 | ||
13 | + <table> | ||
14 | + <tr> | ||
15 | + <td><%= _('Name') %></td> | ||
16 | + <td><%= @project.name %></td> | ||
17 | + </tr> | ||
18 | + <tr> | ||
19 | + <td><%= _('License') %></td> | ||
20 | + <td><%= @project.license %></td> | ||
21 | + </tr> | ||
22 | + <tr> | ||
23 | + <td><%= _('Description') %></td> | ||
24 | + <td><%= @project.description %></td> | ||
25 | + </tr> | ||
26 | + <tr> | ||
27 | + <td><%= _('Repository type') %></td> | ||
28 | + <td><%= @project.repository.type %></td> | ||
29 | + </tr> | ||
30 | + <tr> | ||
31 | + <td><%= _('Repository address') %></td> | ||
32 | + <td><%= @project.repository.address %></td> | ||
33 | + </tr> | ||
34 | + <tr> | ||
35 | + <td><%= _('Configuration') %></td> | ||
36 | + <td><%= @project.configuration_name %></td> | ||
37 | + </tr> | ||
38 | + <tr> | ||
39 | + <td><%= _('Periodicity') %></td> | ||
40 | + <td><%= MezuroPlugin::Helpers::ContentViewerHelper.get_periodicity_option(@page.periodicity_in_days) %></td> | ||
41 | + </tr> | ||
42 | + <tr> | ||
43 | + <td><%= _('Status')%></td> | ||
44 | + <td> | ||
45 | + <div id="project-state"><%= @project.state %></div> | ||
46 | + <div id="msg-time"></div> | ||
47 | + </td> | ||
48 | + </tr> | ||
49 | + </table> | ||
13 | 50 | ||
14 | - <table> | ||
15 | - <tr> | ||
16 | - <td><%= _('Name') %></td> | ||
17 | - <td><%= @project.name %></td> | ||
18 | - </tr> | ||
19 | - <tr> | ||
20 | - <td><%= _('License') %></td> | ||
21 | - <td><%= @project.license %></td> | ||
22 | - </tr> | ||
23 | - <tr> | ||
24 | - <td><%= _('Description') %></td> | ||
25 | - <td><%= @project.description %></td> | ||
26 | - </tr> | ||
27 | - <tr> | ||
28 | - <td><%= _('Repository type') %></td> | ||
29 | - <td><%= @project.repository.type %></td> | ||
30 | - </tr> | ||
31 | - <tr> | ||
32 | - <td><%= _('Repository address') %></td> | ||
33 | - <td><%= @project.repository.address %></td> | ||
34 | - </tr> | ||
35 | - <tr> | ||
36 | - <td><%= _('Configuration') %></td> | ||
37 | - <td><%= @project.configuration_name %></td> | ||
38 | - </tr> | ||
39 | - <tr> | ||
40 | - <td><%= _('Periodicity') %></td> | ||
41 | - <td><%= MezuroPlugin::Helpers::ContentViewerHelper.get_periodicity_option(@page.periodicity_in_days) %></td> | ||
42 | - </tr> | ||
43 | - <tr> | ||
44 | - <td><%= _('Status')%></td> | ||
45 | - <td> | ||
46 | - <div id="project-state"><%= @project.state %></div> | ||
47 | - <div id="msg-time"></div> | ||
48 | - </td> | ||
49 | - </tr> | ||
50 | - </table> | ||
51 | - | ||
52 | - <br /> | 51 | + <br /> |
53 | 52 | ||
54 | - <div id="project-result" data-profile="<%= @page.profile.identifier %>" data-content="<%= @page.id %>" | ||
55 | - data-project-name="<%= @project.name %>"> | ||
56 | - </div> | ||
57 | - <div id="project-tree"></div> | ||
58 | - <div id="module-result"> | ||
59 | - </div> | ||
60 | - <% end %> | 53 | + <div id="project-result" data-profile="<%= @page.profile.identifier %>" data-content="<%= @page.id %>" |
54 | + data-project-name="<%= @project.name %>"> | ||
55 | + </div> | ||
56 | + <div id="project-tree"></div> | ||
57 | + <div id="module-result"> | ||
58 | + </div> | ||
61 | <% end %> | 59 | <% end %> |
plugins/mezuro/views/mezuro_plugin_myprofile/_range_form.html.erb
@@ -38,7 +38,7 @@ | @@ -38,7 +38,7 @@ | ||
38 | <%= f.label :color, "(*) Color:" %> | 38 | <%= f.label :color, "(*) Color:" %> |
39 | </td> | 39 | </td> |
40 | <td> | 40 | <td> |
41 | - <%= f.text_field :color %> | 41 | + <%= f.text_field(:color, :id => "range_color", :value => @range.mezuro_color) %> |
42 | </td> | 42 | </td> |
43 | </tr> | 43 | </tr> |
44 | <tr> | 44 | <tr> |
@@ -51,3 +51,11 @@ | @@ -51,3 +51,11 @@ | ||
51 | </tr> | 51 | </tr> |
52 | </table> | 52 | </table> |
53 | <%= f.submit "Save Range" %> | 53 | <%= f.submit "Save Range" %> |
54 | + | ||
55 | +<script>jQuery(document).ready(function($) { | ||
56 | + $('#range_color').colorPicker({ | ||
57 | + onColorChange : function(id, newValue) { | ||
58 | + jQuery('#range_color').val(newValue); | ||
59 | + } | ||
60 | + }); | ||
61 | +});</script> |
plugins/mezuro/views/mezuro_plugin_myprofile/edit_compound_metric_configuration.html.erb
1 | +<script src="/plugins/mezuro/javascripts/validations.js" type="text/javascript"></script> | ||
2 | +<script src="/plugins/mezuro/javascripts/colorPicker/jquery.colorPicker.min.js" type="text/javascript"></script> | ||
3 | +<script src="/plugins/mezuro/javascripts/colorPicker/jquery.colorPicker.js" type="text/javascript"></script> | ||
4 | + | ||
1 | <h2><%= @configuration_content.name %> Configuration</h2> | 5 | <h2><%= @configuration_content.name %> Configuration</h2> |
2 | 6 | ||
3 | <% form_for :metric_configuration, :url => {:action =>"update_compound_metric_configuration", :controller => "mezuro_plugin_myprofile"}, :method => :get do |f| %> | 7 | <% form_for :metric_configuration, :url => {:action =>"update_compound_metric_configuration", :controller => "mezuro_plugin_myprofile"}, :method => :get do |f| %> |
plugins/mezuro/views/mezuro_plugin_myprofile/edit_metric_configuration.html.erb
1 | <script src="/plugins/mezuro/javascripts/validations.js" type="text/javascript"></script> | 1 | <script src="/plugins/mezuro/javascripts/validations.js" type="text/javascript"></script> |
2 | +<script src="/plugins/mezuro/javascripts/colorPicker/jquery.colorPicker.min.js" type="text/javascript"></script> | ||
3 | +<script src="/plugins/mezuro/javascripts/colorPicker/jquery.colorPicker.js" type="text/javascript"></script> | ||
2 | 4 | ||
3 | <h2><%= @configuration_content.name %> Configuration</h2> | 5 | <h2><%= @configuration_content.name %> Configuration</h2> |
4 | 6 | ||
@@ -84,3 +86,4 @@ | @@ -84,3 +86,4 @@ | ||
84 | <%= link_to_remote "New Range", :url => {:action =>"new_range", :controller => "mezuro_plugin_myprofile", :id => @configuration_content.id, :metric_name => @metric.name} %> | 86 | <%= link_to_remote "New Range", :url => {:action =>"new_range", :controller => "mezuro_plugin_myprofile", :id => @configuration_content.id, :metric_name => @metric.name} %> |
85 | <div id="range_form" style="display:none"></div> | 87 | <div id="range_form" style="display:none"></div> |
86 | 88 | ||
89 | + |
plugins/mezuro/views/mezuro_plugin_myprofile/error_page.html.erb
0 → 100644
plugins/mezuro/views/mezuro_plugin_profile/error_page.html.erb
0 → 100644
plugins/tolerance_time/controllers/tolerance_time_plugin_myprofile_controller.rb
0 → 100644
@@ -0,0 +1,37 @@ | @@ -0,0 +1,37 @@ | ||
1 | +class ToleranceTimePluginMyprofileController < MyProfileController | ||
2 | + def index | ||
3 | + @tolerance = ToleranceTimePlugin::Tolerance.find_by_profile_id(profile.id) || ToleranceTimePlugin::Tolerance.create!(:profile => profile) | ||
4 | + convert_values | ||
5 | + if request.post? | ||
6 | + begin | ||
7 | + convert_params | ||
8 | + @tolerance.update_attributes!(params[:tolerance]) | ||
9 | + convert_values | ||
10 | + session[:notice] = _('Tolerance updated') | ||
11 | + rescue | ||
12 | + session[:notice] = _('Tolerance could not be updated') | ||
13 | + end | ||
14 | + end | ||
15 | + end | ||
16 | + | ||
17 | + private | ||
18 | + | ||
19 | + def convert_params | ||
20 | + params[:tolerance][:content_tolerance] = params[:tolerance][:content_tolerance].to_i * params[:content_tolerance_unit].to_i if !params[:tolerance][:content_tolerance].blank? | ||
21 | + params[:tolerance][:comment_tolerance] = params[:tolerance][:comment_tolerance].to_i * params[:comment_tolerance_unit].to_i if !params[:tolerance][:comment_tolerance].blank? | ||
22 | + end | ||
23 | + | ||
24 | + def convert_values | ||
25 | + @content_default_unit = select_unit(@tolerance.content_tolerance) | ||
26 | + @comment_default_unit = select_unit(@tolerance.comment_tolerance) | ||
27 | + @tolerance.content_tolerance /= @content_default_unit if !@tolerance.content_tolerance.nil? | ||
28 | + @tolerance.comment_tolerance /= @comment_default_unit if !@tolerance.comment_tolerance.nil? | ||
29 | + end | ||
30 | + | ||
31 | + def select_unit(value) | ||
32 | + return 1 if value.nil? || value == 0 | ||
33 | + return 3600 if value % 3600 == 0 | ||
34 | + return 60 if value % 60 == 0 | ||
35 | + return 1 | ||
36 | + end | ||
37 | +end |
plugins/tolerance_time/db/migrate/20120719090320_create_tolerance_time_plugin_tolerances.rb
0 → 100644
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +class CreateToleranceTimePluginTolerances < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + create_table :tolerance_time_plugin_tolerances do |t| | ||
4 | + t.references :profile | ||
5 | + t.integer :content_tolerance | ||
6 | + t.integer :comment_tolerance | ||
7 | + end | ||
8 | + end | ||
9 | + | ||
10 | + def self.down | ||
11 | + drop_table :tolerance_time_plugin_tolerances | ||
12 | + end | ||
13 | +end |
plugins/tolerance_time/db/migrate/20120719095004_create_tolerance_time_plugin_publications.rb
0 → 100644
@@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
1 | +class CreateToleranceTimePluginPublications < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + create_table :tolerance_time_plugin_publications do |t| | ||
4 | + t.references :target, :polymorphic => true | ||
5 | + t.timestamps | ||
6 | + end | ||
7 | + end | ||
8 | + | ||
9 | + def self.down | ||
10 | + drop_table :tolerance_time_plugin_publications | ||
11 | + end | ||
12 | +end |
@@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
1 | +require_dependency 'article' | ||
2 | + | ||
3 | +class Article | ||
4 | + after_create do |article| | ||
5 | + ToleranceTimePlugin::Publication.create!(:target => article) if article.published | ||
6 | + end | ||
7 | + | ||
8 | + before_save do |article| | ||
9 | + if article.published_changed? | ||
10 | + if article.published | ||
11 | + ToleranceTimePlugin::Publication.create!(:target => article) | ||
12 | + else | ||
13 | + publication = ToleranceTimePlugin::Publication.find_by_target(article) | ||
14 | + publication.destroy if publication.present? | ||
15 | + end | ||
16 | + end | ||
17 | + end | ||
18 | + | ||
19 | + before_destroy do |article| | ||
20 | + publication = ToleranceTimePlugin::Publication.find_by_target(article) | ||
21 | + publication.destroy if publication.present? | ||
22 | + end | ||
23 | +end |
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +require_dependency 'comment' | ||
2 | + | ||
3 | +class Comment | ||
4 | + after_create do |comment| | ||
5 | + ToleranceTimePlugin::Publication.create!(:target => comment) | ||
6 | + end | ||
7 | + | ||
8 | + before_destroy do |comment| | ||
9 | + publication = ToleranceTimePlugin::Publication.find_by_target(comment) | ||
10 | + publication.destroy if publication.present? | ||
11 | + end | ||
12 | +end | ||
13 | + |
@@ -0,0 +1,71 @@ | @@ -0,0 +1,71 @@ | ||
1 | +require_dependency 'ext/article' | ||
2 | +require_dependency 'ext/comment' | ||
3 | + | ||
4 | +class ToleranceTimePlugin < Noosfero::Plugin | ||
5 | + | ||
6 | + def self.plugin_name | ||
7 | + "Tolerance Time" | ||
8 | + end | ||
9 | + | ||
10 | + def self.plugin_description | ||
11 | + _("Adds a tolerance time for editing content after its publication") | ||
12 | + end | ||
13 | + | ||
14 | + def self.expired?(content) | ||
15 | + return false if content.kind_of?(Comment) && !content.article.kind_of?(Article) | ||
16 | + | ||
17 | + expirable = content.kind_of?(Comment) || (!content.folder? && content.published?) | ||
18 | + publication = ToleranceTimePlugin::Publication.find_by_target(content) | ||
19 | + publication = ToleranceTimePlugin::Publication.create!(:target => content) if expirable && publication.nil? | ||
20 | + person_article = content.kind_of?(Article) && content.profile.kind_of?(Person) | ||
21 | + | ||
22 | + !person_article && expirable && publication.expired? | ||
23 | + end | ||
24 | + | ||
25 | + def control_panel_buttons | ||
26 | + {:title => _('Tolerance Adjustements'), :url => {:controller => 'tolerance_time_plugin_myprofile', :profile => context.profile.identifier}, :icon => 'tolerance-time' } | ||
27 | + end | ||
28 | + | ||
29 | + def stylesheet? | ||
30 | + true | ||
31 | + end | ||
32 | + | ||
33 | + def cms_controller_filters | ||
34 | + return if !context.environment.plugin_enabled?(ToleranceTimePlugin) | ||
35 | + block = lambda do | ||
36 | + content = Article.find(params[:id]) | ||
37 | + if ToleranceTimePlugin.expired?(content) | ||
38 | + session[:notice] = _("This content can't be edited anymore because it expired the tolerance time") | ||
39 | + redirect_to content.url | ||
40 | + end | ||
41 | + end | ||
42 | + | ||
43 | + { :type => 'before_filter', | ||
44 | + :method_name => 'expired_content', | ||
45 | + :options => {:only => 'edit'}, | ||
46 | + :block => block } | ||
47 | + end | ||
48 | + | ||
49 | + def content_viewer_controller_filters | ||
50 | + return if !context.environment.plugin_enabled?(ToleranceTimePlugin) | ||
51 | + block = lambda do | ||
52 | + content = Comment.find(params[:id]) | ||
53 | + if ToleranceTimePlugin.expired?(content) | ||
54 | + session[:notice] = _("This content can't be edited anymore because it expired the tolerance time") | ||
55 | + redirect_to content.article.url | ||
56 | + end | ||
57 | + end | ||
58 | + | ||
59 | + { :type => 'before_filter', | ||
60 | + :method_name => 'expired_content', | ||
61 | + :options => {:only => 'edit_comment'}, | ||
62 | + :block => block } | ||
63 | + end | ||
64 | + | ||
65 | + def content_expire_edit(content) | ||
66 | + if ToleranceTimePlugin.expired?(content) | ||
67 | + _('The tolerance time for editing this content is over.') | ||
68 | + end | ||
69 | + end | ||
70 | + | ||
71 | +end |
plugins/tolerance_time/lib/tolerance_time_plugin/publication.rb
0 → 100644
@@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
1 | +class ToleranceTimePlugin::Publication < Noosfero::Plugin::ActiveRecord | ||
2 | + belongs_to :target, :polymorphic => true | ||
3 | + validates_presence_of :target_id, :target_type | ||
4 | + validates_uniqueness_of :target_id, :scope => :target_type | ||
5 | + | ||
6 | + class << self | ||
7 | + def find_by_target(target) | ||
8 | + kind = target.kind_of?(Article) ? 'Article' : 'Comment' | ||
9 | + find_by_target_id_and_target_type(target.id, kind) | ||
10 | + end | ||
11 | + end | ||
12 | + | ||
13 | + def expired? | ||
14 | + profile = (target.kind_of?(Article) ? target.profile : target.article.profile) | ||
15 | + profile_tolerance = ToleranceTimePlugin::Tolerance.find_by_profile_id(profile.id) | ||
16 | + content_tolerance = profile_tolerance ? profile_tolerance.content_tolerance : nil | ||
17 | + comment_tolerance = profile_tolerance ? profile_tolerance.comment_tolerance : nil | ||
18 | + if target.kind_of?(Article) | ||
19 | + tolerance_time = content_tolerance || 1.0/0 | ||
20 | + elsif target.kind_of?(Comment) | ||
21 | + tolerance_time = comment_tolerance || 1.0/0 | ||
22 | + else | ||
23 | + tolerance_time = 1.0/0 | ||
24 | + end | ||
25 | + created_at.to_f.to_i+tolerance_time < Time.now.to_i | ||
26 | + end | ||
27 | +end |
plugins/tolerance_time/lib/tolerance_time_plugin/tolerance.rb
0 → 100644
@@ -0,0 +1,7 @@ | @@ -0,0 +1,7 @@ | ||
1 | +class ToleranceTimePlugin::Tolerance < Noosfero::Plugin::ActiveRecord | ||
2 | + belongs_to :profile | ||
3 | + validates_presence_of :profile_id | ||
4 | + validates_uniqueness_of :profile_id | ||
5 | + validates_numericality_of :content_tolerance, :only_integer => true, :allow_nil => true | ||
6 | + validates_numericality_of :comment_tolerance, :only_integer => true, :allow_nil => true | ||
7 | +end |
4.71 KB
@@ -0,0 +1,33 @@ | @@ -0,0 +1,33 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | ||
2 | + | ||
3 | +class ArticleTest < ActiveSupport::TestCase | ||
4 | + should 'create a publication after publishing the article' do | ||
5 | + article = fast_create(Article, :published => false, :profile_id => fast_create(Profile).id) | ||
6 | + assert_nil ToleranceTimePlugin::Publication.find_by_target(article) | ||
7 | + | ||
8 | + article.published = true | ||
9 | + article.save! | ||
10 | + assert_not_nil ToleranceTimePlugin::Publication.find_by_target(article) | ||
11 | + end | ||
12 | + | ||
13 | + should 'destroy publication if the article is destroyed' do | ||
14 | + profile = fast_create(Profile) | ||
15 | + article = fast_create(Article, :profile_id => profile.id) | ||
16 | + article_publication = ToleranceTimePlugin::Publication.create!(:target => article) | ||
17 | + article.destroy | ||
18 | + assert_raise ActiveRecord::RecordNotFound do | ||
19 | + article_publication.reload | ||
20 | + end | ||
21 | + end | ||
22 | + | ||
23 | + should 'destroy publication if the article is changed to not published' do | ||
24 | + profile = fast_create(Profile) | ||
25 | + article = fast_create(Article, :profile_id => profile.id) | ||
26 | + article_publication = ToleranceTimePlugin::Publication.create!(:target => article) | ||
27 | + article.published = false | ||
28 | + article.save! | ||
29 | + assert_raise ActiveRecord::RecordNotFound do | ||
30 | + article_publication.reload | ||
31 | + end | ||
32 | + end | ||
33 | +end |
@@ -0,0 +1,24 @@ | @@ -0,0 +1,24 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | ||
2 | + | ||
3 | +class CommentTest < ActiveSupport::TestCase | ||
4 | + should 'create a publication after posting a comment' do | ||
5 | + article = fast_create(Article, :profile_id => fast_create(Person).id) | ||
6 | + comment = Comment.new(:author_id => fast_create(Person).id, :body => 'Hello There!', :source_id => article.id) | ||
7 | + assert_difference ToleranceTimePlugin::Publication, :count do | ||
8 | + comment.save! | ||
9 | + end | ||
10 | + assert_not_nil ToleranceTimePlugin::Publication.find_by_target(comment) | ||
11 | + end | ||
12 | + | ||
13 | + should 'destroy publication if the comment is destroyed' do | ||
14 | + profile = fast_create(Profile) | ||
15 | + article = fast_create(Article, :profile_id => profile.id) | ||
16 | + comment = fast_create(Comment, :source_id => article.id) | ||
17 | + comment_publication = ToleranceTimePlugin::Publication.create!(:target => comment) | ||
18 | + comment.destroy | ||
19 | + assert_raise ActiveRecord::RecordNotFound do | ||
20 | + comment_publication.reload | ||
21 | + end | ||
22 | + end | ||
23 | +end | ||
24 | + |
plugins/tolerance_time/test/unit/tolerance_time_plugin/publication_test.rb
0 → 100644
@@ -0,0 +1,78 @@ | @@ -0,0 +1,78 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../../test/test_helper' | ||
2 | + | ||
3 | +class ToleranceTimePlugin::PublicationTest < ActiveSupport::TestCase | ||
4 | + should 'validate presence of target' do | ||
5 | + publication = ToleranceTimePlugin::Publication.new | ||
6 | + publication.valid? | ||
7 | + assert publication.errors.invalid?(:target_id) | ||
8 | + assert publication.errors.invalid?(:target_type) | ||
9 | + | ||
10 | + publication.target = fast_create(Article) | ||
11 | + publication.valid? | ||
12 | + assert !publication.errors.invalid?(:target_id) | ||
13 | + assert !publication.errors.invalid?(:target_type) | ||
14 | + end | ||
15 | + | ||
16 | + should 'validate uniqueness of target' do | ||
17 | + target = fast_create(Article) | ||
18 | + p1 = ToleranceTimePlugin::Publication.create!(:target => target) | ||
19 | + p2 = ToleranceTimePlugin::Publication.new(:target => target) | ||
20 | + p2.valid? | ||
21 | + | ||
22 | + assert p2.errors.invalid?(:target_id) | ||
23 | + end | ||
24 | + | ||
25 | + should 'be able to find publication by target' do | ||
26 | + article = fast_create(Article) | ||
27 | + publication = ToleranceTimePlugin::Publication.create!(:target => article) | ||
28 | + assert_equal publication, ToleranceTimePlugin::Publication.find_by_target(article) | ||
29 | + end | ||
30 | + | ||
31 | + should 'avaliate if the publication is expired' do | ||
32 | + profile = fast_create(Profile) | ||
33 | + author = fast_create(Person) | ||
34 | + a1 = fast_create(Article, :profile_id => profile.id) | ||
35 | + a2 = fast_create(Article, :profile_id => profile.id) | ||
36 | + c1 = fast_create(Comment, :source_id => a1.id) | ||
37 | + c2 = fast_create(Comment, :source_id => a2.id) | ||
38 | + ToleranceTimePlugin::Tolerance.create!(:profile => profile, :content_tolerance => 10.minutes, :comment_tolerance => 5.minutes) | ||
39 | + expired_article = ToleranceTimePlugin::Publication.create!(:target => a1) | ||
40 | + expired_article.created_at = 15.minutes.ago | ||
41 | + expired_article.save! | ||
42 | + on_time_article = ToleranceTimePlugin::Publication.create!(:target => a2) | ||
43 | + on_time_article.created_at = 5.minutes.ago | ||
44 | + on_time_article.save! | ||
45 | + expired_comment = ToleranceTimePlugin::Publication.create!(:target => c1) | ||
46 | + expired_comment.created_at = 8.minutes.ago | ||
47 | + expired_comment.save! | ||
48 | + on_time_comment = ToleranceTimePlugin::Publication.create!(:target => c2) | ||
49 | + on_time_comment.created_at = 2.minutes.ago | ||
50 | + on_time_comment.save! | ||
51 | + | ||
52 | + assert expired_article.expired? | ||
53 | + assert !on_time_article.expired? | ||
54 | + assert expired_comment.expired? | ||
55 | + assert !on_time_comment.expired? | ||
56 | + end | ||
57 | + | ||
58 | + should 'consider tolerance infinity if not defined' do | ||
59 | + profile = fast_create(Profile) | ||
60 | + article = fast_create(Article, :profile_id => profile.id) | ||
61 | + article_publication = ToleranceTimePlugin::Publication.create!(:target => article) | ||
62 | + article_publication.created_at = 1000.years.ago | ||
63 | + article_publication.save! | ||
64 | + ToleranceTimePlugin::Tolerance.create!(:profile => profile) | ||
65 | + | ||
66 | + assert !article_publication.expired? | ||
67 | + end | ||
68 | + | ||
69 | + should 'not crash if profile has no tolerance yet defined' do | ||
70 | + profile = fast_create(Profile) | ||
71 | + article = fast_create(Article, :profile_id => profile.id) | ||
72 | + article_publication = ToleranceTimePlugin::Publication.create!(:target => article) | ||
73 | + article_publication.created_at = 1000.years.ago | ||
74 | + article_publication.save! | ||
75 | + | ||
76 | + assert !article_publication.expired? | ||
77 | + end | ||
78 | +end |
plugins/tolerance_time/test/unit/tolerance_time_plugin/tolerance_test.rb
0 → 100644
@@ -0,0 +1,37 @@ | @@ -0,0 +1,37 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../../test/test_helper' | ||
2 | + | ||
3 | +class ToleranceTimePlugin::ToleranceTest < ActiveSupport::TestCase | ||
4 | + should 'validate presence of profile' do | ||
5 | + tolerance = ToleranceTimePlugin::Tolerance.new | ||
6 | + tolerance.valid? | ||
7 | + assert tolerance.errors.invalid?(:profile_id) | ||
8 | + | ||
9 | + tolerance.profile = fast_create(Profile) | ||
10 | + tolerance.valid? | ||
11 | + assert !tolerance.errors.invalid?(:profile_id) | ||
12 | + end | ||
13 | + | ||
14 | + should 'validate uniqueness of profile' do | ||
15 | + profile = fast_create(Profile) | ||
16 | + t1 = ToleranceTimePlugin::Tolerance.create!(:profile => profile) | ||
17 | + t2 = ToleranceTimePlugin::Tolerance.new(:profile => profile) | ||
18 | + t2.valid? | ||
19 | + | ||
20 | + assert t2.errors.invalid?(:profile_id) | ||
21 | + end | ||
22 | + | ||
23 | + should 'validate integer format for comment and content tolerance' do | ||
24 | + tolerance = ToleranceTimePlugin::Tolerance.new(:profile => fast_create(Profile)) | ||
25 | + assert tolerance.valid? | ||
26 | + | ||
27 | + tolerance.comment_tolerance = 'sdfa' | ||
28 | + tolerance.content_tolerance = 4.5 | ||
29 | + tolerance.valid? | ||
30 | + assert tolerance.errors.invalid?(:comment_tolerance) | ||
31 | + assert tolerance.errors.invalid?(:content_tolerance) | ||
32 | + | ||
33 | + tolerance.comment_tolerance = 3 | ||
34 | + tolerance.content_tolerance = 6 | ||
35 | + assert tolerance.valid? | ||
36 | + end | ||
37 | +end |
plugins/tolerance_time/views/tolerance_time_plugin_myprofile/index.html.erb
0 → 100644
@@ -0,0 +1,24 @@ | @@ -0,0 +1,24 @@ | ||
1 | +<h1><%= _('Tolerance Adjustments') %></h1> | ||
2 | + | ||
3 | +<%= error_messages_for :tolerance %> | ||
4 | + | ||
5 | +<% form_for :tolerance, @tolerance do |f| %> | ||
6 | + | ||
7 | + <% time_units = [[_('Seconds'), 1], [_('Minutes'), 60], [_('Hours'), 3600]]%> | ||
8 | + | ||
9 | + <% if profile.organization? %> | ||
10 | + <%= labelled_form_field(_('Content edition tolerance time'), | ||
11 | + f.text_field(:content_tolerance, :size => 2, :style => 'font-size: 14px; text-align: right') + | ||
12 | + select_tag(:content_tolerance_unit, options_for_select(time_units, @content_default_unit) )) %> | ||
13 | + <% end %> | ||
14 | + <%= labelled_form_field(_('Comment edition tolerance time'), | ||
15 | + f.text_field(:comment_tolerance, :size => 2, :style => 'font-size: 14px; text-align: right') + | ||
16 | + select_tag(:comment_tolerance_unit, options_for_select(time_units, @comment_default_unit) )) %> | ||
17 | + | ||
18 | + <%= content_tag( 'small', _('Empty means unlimited and zero means right away.') ) %> | ||
19 | + | ||
20 | + <% button_bar do %> | ||
21 | + <%= submit_button('save', _('Save'))%> | ||
22 | + <%= button('back', _('Back'), {:controller => 'profile_editor'})%> | ||
23 | + <% end %> | ||
24 | +<% end %> |
public/designs/icons/tango/ie6.css
1 | .msie6 .icon-edit { background-image: url(ie6/Tango/16x16/apps/text-editor.gif) } | 1 | .msie6 .icon-edit { background-image: url(ie6/Tango/16x16/apps/text-editor.gif) } |
2 | .msie6 .icon-home { background-image: url(ie6/Tango/16x16/actions/go-home.gif) } | 2 | .msie6 .icon-home { background-image: url(ie6/Tango/16x16/actions/go-home.gif) } |
3 | -.msie6 .icon-new { background-image: url(ie6/Tango/16x16/actions/filenew.gif) } | 3 | +.msie6 .icon-new, |
4 | +.msie6 .icon-suggest { background-image: url(ie6/Tango/16x16/actions/filenew.gif) } | ||
4 | .msie6 .icon-close { background-image: url(ie6/Tango/16x16/actions/gtk-cancel.gif) } | 5 | .msie6 .icon-close { background-image: url(ie6/Tango/16x16/actions/gtk-cancel.gif) } |
5 | .msie6 .icon-newfolder { background-image: url(ie6/Tango/16x16/actions/folder-new.gif) } | 6 | .msie6 .icon-newfolder { background-image: url(ie6/Tango/16x16/actions/folder-new.gif) } |
6 | .msie6 .icon-save { background-image: url(ie6/Tango/16x16/actions/filesave.gif) } | 7 | .msie6 .icon-save { background-image: url(ie6/Tango/16x16/actions/filesave.gif) } |
public/designs/icons/tango/style.css
@@ -3,7 +3,8 @@ | @@ -3,7 +3,8 @@ | ||
3 | /******************SMALL ICONS********************/ | 3 | /******************SMALL ICONS********************/ |
4 | .icon-edit { background-image: url(Tango/16x16/apps/text-editor.png) } | 4 | .icon-edit { background-image: url(Tango/16x16/apps/text-editor.png) } |
5 | .icon-home { background-image: url(Tango/16x16/actions/go-home.png) } | 5 | .icon-home { background-image: url(Tango/16x16/actions/go-home.png) } |
6 | -.icon-new { background-image: url(Tango/16x16/actions/filenew.png) } | 6 | +.icon-new, |
7 | +.icon-suggest { background-image: url(Tango/16x16/actions/filenew.png) } | ||
7 | .icon-close { background-image: url(Tango/16x16/actions/gtk-cancel.png) } | 8 | .icon-close { background-image: url(Tango/16x16/actions/gtk-cancel.png) } |
8 | .icon-newfolder { background-image: url(Tango/16x16/actions/folder-new.png) } | 9 | .icon-newfolder { background-image: url(Tango/16x16/actions/folder-new.png) } |
9 | .icon-folder { background-image: url(Tango/16x16/places/folder.png) } | 10 | .icon-folder { background-image: url(Tango/16x16/places/folder.png) } |
public/designs/themes/base/style.css
@@ -483,6 +483,17 @@ div#notice { | @@ -483,6 +483,17 @@ div#notice { | ||
483 | padding: 0px; | 483 | padding: 0px; |
484 | } | 484 | } |
485 | 485 | ||
486 | +#content .box-1 .people-block ul, | ||
487 | +#content .box-1 .profile-list-block ul, | ||
488 | +#content .box-1 .enterprises-block ul, | ||
489 | +#content .box-1 .members-block ul, | ||
490 | +#content .box-1 .communities-block ul, | ||
491 | +#content .box-1 .friends-block ul, | ||
492 | +#content .box-1 .fans-block ul { | ||
493 | + width: auto; | ||
494 | + display: block; | ||
495 | +} | ||
496 | + | ||
486 | #content .tags-block .block-footer-content a, | 497 | #content .tags-block .block-footer-content a, |
487 | #content .people-block .block-footer-content a, | 498 | #content .people-block .block-footer-content a, |
488 | #content .profile-list-block .block-footer-content a, | 499 | #content .profile-list-block .block-footer-content a, |
@@ -588,33 +599,51 @@ div#notice { | @@ -588,33 +599,51 @@ div#notice { | ||
588 | display: none; | 599 | display: none; |
589 | } | 600 | } |
590 | 601 | ||
602 | +#content .box-1 .link-list-block { | ||
603 | + margin: 0px; | ||
604 | +} | ||
605 | + | ||
591 | #content .link-list-block li { | 606 | #content .link-list-block li { |
592 | background: #FFF; | 607 | background: #FFF; |
593 | padding: 0px; | 608 | padding: 0px; |
594 | margin: 5px 0px; | 609 | margin: 5px 0px; |
595 | } | 610 | } |
596 | 611 | ||
612 | +#content .box-1 .link-list-block li { | ||
613 | + display: inline-block; | ||
614 | +} | ||
615 | + | ||
597 | #content .link-list-block li a { | 616 | #content .link-list-block li a { |
598 | font-size: 14px; | 617 | font-size: 14px; |
599 | line-height: 24px; | 618 | line-height: 24px; |
600 | color: #000; | 619 | color: #000; |
601 | background-color: #EEE; | 620 | background-color: #EEE; |
602 | background-position: 4px 50%; | 621 | background-position: 4px 50%; |
603 | - -moz-border-radius: 4px; | ||
604 | - -webkit-border-radius: 4px; | 622 | + border-radius: 4px; |
623 | +} | ||
624 | + | ||
625 | +#content .box-1 .link-list-block li a { | ||
626 | + padding-left: 25px; | ||
627 | + padding-right: 10px; | ||
628 | + margin-right: 5px; | ||
629 | + background-position: 5px 50%; | ||
605 | } | 630 | } |
606 | 631 | ||
607 | #content .link-list-block li a.link-this-page { | 632 | #content .link-list-block li a.link-this-page { |
608 | - -moz-border-radius-topright: 0px; | ||
609 | - -moz-border-radius-bottomright: 0px; | ||
610 | - -webkit-border-radius-topright: 0px; | ||
611 | - -webkit-border-radius-bottomright: 0px; | 633 | + border-radius-topright: 0px; |
634 | + border-radius-bottomright: 0px; | ||
612 | background-color: #cecece; | 635 | background-color: #cecece; |
613 | max-width: 175px; | 636 | max-width: 175px; |
614 | width: 200px; | 637 | width: 200px; |
615 | border-right: 2px solid #555753; | 638 | border-right: 2px solid #555753; |
616 | } | 639 | } |
617 | 640 | ||
641 | +#content .box-1 .link-list-block li a.link-this-page { | ||
642 | + width: auto; | ||
643 | + border: none; | ||
644 | + border-radius: 4px; | ||
645 | +} | ||
646 | + | ||
618 | #content .link-list-block li a:hover { | 647 | #content .link-list-block li a:hover { |
619 | background-color: #555753; | 648 | background-color: #555753; |
620 | color: #FFF; | 649 | color: #FFF; |
@@ -908,6 +937,16 @@ hr.pre-posts, hr.sep-posts { | @@ -908,6 +937,16 @@ hr.pre-posts, hr.sep-posts { | ||
908 | #article-actions a.button:hover { | 937 | #article-actions a.button:hover { |
909 | color: #555753; | 938 | color: #555753; |
910 | } | 939 | } |
940 | +#content a.disabled, | ||
941 | +#content a.disabled:hover { | ||
942 | + color: #888; | ||
943 | + text-decoration: none; | ||
944 | +} | ||
945 | +#content a.button.disabled, | ||
946 | +#content a.button.disabled:hover { | ||
947 | + background-color: #CCC; | ||
948 | + border-color: #CCC; | ||
949 | +} | ||
911 | 950 | ||
912 | #addThis { | 951 | #addThis { |
913 | text-align: right; | 952 | text-align: right; |
public/javascripts/application.js
@@ -922,7 +922,7 @@ jQuery(function($) { | @@ -922,7 +922,7 @@ jQuery(function($) { | ||
922 | $('.colorbox').live('click', function() { | 922 | $('.colorbox').live('click', function() { |
923 | $.fn.colorbox({ | 923 | $.fn.colorbox({ |
924 | href:$(this).attr('href'), | 924 | href:$(this).attr('href'), |
925 | - maxWidth: '500', | 925 | + maxWidth: '600', |
926 | maxHeight: '550', | 926 | maxHeight: '550', |
927 | open:true | 927 | open:true |
928 | }); | 928 | }); |
@@ -0,0 +1,1530 @@ | @@ -0,0 +1,1530 @@ | ||
1 | +/* | ||
2 | +* jQuery timepicker addon | ||
3 | +* By: Trent Richardson [http://trentrichardson.com] | ||
4 | +* Version 1.0.1 | ||
5 | +* Last Modified: 07/01/2012 | ||
6 | +* | ||
7 | +* Copyright 2012 Trent Richardson | ||
8 | +* You may use this project under MIT or GPL licenses. | ||
9 | +* http://trentrichardson.com/Impromptu/GPL-LICENSE.txt | ||
10 | +* http://trentrichardson.com/Impromptu/MIT-LICENSE.txt | ||
11 | +* | ||
12 | +* HERES THE CSS: | ||
13 | +* .ui-timepicker-div .ui-widget-header { margin-bottom: 8px; } | ||
14 | +* .ui-timepicker-div dl { text-align: left; } | ||
15 | +* .ui-timepicker-div dl dt { height: 25px; margin-bottom: -25px; } | ||
16 | +* .ui-timepicker-div dl dd { margin: 0 10px 10px 65px; } | ||
17 | +* .ui-timepicker-div td { font-size: 90%; } | ||
18 | +* .ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; } | ||
19 | +*/ | ||
20 | + | ||
21 | +/*jslint evil: true, maxlen: 300, white: false, undef: false, nomen: false, onevar: false */ | ||
22 | + | ||
23 | +(function($) { | ||
24 | + | ||
25 | +// Prevent "Uncaught RangeError: Maximum call stack size exceeded" | ||
26 | +$.ui.timepicker = $.ui.timepicker || {}; | ||
27 | +if ($.ui.timepicker.version) { | ||
28 | + return; | ||
29 | +} | ||
30 | + | ||
31 | +$.extend($.ui, { timepicker: { version: "1.0.1" } }); | ||
32 | + | ||
33 | +/* Time picker manager. | ||
34 | + Use the singleton instance of this class, $.timepicker, to interact with the time picker. | ||
35 | + Settings for (groups of) time pickers are maintained in an instance object, | ||
36 | + allowing multiple different settings on the same page. */ | ||
37 | + | ||
38 | +function Timepicker() { | ||
39 | + this.regional = []; // Available regional settings, indexed by language code | ||
40 | + this.regional[''] = { // Default regional settings | ||
41 | + currentText: 'Now', | ||
42 | + closeText: 'Done', | ||
43 | + ampm: false, | ||
44 | + amNames: ['AM', 'A'], | ||
45 | + pmNames: ['PM', 'P'], | ||
46 | + timeFormat: 'hh:mm tt', | ||
47 | + timeSuffix: '', | ||
48 | + timeOnlyTitle: 'Choose Time', | ||
49 | + timeText: 'Time', | ||
50 | + hourText: 'Hour', | ||
51 | + minuteText: 'Minute', | ||
52 | + secondText: 'Second', | ||
53 | + millisecText: 'Millisecond', | ||
54 | + timezoneText: 'Time Zone' | ||
55 | + }; | ||
56 | + this._defaults = { // Global defaults for all the datetime picker instances | ||
57 | + showButtonPanel: true, | ||
58 | + timeOnly: false, | ||
59 | + showHour: true, | ||
60 | + showMinute: true, | ||
61 | + showSecond: false, | ||
62 | + showMillisec: false, | ||
63 | + showTimezone: false, | ||
64 | + showTime: true, | ||
65 | + stepHour: 1, | ||
66 | + stepMinute: 1, | ||
67 | + stepSecond: 1, | ||
68 | + stepMillisec: 1, | ||
69 | + hour: 0, | ||
70 | + minute: 0, | ||
71 | + second: 0, | ||
72 | + millisec: 0, | ||
73 | + timezone: null, | ||
74 | + useLocalTimezone: false, | ||
75 | + defaultTimezone: "+0000", | ||
76 | + hourMin: 0, | ||
77 | + minuteMin: 0, | ||
78 | + secondMin: 0, | ||
79 | + millisecMin: 0, | ||
80 | + hourMax: 23, | ||
81 | + minuteMax: 59, | ||
82 | + secondMax: 59, | ||
83 | + millisecMax: 999, | ||
84 | + minDateTime: null, | ||
85 | + maxDateTime: null, | ||
86 | + onSelect: null, | ||
87 | + hourGrid: 0, | ||
88 | + minuteGrid: 0, | ||
89 | + secondGrid: 0, | ||
90 | + millisecGrid: 0, | ||
91 | + alwaysSetTime: true, | ||
92 | + separator: ' ', | ||
93 | + altFieldTimeOnly: true, | ||
94 | + showTimepicker: true, | ||
95 | + timezoneIso8601: false, | ||
96 | + timezoneList: null, | ||
97 | + addSliderAccess: false, | ||
98 | + sliderAccessArgs: null | ||
99 | + }; | ||
100 | + $.extend(this._defaults, this.regional['']); | ||
101 | +} | ||
102 | + | ||
103 | +$.extend(Timepicker.prototype, { | ||
104 | + $input: null, | ||
105 | + $altInput: null, | ||
106 | + $timeObj: null, | ||
107 | + inst: null, | ||
108 | + hour_slider: null, | ||
109 | + minute_slider: null, | ||
110 | + second_slider: null, | ||
111 | + millisec_slider: null, | ||
112 | + timezone_select: null, | ||
113 | + hour: 0, | ||
114 | + minute: 0, | ||
115 | + second: 0, | ||
116 | + millisec: 0, | ||
117 | + timezone: null, | ||
118 | + defaultTimezone: "+0000", | ||
119 | + hourMinOriginal: null, | ||
120 | + minuteMinOriginal: null, | ||
121 | + secondMinOriginal: null, | ||
122 | + millisecMinOriginal: null, | ||
123 | + hourMaxOriginal: null, | ||
124 | + minuteMaxOriginal: null, | ||
125 | + secondMaxOriginal: null, | ||
126 | + millisecMaxOriginal: null, | ||
127 | + ampm: '', | ||
128 | + formattedDate: '', | ||
129 | + formattedTime: '', | ||
130 | + formattedDateTime: '', | ||
131 | + timezoneList: null, | ||
132 | + | ||
133 | + /* Override the default settings for all instances of the time picker. | ||
134 | + @param settings object - the new settings to use as defaults (anonymous object) | ||
135 | + @return the manager object */ | ||
136 | + setDefaults: function(settings) { | ||
137 | + extendRemove(this._defaults, settings || {}); | ||
138 | + return this; | ||
139 | + }, | ||
140 | + | ||
141 | + //######################################################################## | ||
142 | + // Create a new Timepicker instance | ||
143 | + //######################################################################## | ||
144 | + _newInst: function($input, o) { | ||
145 | + var tp_inst = new Timepicker(), | ||
146 | + inlineSettings = {}; | ||
147 | + | ||
148 | + for (var attrName in this._defaults) { | ||
149 | + var attrValue = $input.attr('time:' + attrName); | ||
150 | + if (attrValue) { | ||
151 | + try { | ||
152 | + inlineSettings[attrName] = eval(attrValue); | ||
153 | + } catch (err) { | ||
154 | + inlineSettings[attrName] = attrValue; | ||
155 | + } | ||
156 | + } | ||
157 | + } | ||
158 | + tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, o, { | ||
159 | + beforeShow: function(input, dp_inst) { | ||
160 | + if ($.isFunction(o.beforeShow)) { | ||
161 | + return o.beforeShow(input, dp_inst, tp_inst); | ||
162 | + } | ||
163 | + }, | ||
164 | + onChangeMonthYear: function(year, month, dp_inst) { | ||
165 | + // Update the time as well : this prevents the time from disappearing from the $input field. | ||
166 | + tp_inst._updateDateTime(dp_inst); | ||
167 | + if ($.isFunction(o.onChangeMonthYear)) { | ||
168 | + o.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst); | ||
169 | + } | ||
170 | + }, | ||
171 | + onClose: function(dateText, dp_inst) { | ||
172 | + if (tp_inst.timeDefined === true && $input.val() !== '') { | ||
173 | + tp_inst._updateDateTime(dp_inst); | ||
174 | + } | ||
175 | + if ($.isFunction(o.onClose)) { | ||
176 | + o.onClose.call($input[0], dateText, dp_inst, tp_inst); | ||
177 | + } | ||
178 | + }, | ||
179 | + timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker'); | ||
180 | + }); | ||
181 | + tp_inst.amNames = $.map(tp_inst._defaults.amNames, function(val) { return val.toUpperCase(); }); | ||
182 | + tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function(val) { return val.toUpperCase(); }); | ||
183 | + | ||
184 | + if (tp_inst._defaults.timezoneList === null) { | ||
185 | + var timezoneList = []; | ||
186 | + for (var i = -11; i <= 12; i++) { | ||
187 | + timezoneList.push((i >= 0 ? '+' : '-') + ('0' + Math.abs(i).toString()).slice(-2) + '00'); | ||
188 | + } | ||
189 | + if (tp_inst._defaults.timezoneIso8601) { | ||
190 | + timezoneList = $.map(timezoneList, function(val) { | ||
191 | + return val == '+0000' ? 'Z' : (val.substring(0, 3) + ':' + val.substring(3)); | ||
192 | + }); | ||
193 | + } | ||
194 | + tp_inst._defaults.timezoneList = timezoneList; | ||
195 | + } | ||
196 | + | ||
197 | + tp_inst.timezone = tp_inst._defaults.timezone; | ||
198 | + tp_inst.hour = tp_inst._defaults.hour; | ||
199 | + tp_inst.minute = tp_inst._defaults.minute; | ||
200 | + tp_inst.second = tp_inst._defaults.second; | ||
201 | + tp_inst.millisec = tp_inst._defaults.millisec; | ||
202 | + tp_inst.ampm = ''; | ||
203 | + tp_inst.$input = $input; | ||
204 | + | ||
205 | + if (o.altField) { | ||
206 | + tp_inst.$altInput = $(o.altField) | ||
207 | + .css({ cursor: 'pointer' }) | ||
208 | + .focus(function(){ $input.trigger("focus"); }); | ||
209 | + } | ||
210 | + | ||
211 | + if(tp_inst._defaults.minDate===0 || tp_inst._defaults.minDateTime===0) | ||
212 | + { | ||
213 | + tp_inst._defaults.minDate=new Date(); | ||
214 | + } | ||
215 | + if(tp_inst._defaults.maxDate===0 || tp_inst._defaults.maxDateTime===0) | ||
216 | + { | ||
217 | + tp_inst._defaults.maxDate=new Date(); | ||
218 | + } | ||
219 | + | ||
220 | + // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime.. | ||
221 | + if(tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) { | ||
222 | + tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime()); | ||
223 | + } | ||
224 | + if(tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) { | ||
225 | + tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime()); | ||
226 | + } | ||
227 | + if(tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) { | ||
228 | + tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime()); | ||
229 | + } | ||
230 | + if(tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) { | ||
231 | + tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime()); | ||
232 | + } | ||
233 | + return tp_inst; | ||
234 | + }, | ||
235 | + | ||
236 | + //######################################################################## | ||
237 | + // add our sliders to the calendar | ||
238 | + //######################################################################## | ||
239 | + _addTimePicker: function(dp_inst) { | ||
240 | + var currDT = (this.$altInput && this._defaults.altFieldTimeOnly) ? | ||
241 | + this.$input.val() + ' ' + this.$altInput.val() : | ||
242 | + this.$input.val(); | ||
243 | + | ||
244 | + this.timeDefined = this._parseTime(currDT); | ||
245 | + this._limitMinMaxDateTime(dp_inst, false); | ||
246 | + this._injectTimePicker(); | ||
247 | + }, | ||
248 | + | ||
249 | + //######################################################################## | ||
250 | + // parse the time string from input value or _setTime | ||
251 | + //######################################################################## | ||
252 | + _parseTime: function(timeString, withDate) { | ||
253 | + if (!this.inst) { | ||
254 | + this.inst = $.datepicker._getInst(this.$input[0]); | ||
255 | + } | ||
256 | + | ||
257 | + if (withDate || !this._defaults.timeOnly) | ||
258 | + { | ||
259 | + var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat'); | ||
260 | + try { | ||
261 | + var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults); | ||
262 | + if (!parseRes.timeObj) { return false; } | ||
263 | + $.extend(this, parseRes.timeObj); | ||
264 | + } catch (err) | ||
265 | + { | ||
266 | + return false; | ||
267 | + } | ||
268 | + return true; | ||
269 | + } | ||
270 | + else | ||
271 | + { | ||
272 | + var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults); | ||
273 | + if(!timeObj) { return false; } | ||
274 | + $.extend(this, timeObj); | ||
275 | + return true; | ||
276 | + } | ||
277 | + }, | ||
278 | + | ||
279 | + //######################################################################## | ||
280 | + // generate and inject html for timepicker into ui datepicker | ||
281 | + //######################################################################## | ||
282 | + _injectTimePicker: function() { | ||
283 | + var $dp = this.inst.dpDiv, | ||
284 | + o = this._defaults, | ||
285 | + tp_inst = this, | ||
286 | + // Added by Peter Medeiros: | ||
287 | + // - Figure out what the hour/minute/second max should be based on the step values. | ||
288 | + // - Example: if stepMinute is 15, then minMax is 45. | ||
289 | + hourMax = parseInt((o.hourMax - ((o.hourMax - o.hourMin) % o.stepHour)) ,10), | ||
290 | + minMax = parseInt((o.minuteMax - ((o.minuteMax - o.minuteMin) % o.stepMinute)) ,10), | ||
291 | + secMax = parseInt((o.secondMax - ((o.secondMax - o.secondMin) % o.stepSecond)) ,10), | ||
292 | + millisecMax = parseInt((o.millisecMax - ((o.millisecMax - o.millisecMin) % o.stepMillisec)) ,10), | ||
293 | + dp_id = this.inst.id.toString().replace(/([^A-Za-z0-9_])/g, ''); | ||
294 | + | ||
295 | + // Prevent displaying twice | ||
296 | + //if ($dp.find("div#ui-timepicker-div-"+ dp_id).length === 0) { | ||
297 | + if ($dp.find("div#ui-timepicker-div-"+ dp_id).length === 0 && o.showTimepicker) { | ||
298 | + var noDisplay = ' style="display:none;"', | ||
299 | + html = '<div class="ui-timepicker-div" id="ui-timepicker-div-' + dp_id + '"><dl>' + | ||
300 | + '<dt class="ui_tpicker_time_label" id="ui_tpicker_time_label_' + dp_id + '"' + | ||
301 | + ((o.showTime) ? '' : noDisplay) + '>' + o.timeText + '</dt>' + | ||
302 | + '<dd class="ui_tpicker_time" id="ui_tpicker_time_' + dp_id + '"' + | ||
303 | + ((o.showTime) ? '' : noDisplay) + '></dd>' + | ||
304 | + '<dt class="ui_tpicker_hour_label" id="ui_tpicker_hour_label_' + dp_id + '"' + | ||
305 | + ((o.showHour) ? '' : noDisplay) + '>' + o.hourText + '</dt>', | ||
306 | + hourGridSize = 0, | ||
307 | + minuteGridSize = 0, | ||
308 | + secondGridSize = 0, | ||
309 | + millisecGridSize = 0, | ||
310 | + size = null; | ||
311 | + | ||
312 | + // Hours | ||
313 | + html += '<dd class="ui_tpicker_hour"><div id="ui_tpicker_hour_' + dp_id + '"' + | ||
314 | + ((o.showHour) ? '' : noDisplay) + '></div>'; | ||
315 | + if (o.showHour && o.hourGrid > 0) { | ||
316 | + html += '<div style="padding-left: 1px"><table class="ui-tpicker-grid-label"><tr>'; | ||
317 | + | ||
318 | + for (var h = o.hourMin; h <= hourMax; h += parseInt(o.hourGrid,10)) { | ||
319 | + hourGridSize++; | ||
320 | + var tmph = (o.ampm && h > 12) ? h-12 : h; | ||
321 | + if (tmph < 10) { tmph = '0' + tmph; } | ||
322 | + if (o.ampm) { | ||
323 | + if (h === 0) { | ||
324 | + tmph = 12 +'a'; | ||
325 | + } else { | ||
326 | + if (h < 12) { tmph += 'a'; } | ||
327 | + else { tmph += 'p'; } | ||
328 | + } | ||
329 | + } | ||
330 | + html += '<td>' + tmph + '</td>'; | ||
331 | + } | ||
332 | + | ||
333 | + html += '</tr></table></div>'; | ||
334 | + } | ||
335 | + html += '</dd>'; | ||
336 | + | ||
337 | + // Minutes | ||
338 | + html += '<dt class="ui_tpicker_minute_label" id="ui_tpicker_minute_label_' + dp_id + '"' + | ||
339 | + ((o.showMinute) ? '' : noDisplay) + '>' + o.minuteText + '</dt>'+ | ||
340 | + '<dd class="ui_tpicker_minute"><div id="ui_tpicker_minute_' + dp_id + '"' + | ||
341 | + ((o.showMinute) ? '' : noDisplay) + '></div>'; | ||
342 | + | ||
343 | + if (o.showMinute && o.minuteGrid > 0) { | ||
344 | + html += '<div style="padding-left: 1px"><table class="ui-tpicker-grid-label"><tr>'; | ||
345 | + | ||
346 | + for (var m = o.minuteMin; m <= minMax; m += parseInt(o.minuteGrid,10)) { | ||
347 | + minuteGridSize++; | ||
348 | + html += '<td>' + ((m < 10) ? '0' : '') + m + '</td>'; | ||
349 | + } | ||
350 | + | ||
351 | + html += '</tr></table></div>'; | ||
352 | + } | ||
353 | + html += '</dd>'; | ||
354 | + | ||
355 | + // Seconds | ||
356 | + html += '<dt class="ui_tpicker_second_label" id="ui_tpicker_second_label_' + dp_id + '"' + | ||
357 | + ((o.showSecond) ? '' : noDisplay) + '>' + o.secondText + '</dt>'+ | ||
358 | + '<dd class="ui_tpicker_second"><div id="ui_tpicker_second_' + dp_id + '"'+ | ||
359 | + ((o.showSecond) ? '' : noDisplay) + '></div>'; | ||
360 | + | ||
361 | + if (o.showSecond && o.secondGrid > 0) { | ||
362 | + html += '<div style="padding-left: 1px"><table><tr>'; | ||
363 | + | ||
364 | + for (var s = o.secondMin; s <= secMax; s += parseInt(o.secondGrid,10)) { | ||
365 | + secondGridSize++; | ||
366 | + html += '<td>' + ((s < 10) ? '0' : '') + s + '</td>'; | ||
367 | + } | ||
368 | + | ||
369 | + html += '</tr></table></div>'; | ||
370 | + } | ||
371 | + html += '</dd>'; | ||
372 | + | ||
373 | + // Milliseconds | ||
374 | + html += '<dt class="ui_tpicker_millisec_label" id="ui_tpicker_millisec_label_' + dp_id + '"' + | ||
375 | + ((o.showMillisec) ? '' : noDisplay) + '>' + o.millisecText + '</dt>'+ | ||
376 | + '<dd class="ui_tpicker_millisec"><div id="ui_tpicker_millisec_' + dp_id + '"'+ | ||
377 | + ((o.showMillisec) ? '' : noDisplay) + '></div>'; | ||
378 | + | ||
379 | + if (o.showMillisec && o.millisecGrid > 0) { | ||
380 | + html += '<div style="padding-left: 1px"><table><tr>'; | ||
381 | + | ||
382 | + for (var l = o.millisecMin; l <= millisecMax; l += parseInt(o.millisecGrid,10)) { | ||
383 | + millisecGridSize++; | ||
384 | + html += '<td>' + ((l < 10) ? '0' : '') + l + '</td>'; | ||
385 | + } | ||
386 | + | ||
387 | + html += '</tr></table></div>'; | ||
388 | + } | ||
389 | + html += '</dd>'; | ||
390 | + | ||
391 | + // Timezone | ||
392 | + html += '<dt class="ui_tpicker_timezone_label" id="ui_tpicker_timezone_label_' + dp_id + '"' + | ||
393 | + ((o.showTimezone) ? '' : noDisplay) + '>' + o.timezoneText + '</dt>'; | ||
394 | + html += '<dd class="ui_tpicker_timezone" id="ui_tpicker_timezone_' + dp_id + '"' + | ||
395 | + ((o.showTimezone) ? '' : noDisplay) + '></dd>'; | ||
396 | + | ||
397 | + html += '</dl></div>'; | ||
398 | + var $tp = $(html); | ||
399 | + | ||
400 | + // if we only want time picker... | ||
401 | + if (o.timeOnly === true) { | ||
402 | + $tp.prepend( | ||
403 | + '<div class="ui-widget-header ui-helper-clearfix ui-corner-all">' + | ||
404 | + '<div class="ui-datepicker-title">' + o.timeOnlyTitle + '</div>' + | ||
405 | + '</div>'); | ||
406 | + $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide(); | ||
407 | + } | ||
408 | + | ||
409 | + this.hour_slider = $tp.find('#ui_tpicker_hour_'+ dp_id).slider({ | ||
410 | + orientation: "horizontal", | ||
411 | + value: this.hour, | ||
412 | + min: o.hourMin, | ||
413 | + max: hourMax, | ||
414 | + step: o.stepHour, | ||
415 | + slide: function(event, ui) { | ||
416 | + tp_inst.hour_slider.slider( "option", "value", ui.value); | ||
417 | + tp_inst._onTimeChange(); | ||
418 | + } | ||
419 | + }); | ||
420 | + | ||
421 | + | ||
422 | + // Updated by Peter Medeiros: | ||
423 | + // - Pass in Event and UI instance into slide function | ||
424 | + this.minute_slider = $tp.find('#ui_tpicker_minute_'+ dp_id).slider({ | ||
425 | + orientation: "horizontal", | ||
426 | + value: this.minute, | ||
427 | + min: o.minuteMin, | ||
428 | + max: minMax, | ||
429 | + step: o.stepMinute, | ||
430 | + slide: function(event, ui) { | ||
431 | + tp_inst.minute_slider.slider( "option", "value", ui.value); | ||
432 | + tp_inst._onTimeChange(); | ||
433 | + } | ||
434 | + }); | ||
435 | + | ||
436 | + this.second_slider = $tp.find('#ui_tpicker_second_'+ dp_id).slider({ | ||
437 | + orientation: "horizontal", | ||
438 | + value: this.second, | ||
439 | + min: o.secondMin, | ||
440 | + max: secMax, | ||
441 | + step: o.stepSecond, | ||
442 | + slide: function(event, ui) { | ||
443 | + tp_inst.second_slider.slider( "option", "value", ui.value); | ||
444 | + tp_inst._onTimeChange(); | ||
445 | + } | ||
446 | + }); | ||
447 | + | ||
448 | + this.millisec_slider = $tp.find('#ui_tpicker_millisec_'+ dp_id).slider({ | ||
449 | + orientation: "horizontal", | ||
450 | + value: this.millisec, | ||
451 | + min: o.millisecMin, | ||
452 | + max: millisecMax, | ||
453 | + step: o.stepMillisec, | ||
454 | + slide: function(event, ui) { | ||
455 | + tp_inst.millisec_slider.slider( "option", "value", ui.value); | ||
456 | + tp_inst._onTimeChange(); | ||
457 | + } | ||
458 | + }); | ||
459 | + | ||
460 | + this.timezone_select = $tp.find('#ui_tpicker_timezone_'+ dp_id).append('<select></select>').find("select"); | ||
461 | + $.fn.append.apply(this.timezone_select, | ||
462 | + $.map(o.timezoneList, function(val, idx) { | ||
463 | + return $("<option />") | ||
464 | + .val(typeof val == "object" ? val.value : val) | ||
465 | + .text(typeof val == "object" ? val.label : val); | ||
466 | + }) | ||
467 | + ); | ||
468 | + if (typeof(this.timezone) != "undefined" && this.timezone !== null && this.timezone !== "") { | ||
469 | + var local_date = new Date(this.inst.selectedYear, this.inst.selectedMonth, this.inst.selectedDay, 12); | ||
470 | + var local_timezone = timeZoneString(local_date); | ||
471 | + if (local_timezone == this.timezone) { | ||
472 | + selectLocalTimeZone(tp_inst); | ||
473 | + } else { | ||
474 | + this.timezone_select.val(this.timezone); | ||
475 | + } | ||
476 | + } else { | ||
477 | + if (typeof(this.hour) != "undefined" && this.hour !== null && this.hour !== "") { | ||
478 | + this.timezone_select.val(o.defaultTimezone); | ||
479 | + } else { | ||
480 | + selectLocalTimeZone(tp_inst); | ||
481 | + } | ||
482 | + } | ||
483 | + this.timezone_select.change(function() { | ||
484 | + tp_inst._defaults.useLocalTimezone = false; | ||
485 | + tp_inst._onTimeChange(); | ||
486 | + }); | ||
487 | + | ||
488 | + // Add grid functionality | ||
489 | + if (o.showHour && o.hourGrid > 0) { | ||
490 | + size = 100 * hourGridSize * o.hourGrid / (hourMax - o.hourMin); | ||
491 | + | ||
492 | + $tp.find(".ui_tpicker_hour table").css({ | ||
493 | + width: size + "%", | ||
494 | + marginLeft: (size / (-2 * hourGridSize)) + "%", | ||
495 | + borderCollapse: 'collapse' | ||
496 | + }).find("td").each( function(index) { | ||
497 | + $(this).click(function() { | ||
498 | + var h = $(this).html(); | ||
499 | + if(o.ampm) { | ||
500 | + var ap = h.substring(2).toLowerCase(), | ||
501 | + aph = parseInt(h.substring(0,2), 10); | ||
502 | + if (ap == 'a') { | ||
503 | + if (aph == 12) { h = 0; } | ||
504 | + else { h = aph; } | ||
505 | + } else if (aph == 12) { h = 12; } | ||
506 | + else { h = aph + 12; } | ||
507 | + } | ||
508 | + tp_inst.hour_slider.slider("option", "value", h); | ||
509 | + tp_inst._onTimeChange(); | ||
510 | + tp_inst._onSelectHandler(); | ||
511 | + }).css({ | ||
512 | + cursor: 'pointer', | ||
513 | + width: (100 / hourGridSize) + '%', | ||
514 | + textAlign: 'center', | ||
515 | + overflow: 'hidden' | ||
516 | + }); | ||
517 | + }); | ||
518 | + } | ||
519 | + | ||
520 | + if (o.showMinute && o.minuteGrid > 0) { | ||
521 | + size = 100 * minuteGridSize * o.minuteGrid / (minMax - o.minuteMin); | ||
522 | + $tp.find(".ui_tpicker_minute table").css({ | ||
523 | + width: size + "%", | ||
524 | + marginLeft: (size / (-2 * minuteGridSize)) + "%", | ||
525 | + borderCollapse: 'collapse' | ||
526 | + }).find("td").each(function(index) { | ||
527 | + $(this).click(function() { | ||
528 | + tp_inst.minute_slider.slider("option", "value", $(this).html()); | ||
529 | + tp_inst._onTimeChange(); | ||
530 | + tp_inst._onSelectHandler(); | ||
531 | + }).css({ | ||
532 | + cursor: 'pointer', | ||
533 | + width: (100 / minuteGridSize) + '%', | ||
534 | + textAlign: 'center', | ||
535 | + overflow: 'hidden' | ||
536 | + }); | ||
537 | + }); | ||
538 | + } | ||
539 | + | ||
540 | + if (o.showSecond && o.secondGrid > 0) { | ||
541 | + $tp.find(".ui_tpicker_second table").css({ | ||
542 | + width: size + "%", | ||
543 | + marginLeft: (size / (-2 * secondGridSize)) + "%", | ||
544 | + borderCollapse: 'collapse' | ||
545 | + }).find("td").each(function(index) { | ||
546 | + $(this).click(function() { | ||
547 | + tp_inst.second_slider.slider("option", "value", $(this).html()); | ||
548 | + tp_inst._onTimeChange(); | ||
549 | + tp_inst._onSelectHandler(); | ||
550 | + }).css({ | ||
551 | + cursor: 'pointer', | ||
552 | + width: (100 / secondGridSize) + '%', | ||
553 | + textAlign: 'center', | ||
554 | + overflow: 'hidden' | ||
555 | + }); | ||
556 | + }); | ||
557 | + } | ||
558 | + | ||
559 | + if (o.showMillisec && o.millisecGrid > 0) { | ||
560 | + $tp.find(".ui_tpicker_millisec table").css({ | ||
561 | + width: size + "%", | ||
562 | + marginLeft: (size / (-2 * millisecGridSize)) + "%", | ||
563 | + borderCollapse: 'collapse' | ||
564 | + }).find("td").each(function(index) { | ||
565 | + $(this).click(function() { | ||
566 | + tp_inst.millisec_slider.slider("option", "value", $(this).html()); | ||
567 | + tp_inst._onTimeChange(); | ||
568 | + tp_inst._onSelectHandler(); | ||
569 | + }).css({ | ||
570 | + cursor: 'pointer', | ||
571 | + width: (100 / millisecGridSize) + '%', | ||
572 | + textAlign: 'center', | ||
573 | + overflow: 'hidden' | ||
574 | + }); | ||
575 | + }); | ||
576 | + } | ||
577 | + | ||
578 | + var $buttonPanel = $dp.find('.ui-datepicker-buttonpane'); | ||
579 | + if ($buttonPanel.length) { $buttonPanel.before($tp); } | ||
580 | + else { $dp.append($tp); } | ||
581 | + | ||
582 | + this.$timeObj = $tp.find('#ui_tpicker_time_'+ dp_id); | ||
583 | + | ||
584 | + if (this.inst !== null) { | ||
585 | + var timeDefined = this.timeDefined; | ||
586 | + this._onTimeChange(); | ||
587 | + this.timeDefined = timeDefined; | ||
588 | + } | ||
589 | + | ||
590 | + //Emulate datepicker onSelect behavior. Call on slidestop. | ||
591 | + var onSelectDelegate = function() { | ||
592 | + tp_inst._onSelectHandler(); | ||
593 | + }; | ||
594 | + this.hour_slider.bind('slidestop',onSelectDelegate); | ||
595 | + this.minute_slider.bind('slidestop',onSelectDelegate); | ||
596 | + this.second_slider.bind('slidestop',onSelectDelegate); | ||
597 | + this.millisec_slider.bind('slidestop',onSelectDelegate); | ||
598 | + | ||
599 | + // slideAccess integration: http://trentrichardson.com/2011/11/11/jquery-ui-sliders-and-touch-accessibility/ | ||
600 | + if (this._defaults.addSliderAccess){ | ||
601 | + var sliderAccessArgs = this._defaults.sliderAccessArgs; | ||
602 | + setTimeout(function(){ // fix for inline mode | ||
603 | + if($tp.find('.ui-slider-access').length === 0){ | ||
604 | + $tp.find('.ui-slider:visible').sliderAccess(sliderAccessArgs); | ||
605 | + | ||
606 | + // fix any grids since sliders are shorter | ||
607 | + var sliderAccessWidth = $tp.find('.ui-slider-access:eq(0)').outerWidth(true); | ||
608 | + if(sliderAccessWidth){ | ||
609 | + $tp.find('table:visible').each(function(){ | ||
610 | + var $g = $(this), | ||
611 | + oldWidth = $g.outerWidth(), | ||
612 | + oldMarginLeft = $g.css('marginLeft').toString().replace('%',''), | ||
613 | + newWidth = oldWidth - sliderAccessWidth, | ||
614 | + newMarginLeft = ((oldMarginLeft * newWidth)/oldWidth) + '%'; | ||
615 | + | ||
616 | + $g.css({ width: newWidth, marginLeft: newMarginLeft }); | ||
617 | + }); | ||
618 | + } | ||
619 | + } | ||
620 | + },0); | ||
621 | + } | ||
622 | + // end slideAccess integration | ||
623 | + | ||
624 | + } | ||
625 | + }, | ||
626 | + | ||
627 | + //######################################################################## | ||
628 | + // This function tries to limit the ability to go outside the | ||
629 | + // min/max date range | ||
630 | + //######################################################################## | ||
631 | + _limitMinMaxDateTime: function(dp_inst, adjustSliders){ | ||
632 | + var o = this._defaults, | ||
633 | + dp_date = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay); | ||
634 | + | ||
635 | + if(!this._defaults.showTimepicker) { return; } // No time so nothing to check here | ||
636 | + | ||
637 | + if($.datepicker._get(dp_inst, 'minDateTime') !== null && $.datepicker._get(dp_inst, 'minDateTime') !== undefined && dp_date){ | ||
638 | + var minDateTime = $.datepicker._get(dp_inst, 'minDateTime'), | ||
639 | + minDateTimeDate = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0); | ||
640 | + | ||
641 | + if(this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null || this.millisecMinOriginal === null){ | ||
642 | + this.hourMinOriginal = o.hourMin; | ||
643 | + this.minuteMinOriginal = o.minuteMin; | ||
644 | + this.secondMinOriginal = o.secondMin; | ||
645 | + this.millisecMinOriginal = o.millisecMin; | ||
646 | + } | ||
647 | + | ||
648 | + if(dp_inst.settings.timeOnly || minDateTimeDate.getTime() == dp_date.getTime()) { | ||
649 | + this._defaults.hourMin = minDateTime.getHours(); | ||
650 | + if (this.hour <= this._defaults.hourMin) { | ||
651 | + this.hour = this._defaults.hourMin; | ||
652 | + this._defaults.minuteMin = minDateTime.getMinutes(); | ||
653 | + if (this.minute <= this._defaults.minuteMin) { | ||
654 | + this.minute = this._defaults.minuteMin; | ||
655 | + this._defaults.secondMin = minDateTime.getSeconds(); | ||
656 | + } else if (this.second <= this._defaults.secondMin){ | ||
657 | + this.second = this._defaults.secondMin; | ||
658 | + this._defaults.millisecMin = minDateTime.getMilliseconds(); | ||
659 | + } else { | ||
660 | + if(this.millisec < this._defaults.millisecMin) { | ||
661 | + this.millisec = this._defaults.millisecMin; | ||
662 | + } | ||
663 | + this._defaults.millisecMin = this.millisecMinOriginal; | ||
664 | + } | ||
665 | + } else { | ||
666 | + this._defaults.minuteMin = this.minuteMinOriginal; | ||
667 | + this._defaults.secondMin = this.secondMinOriginal; | ||
668 | + this._defaults.millisecMin = this.millisecMinOriginal; | ||
669 | + } | ||
670 | + }else{ | ||
671 | + this._defaults.hourMin = this.hourMinOriginal; | ||
672 | + this._defaults.minuteMin = this.minuteMinOriginal; | ||
673 | + this._defaults.secondMin = this.secondMinOriginal; | ||
674 | + this._defaults.millisecMin = this.millisecMinOriginal; | ||
675 | + } | ||
676 | + } | ||
677 | + | ||
678 | + if($.datepicker._get(dp_inst, 'maxDateTime') !== null && $.datepicker._get(dp_inst, 'maxDateTime') !== undefined && dp_date){ | ||
679 | + var maxDateTime = $.datepicker._get(dp_inst, 'maxDateTime'), | ||
680 | + maxDateTimeDate = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0); | ||
681 | + | ||
682 | + if(this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null){ | ||
683 | + this.hourMaxOriginal = o.hourMax; | ||
684 | + this.minuteMaxOriginal = o.minuteMax; | ||
685 | + this.secondMaxOriginal = o.secondMax; | ||
686 | + this.millisecMaxOriginal = o.millisecMax; | ||
687 | + } | ||
688 | + | ||
689 | + if(dp_inst.settings.timeOnly || maxDateTimeDate.getTime() == dp_date.getTime()){ | ||
690 | + this._defaults.hourMax = maxDateTime.getHours(); | ||
691 | + if (this.hour >= this._defaults.hourMax) { | ||
692 | + this.hour = this._defaults.hourMax; | ||
693 | + this._defaults.minuteMax = maxDateTime.getMinutes(); | ||
694 | + if (this.minute >= this._defaults.minuteMax) { | ||
695 | + this.minute = this._defaults.minuteMax; | ||
696 | + this._defaults.secondMax = maxDateTime.getSeconds(); | ||
697 | + } else if (this.second >= this._defaults.secondMax) { | ||
698 | + this.second = this._defaults.secondMax; | ||
699 | + this._defaults.millisecMax = maxDateTime.getMilliseconds(); | ||
700 | + } else { | ||
701 | + if(this.millisec > this._defaults.millisecMax) { this.millisec = this._defaults.millisecMax; } | ||
702 | + this._defaults.millisecMax = this.millisecMaxOriginal; | ||
703 | + } | ||
704 | + } else { | ||
705 | + this._defaults.minuteMax = this.minuteMaxOriginal; | ||
706 | + this._defaults.secondMax = this.secondMaxOriginal; | ||
707 | + this._defaults.millisecMax = this.millisecMaxOriginal; | ||
708 | + } | ||
709 | + }else{ | ||
710 | + this._defaults.hourMax = this.hourMaxOriginal; | ||
711 | + this._defaults.minuteMax = this.minuteMaxOriginal; | ||
712 | + this._defaults.secondMax = this.secondMaxOriginal; | ||
713 | + this._defaults.millisecMax = this.millisecMaxOriginal; | ||
714 | + } | ||
715 | + } | ||
716 | + | ||
717 | + if(adjustSliders !== undefined && adjustSliders === true){ | ||
718 | + var hourMax = parseInt((this._defaults.hourMax - ((this._defaults.hourMax - this._defaults.hourMin) % this._defaults.stepHour)) ,10), | ||
719 | + minMax = parseInt((this._defaults.minuteMax - ((this._defaults.minuteMax - this._defaults.minuteMin) % this._defaults.stepMinute)) ,10), | ||
720 | + secMax = parseInt((this._defaults.secondMax - ((this._defaults.secondMax - this._defaults.secondMin) % this._defaults.stepSecond)) ,10), | ||
721 | + millisecMax = parseInt((this._defaults.millisecMax - ((this._defaults.millisecMax - this._defaults.millisecMin) % this._defaults.stepMillisec)) ,10); | ||
722 | + | ||
723 | + if(this.hour_slider) { | ||
724 | + this.hour_slider.slider("option", { min: this._defaults.hourMin, max: hourMax }).slider('value', this.hour); | ||
725 | + } | ||
726 | + if(this.minute_slider) { | ||
727 | + this.minute_slider.slider("option", { min: this._defaults.minuteMin, max: minMax }).slider('value', this.minute); | ||
728 | + } | ||
729 | + if(this.second_slider){ | ||
730 | + this.second_slider.slider("option", { min: this._defaults.secondMin, max: secMax }).slider('value', this.second); | ||
731 | + } | ||
732 | + if(this.millisec_slider) { | ||
733 | + this.millisec_slider.slider("option", { min: this._defaults.millisecMin, max: millisecMax }).slider('value', this.millisec); | ||
734 | + } | ||
735 | + } | ||
736 | + | ||
737 | + }, | ||
738 | + | ||
739 | + | ||
740 | + //######################################################################## | ||
741 | + // when a slider moves, set the internal time... | ||
742 | + // on time change is also called when the time is updated in the text field | ||
743 | + //######################################################################## | ||
744 | + _onTimeChange: function() { | ||
745 | + var hour = (this.hour_slider) ? this.hour_slider.slider('value') : false, | ||
746 | + minute = (this.minute_slider) ? this.minute_slider.slider('value') : false, | ||
747 | + second = (this.second_slider) ? this.second_slider.slider('value') : false, | ||
748 | + millisec = (this.millisec_slider) ? this.millisec_slider.slider('value') : false, | ||
749 | + timezone = (this.timezone_select) ? this.timezone_select.val() : false, | ||
750 | + o = this._defaults; | ||
751 | + | ||
752 | + if (typeof(hour) == 'object') { hour = false; } | ||
753 | + if (typeof(minute) == 'object') { minute = false; } | ||
754 | + if (typeof(second) == 'object') { second = false; } | ||
755 | + if (typeof(millisec) == 'object') { millisec = false; } | ||
756 | + if (typeof(timezone) == 'object') { timezone = false; } | ||
757 | + | ||
758 | + if (hour !== false) { hour = parseInt(hour,10); } | ||
759 | + if (minute !== false) { minute = parseInt(minute,10); } | ||
760 | + if (second !== false) { second = parseInt(second,10); } | ||
761 | + if (millisec !== false) { millisec = parseInt(millisec,10); } | ||
762 | + | ||
763 | + var ampm = o[hour < 12 ? 'amNames' : 'pmNames'][0]; | ||
764 | + | ||
765 | + // If the update was done in the input field, the input field should not be updated. | ||
766 | + // If the update was done using the sliders, update the input field. | ||
767 | + var hasChanged = (hour != this.hour || minute != this.minute || | ||
768 | + second != this.second || millisec != this.millisec || | ||
769 | + (this.ampm.length > 0 && | ||
770 | + (hour < 12) != ($.inArray(this.ampm.toUpperCase(), this.amNames) !== -1)) || | ||
771 | + timezone != this.timezone); | ||
772 | + | ||
773 | + if (hasChanged) { | ||
774 | + | ||
775 | + if (hour !== false) { this.hour = hour; } | ||
776 | + if (minute !== false) { this.minute = minute; } | ||
777 | + if (second !== false) { this.second = second; } | ||
778 | + if (millisec !== false) { this.millisec = millisec; } | ||
779 | + if (timezone !== false) { this.timezone = timezone; } | ||
780 | + | ||
781 | + if (!this.inst) { this.inst = $.datepicker._getInst(this.$input[0]); } | ||
782 | + | ||
783 | + this._limitMinMaxDateTime(this.inst, true); | ||
784 | + } | ||
785 | + if (o.ampm) { this.ampm = ampm; } | ||
786 | + | ||
787 | + //this._formatTime(); | ||
788 | + this.formattedTime = $.datepicker.formatTime(this._defaults.timeFormat, this, this._defaults); | ||
789 | + if (this.$timeObj) { this.$timeObj.text(this.formattedTime + o.timeSuffix); } | ||
790 | + this.timeDefined = true; | ||
791 | + if (hasChanged) { this._updateDateTime(); } | ||
792 | + }, | ||
793 | + | ||
794 | + //######################################################################## | ||
795 | + // call custom onSelect. | ||
796 | + // bind to sliders slidestop, and grid click. | ||
797 | + //######################################################################## | ||
798 | + _onSelectHandler: function() { | ||
799 | + var onSelect = this._defaults.onSelect; | ||
800 | + var inputEl = this.$input ? this.$input[0] : null; | ||
801 | + if (onSelect && inputEl) { | ||
802 | + onSelect.apply(inputEl, [this.formattedDateTime, this]); | ||
803 | + } | ||
804 | + }, | ||
805 | + | ||
806 | + //######################################################################## | ||
807 | + // left for any backwards compatibility | ||
808 | + //######################################################################## | ||
809 | + _formatTime: function(time, format) { | ||
810 | + time = time || { hour: this.hour, minute: this.minute, second: this.second, millisec: this.millisec, ampm: this.ampm, timezone: this.timezone }; | ||
811 | + var tmptime = (format || this._defaults.timeFormat).toString(); | ||
812 | + | ||
813 | + tmptime = $.datepicker.formatTime(tmptime, time, this._defaults); | ||
814 | + | ||
815 | + if (arguments.length) { return tmptime; } | ||
816 | + else { this.formattedTime = tmptime; } | ||
817 | + }, | ||
818 | + | ||
819 | + //######################################################################## | ||
820 | + // update our input with the new date time.. | ||
821 | + //######################################################################## | ||
822 | + _updateDateTime: function(dp_inst) { | ||
823 | + dp_inst = this.inst || dp_inst; | ||
824 | + var dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)), | ||
825 | + dateFmt = $.datepicker._get(dp_inst, 'dateFormat'), | ||
826 | + formatCfg = $.datepicker._getFormatConfig(dp_inst), | ||
827 | + timeAvailable = dt !== null && this.timeDefined; | ||
828 | + this.formattedDate = $.datepicker.formatDate(dateFmt, (dt === null ? new Date() : dt), formatCfg); | ||
829 | + var formattedDateTime = this.formattedDate; | ||
830 | + // remove following lines to force every changes in date picker to change the input value | ||
831 | + // Bug descriptions: when an input field has a default value, and click on the field to pop up the date picker. | ||
832 | + // If the user manually empty the value in the input field, the date picker will never change selected value. | ||
833 | + //if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0)) { | ||
834 | + // return; | ||
835 | + //} | ||
836 | + | ||
837 | + if (this._defaults.timeOnly === true) { | ||
838 | + formattedDateTime = this.formattedTime; | ||
839 | + } else if (this._defaults.timeOnly !== true && (this._defaults.alwaysSetTime || timeAvailable)) { | ||
840 | + formattedDateTime += this._defaults.separator + this.formattedTime + this._defaults.timeSuffix; | ||
841 | + } | ||
842 | + | ||
843 | + this.formattedDateTime = formattedDateTime; | ||
844 | + | ||
845 | + if(!this._defaults.showTimepicker) { | ||
846 | + this.$input.val(this.formattedDate); | ||
847 | + } else if (this.$altInput && this._defaults.altFieldTimeOnly === true) { | ||
848 | + this.$altInput.val(this.formattedTime); | ||
849 | + this.$input.val(this.formattedDate); | ||
850 | + } else if(this.$altInput) { | ||
851 | + this.$altInput.val(formattedDateTime); | ||
852 | + this.$input.val(formattedDateTime); | ||
853 | + } else { | ||
854 | + this.$input.val(formattedDateTime); | ||
855 | + } | ||
856 | + | ||
857 | + this.$input.trigger("change"); | ||
858 | + } | ||
859 | + | ||
860 | +}); | ||
861 | + | ||
862 | +$.fn.extend({ | ||
863 | + //######################################################################## | ||
864 | + // shorthand just to use timepicker.. | ||
865 | + //######################################################################## | ||
866 | + timepicker: function(o) { | ||
867 | + o = o || {}; | ||
868 | + var tmp_args = arguments; | ||
869 | + | ||
870 | + if (typeof o == 'object') { tmp_args[0] = $.extend(o, { timeOnly: true }); } | ||
871 | + | ||
872 | + return $(this).each(function() { | ||
873 | + $.fn.datetimepicker.apply($(this), tmp_args); | ||
874 | + }); | ||
875 | + }, | ||
876 | + | ||
877 | + //######################################################################## | ||
878 | + // extend timepicker to datepicker | ||
879 | + //######################################################################## | ||
880 | + datetimepicker: function(o) { | ||
881 | + o = o || {}; | ||
882 | + var tmp_args = arguments; | ||
883 | + | ||
884 | + if (typeof(o) == 'string'){ | ||
885 | + if(o == 'getDate') { | ||
886 | + return $.fn.datepicker.apply($(this[0]), tmp_args); | ||
887 | + } | ||
888 | + else { | ||
889 | + return this.each(function() { | ||
890 | + var $t = $(this); | ||
891 | + $t.datepicker.apply($t, tmp_args); | ||
892 | + }); | ||
893 | + } | ||
894 | + } | ||
895 | + else { | ||
896 | + return this.each(function() { | ||
897 | + var $t = $(this); | ||
898 | + $t.datepicker($.timepicker._newInst($t, o)._defaults); | ||
899 | + }); | ||
900 | + } | ||
901 | + } | ||
902 | +}); | ||
903 | + | ||
904 | +$.datepicker.parseDateTime = function(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) { | ||
905 | + var parseRes = parseDateTimeInternal(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings); | ||
906 | + if (parseRes.timeObj) | ||
907 | + { | ||
908 | + var t = parseRes.timeObj; | ||
909 | + parseRes.date.setHours(t.hour, t.minute, t.second, t.millisec); | ||
910 | + } | ||
911 | + | ||
912 | + return parseRes.date; | ||
913 | +}; | ||
914 | + | ||
915 | +$.datepicker.parseTime = function(timeFormat, timeString, options) { | ||
916 | + | ||
917 | + //######################################################################## | ||
918 | + // pattern for standard and localized AM/PM markers | ||
919 | + //######################################################################## | ||
920 | + var getPatternAmpm = function(amNames, pmNames) { | ||
921 | + var markers = []; | ||
922 | + if (amNames) { | ||
923 | + $.merge(markers, amNames); | ||
924 | + } | ||
925 | + if (pmNames) { | ||
926 | + $.merge(markers, pmNames); | ||
927 | + } | ||
928 | + markers = $.map(markers, function(val) { return val.replace(/[.*+?|()\[\]{}\\]/g, '\\$&'); }); | ||
929 | + return '(' + markers.join('|') + ')?'; | ||
930 | + }; | ||
931 | + | ||
932 | + //######################################################################## | ||
933 | + // figure out position of time elements.. cause js cant do named captures | ||
934 | + //######################################################################## | ||
935 | + var getFormatPositions = function( timeFormat ) { | ||
936 | + var finds = timeFormat.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|l{1}|t{1,2}|z)/g), | ||
937 | + orders = { h: -1, m: -1, s: -1, l: -1, t: -1, z: -1 }; | ||
938 | + | ||
939 | + if (finds) { | ||
940 | + for (var i = 0; i < finds.length; i++) { | ||
941 | + if (orders[finds[i].toString().charAt(0)] == -1) { | ||
942 | + orders[finds[i].toString().charAt(0)] = i + 1; | ||
943 | + } | ||
944 | + } | ||
945 | + } | ||
946 | + return orders; | ||
947 | + }; | ||
948 | + | ||
949 | + var o = extendRemove(extendRemove({}, $.timepicker._defaults), options || {}); | ||
950 | + | ||
951 | + var regstr = '^' + timeFormat.toString() | ||
952 | + .replace(/h{1,2}/ig, '(\\d?\\d)') | ||
953 | + .replace(/m{1,2}/ig, '(\\d?\\d)') | ||
954 | + .replace(/s{1,2}/ig, '(\\d?\\d)') | ||
955 | + .replace(/l{1}/ig, '(\\d?\\d?\\d)') | ||
956 | + .replace(/t{1,2}/ig, getPatternAmpm(o.amNames, o.pmNames)) | ||
957 | + .replace(/z{1}/ig, '(z|[-+]\\d\\d:?\\d\\d)?') | ||
958 | + .replace(/\s/g, '\\s?') + o.timeSuffix + '$', | ||
959 | + order = getFormatPositions(timeFormat), | ||
960 | + ampm = '', | ||
961 | + treg; | ||
962 | + | ||
963 | + treg = timeString.match(new RegExp(regstr, 'i')); | ||
964 | + | ||
965 | + var resTime = {hour: 0, minute: 0, second: 0, millisec: 0}; | ||
966 | + | ||
967 | + if (treg) { | ||
968 | + if (order.t !== -1) { | ||
969 | + if (treg[order.t] === undefined || treg[order.t].length === 0) { | ||
970 | + ampm = ''; | ||
971 | + resTime.ampm = ''; | ||
972 | + } else { | ||
973 | + ampm = $.inArray(treg[order.t], o.amNames) !== -1 ? 'AM' : 'PM'; | ||
974 | + resTime.ampm = o[ampm == 'AM' ? 'amNames' : 'pmNames'][0]; | ||
975 | + } | ||
976 | + } | ||
977 | + | ||
978 | + if (order.h !== -1) { | ||
979 | + if (ampm == 'AM' && treg[order.h] == '12') { | ||
980 | + resTime.hour = 0; // 12am = 0 hour | ||
981 | + } else { | ||
982 | + if (ampm == 'PM' && treg[order.h] != '12') { | ||
983 | + resTime.hour = parseInt(treg[order.h],10) + 12; // 12pm = 12 hour, any other pm = hour + 12 | ||
984 | + } | ||
985 | + else { resTime.hour = Number(treg[order.h]); } | ||
986 | + } | ||
987 | + } | ||
988 | + | ||
989 | + if (order.m !== -1) { resTime.minute = Number(treg[order.m]); } | ||
990 | + if (order.s !== -1) { resTime.second = Number(treg[order.s]); } | ||
991 | + if (order.l !== -1) { resTime.millisec = Number(treg[order.l]); } | ||
992 | + if (order.z !== -1 && treg[order.z] !== undefined) { | ||
993 | + var tz = treg[order.z].toUpperCase(); | ||
994 | + switch (tz.length) { | ||
995 | + case 1: // Z | ||
996 | + tz = o.timezoneIso8601 ? 'Z' : '+0000'; | ||
997 | + break; | ||
998 | + case 5: // +hhmm | ||
999 | + if (o.timezoneIso8601) { | ||
1000 | + tz = tz.substring(1) == '0000' ? | ||
1001 | + 'Z' : | ||
1002 | + tz.substring(0, 3) + ':' + tz.substring(3); | ||
1003 | + } | ||
1004 | + break; | ||
1005 | + case 6: // +hh:mm | ||
1006 | + if (!o.timezoneIso8601) { | ||
1007 | + tz = tz == 'Z' || tz.substring(1) == '00:00' ? | ||
1008 | + '+0000' : | ||
1009 | + tz.replace(/:/, ''); | ||
1010 | + } else { | ||
1011 | + if (tz.substring(1) == '00:00') { | ||
1012 | + tz = 'Z'; | ||
1013 | + } | ||
1014 | + } | ||
1015 | + break; | ||
1016 | + } | ||
1017 | + resTime.timezone = tz; | ||
1018 | + } | ||
1019 | + | ||
1020 | + | ||
1021 | + return resTime; | ||
1022 | + } | ||
1023 | + | ||
1024 | + return false; | ||
1025 | +}; | ||
1026 | + | ||
1027 | +//######################################################################## | ||
1028 | +// format the time all pretty... | ||
1029 | +// format = string format of the time | ||
1030 | +// time = a {}, not a Date() for timezones | ||
1031 | +// options = essentially the regional[].. amNames, pmNames, ampm | ||
1032 | +//######################################################################## | ||
1033 | +$.datepicker.formatTime = function(format, time, options) { | ||
1034 | + options = options || {}; | ||
1035 | + options = $.extend($.timepicker._defaults, options); | ||
1036 | + time = $.extend({hour:0, minute:0, second:0, millisec:0, timezone:'+0000'}, time); | ||
1037 | + | ||
1038 | + var tmptime = format; | ||
1039 | + var ampmName = options.amNames[0]; | ||
1040 | + | ||
1041 | + var hour = parseInt(time.hour, 10); | ||
1042 | + if (options.ampm) { | ||
1043 | + if (hour > 11){ | ||
1044 | + ampmName = options.pmNames[0]; | ||
1045 | + if(hour > 12) { | ||
1046 | + hour = hour % 12; | ||
1047 | + } | ||
1048 | + } | ||
1049 | + if (hour === 0) { | ||
1050 | + hour = 12; | ||
1051 | + } | ||
1052 | + } | ||
1053 | + tmptime = tmptime.replace(/(?:hh?|mm?|ss?|[tT]{1,2}|[lz])/g, function(match) { | ||
1054 | + switch (match.toLowerCase()) { | ||
1055 | + case 'hh': return ('0' + hour).slice(-2); | ||
1056 | + case 'h': return hour; | ||
1057 | + case 'mm': return ('0' + time.minute).slice(-2); | ||
1058 | + case 'm': return time.minute; | ||
1059 | + case 'ss': return ('0' + time.second).slice(-2); | ||
1060 | + case 's': return time.second; | ||
1061 | + case 'l': return ('00' + time.millisec).slice(-3); | ||
1062 | + case 'z': return time.timezone; | ||
1063 | + case 't': case 'tt': | ||
1064 | + if (options.ampm) { | ||
1065 | + if (match.length == 1) { | ||
1066 | + ampmName = ampmName.charAt(0); | ||
1067 | + } | ||
1068 | + return match.charAt(0) == 'T' ? ampmName.toUpperCase() : ampmName.toLowerCase(); | ||
1069 | + } | ||
1070 | + return ''; | ||
1071 | + } | ||
1072 | + }); | ||
1073 | + | ||
1074 | + tmptime = $.trim(tmptime); | ||
1075 | + return tmptime; | ||
1076 | +}; | ||
1077 | + | ||
1078 | +//######################################################################## | ||
1079 | +// the bad hack :/ override datepicker so it doesnt close on select | ||
1080 | +// inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378 | ||
1081 | +//######################################################################## | ||
1082 | +$.datepicker._base_selectDate = $.datepicker._selectDate; | ||
1083 | +$.datepicker._selectDate = function (id, dateStr) { | ||
1084 | + var inst = this._getInst($(id)[0]), | ||
1085 | + tp_inst = this._get(inst, 'timepicker'); | ||
1086 | + | ||
1087 | + if (tp_inst) { | ||
1088 | + tp_inst._limitMinMaxDateTime(inst, true); | ||
1089 | + inst.inline = inst.stay_open = true; | ||
1090 | + //This way the onSelect handler called from calendarpicker get the full dateTime | ||
1091 | + this._base_selectDate(id, dateStr); | ||
1092 | + inst.inline = inst.stay_open = false; | ||
1093 | + this._notifyChange(inst); | ||
1094 | + this._updateDatepicker(inst); | ||
1095 | + } | ||
1096 | + else { this._base_selectDate(id, dateStr); } | ||
1097 | +}; | ||
1098 | + | ||
1099 | +//############################################################################################# | ||
1100 | +// second bad hack :/ override datepicker so it triggers an event when changing the input field | ||
1101 | +// and does not redraw the datepicker on every selectDate event | ||
1102 | +//############################################################################################# | ||
1103 | +$.datepicker._base_updateDatepicker = $.datepicker._updateDatepicker; | ||
1104 | +$.datepicker._updateDatepicker = function(inst) { | ||
1105 | + | ||
1106 | + // don't popup the datepicker if there is another instance already opened | ||
1107 | + var input = inst.input[0]; | ||
1108 | + if($.datepicker._curInst && | ||
1109 | + $.datepicker._curInst != inst && | ||
1110 | + $.datepicker._datepickerShowing && | ||
1111 | + $.datepicker._lastInput != input) { | ||
1112 | + return; | ||
1113 | + } | ||
1114 | + | ||
1115 | + if (typeof(inst.stay_open) !== 'boolean' || inst.stay_open === false) { | ||
1116 | + | ||
1117 | + this._base_updateDatepicker(inst); | ||
1118 | + | ||
1119 | + // Reload the time control when changing something in the input text field. | ||
1120 | + var tp_inst = this._get(inst, 'timepicker'); | ||
1121 | + if(tp_inst) { | ||
1122 | + tp_inst._addTimePicker(inst); | ||
1123 | + | ||
1124 | + if (tp_inst._defaults.useLocalTimezone) { //checks daylight saving with the new date. | ||
1125 | + var date = new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay, 12); | ||
1126 | + selectLocalTimeZone(tp_inst, date); | ||
1127 | + tp_inst._onTimeChange(); | ||
1128 | + } | ||
1129 | + } | ||
1130 | + } | ||
1131 | +}; | ||
1132 | + | ||
1133 | +//####################################################################################### | ||
1134 | +// third bad hack :/ override datepicker so it allows spaces and colon in the input field | ||
1135 | +//####################################################################################### | ||
1136 | +$.datepicker._base_doKeyPress = $.datepicker._doKeyPress; | ||
1137 | +$.datepicker._doKeyPress = function(event) { | ||
1138 | + var inst = $.datepicker._getInst(event.target), | ||
1139 | + tp_inst = $.datepicker._get(inst, 'timepicker'); | ||
1140 | + | ||
1141 | + if (tp_inst) { | ||
1142 | + if ($.datepicker._get(inst, 'constrainInput')) { | ||
1143 | + var ampm = tp_inst._defaults.ampm, | ||
1144 | + dateChars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')), | ||
1145 | + datetimeChars = tp_inst._defaults.timeFormat.toString() | ||
1146 | + .replace(/[hms]/g, '') | ||
1147 | + .replace(/TT/g, ampm ? 'APM' : '') | ||
1148 | + .replace(/Tt/g, ampm ? 'AaPpMm' : '') | ||
1149 | + .replace(/tT/g, ampm ? 'AaPpMm' : '') | ||
1150 | + .replace(/T/g, ampm ? 'AP' : '') | ||
1151 | + .replace(/tt/g, ampm ? 'apm' : '') | ||
1152 | + .replace(/t/g, ampm ? 'ap' : '') + | ||
1153 | + " " + | ||
1154 | + tp_inst._defaults.separator + | ||
1155 | + tp_inst._defaults.timeSuffix + | ||
1156 | + (tp_inst._defaults.showTimezone ? tp_inst._defaults.timezoneList.join('') : '') + | ||
1157 | + (tp_inst._defaults.amNames.join('')) + | ||
1158 | + (tp_inst._defaults.pmNames.join('')) + | ||
1159 | + dateChars, | ||
1160 | + chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode); | ||
1161 | + return event.ctrlKey || (chr < ' ' || !dateChars || datetimeChars.indexOf(chr) > -1); | ||
1162 | + } | ||
1163 | + } | ||
1164 | + | ||
1165 | + return $.datepicker._base_doKeyPress(event); | ||
1166 | +}; | ||
1167 | + | ||
1168 | +//####################################################################################### | ||
1169 | +// Override key up event to sync manual input changes. | ||
1170 | +//####################################################################################### | ||
1171 | +$.datepicker._base_doKeyUp = $.datepicker._doKeyUp; | ||
1172 | +$.datepicker._doKeyUp = function (event) { | ||
1173 | + var inst = $.datepicker._getInst(event.target), | ||
1174 | + tp_inst = $.datepicker._get(inst, 'timepicker'); | ||
1175 | + | ||
1176 | + if (tp_inst) { | ||
1177 | + if (tp_inst._defaults.timeOnly && (inst.input.val() != inst.lastVal)) { | ||
1178 | + try { | ||
1179 | + $.datepicker._updateDatepicker(inst); | ||
1180 | + } | ||
1181 | + catch (err) { | ||
1182 | + $.datepicker.log(err); | ||
1183 | + } | ||
1184 | + } | ||
1185 | + } | ||
1186 | + | ||
1187 | + return $.datepicker._base_doKeyUp(event); | ||
1188 | +}; | ||
1189 | + | ||
1190 | +//####################################################################################### | ||
1191 | +// override "Today" button to also grab the time. | ||
1192 | +//####################################################################################### | ||
1193 | +$.datepicker._base_gotoToday = $.datepicker._gotoToday; | ||
1194 | +$.datepicker._gotoToday = function(id) { | ||
1195 | + var inst = this._getInst($(id)[0]), | ||
1196 | + $dp = inst.dpDiv; | ||
1197 | + this._base_gotoToday(id); | ||
1198 | + var tp_inst = this._get(inst, 'timepicker'); | ||
1199 | + selectLocalTimeZone(tp_inst); | ||
1200 | + var now = new Date(); | ||
1201 | + this._setTime(inst, now); | ||
1202 | + $( '.ui-datepicker-today', $dp).click(); | ||
1203 | +}; | ||
1204 | + | ||
1205 | +//####################################################################################### | ||
1206 | +// Disable & enable the Time in the datetimepicker | ||
1207 | +//####################################################################################### | ||
1208 | +$.datepicker._disableTimepickerDatepicker = function(target) { | ||
1209 | + var inst = this._getInst(target); | ||
1210 | + if (!inst) { return; } | ||
1211 | + | ||
1212 | + var tp_inst = this._get(inst, 'timepicker'); | ||
1213 | + $(target).datepicker('getDate'); // Init selected[Year|Month|Day] | ||
1214 | + if (tp_inst) { | ||
1215 | + tp_inst._defaults.showTimepicker = false; | ||
1216 | + tp_inst._updateDateTime(inst); | ||
1217 | + } | ||
1218 | +}; | ||
1219 | + | ||
1220 | +$.datepicker._enableTimepickerDatepicker = function(target) { | ||
1221 | + var inst = this._getInst(target); | ||
1222 | + if (!inst) { return; } | ||
1223 | + | ||
1224 | + var tp_inst = this._get(inst, 'timepicker'); | ||
1225 | + $(target).datepicker('getDate'); // Init selected[Year|Month|Day] | ||
1226 | + if (tp_inst) { | ||
1227 | + tp_inst._defaults.showTimepicker = true; | ||
1228 | + tp_inst._addTimePicker(inst); // Could be disabled on page load | ||
1229 | + tp_inst._updateDateTime(inst); | ||
1230 | + } | ||
1231 | +}; | ||
1232 | + | ||
1233 | +//####################################################################################### | ||
1234 | +// Create our own set time function | ||
1235 | +//####################################################################################### | ||
1236 | +$.datepicker._setTime = function(inst, date) { | ||
1237 | + var tp_inst = this._get(inst, 'timepicker'); | ||
1238 | + if (tp_inst) { | ||
1239 | + var defaults = tp_inst._defaults, | ||
1240 | + // calling _setTime with no date sets time to defaults | ||
1241 | + hour = date ? date.getHours() : defaults.hour, | ||
1242 | + minute = date ? date.getMinutes() : defaults.minute, | ||
1243 | + second = date ? date.getSeconds() : defaults.second, | ||
1244 | + millisec = date ? date.getMilliseconds() : defaults.millisec; | ||
1245 | + //check if within min/max times.. | ||
1246 | + // correct check if within min/max times. | ||
1247 | + // Rewritten by Scott A. Woodward | ||
1248 | + var hourEq = hour === defaults.hourMin, | ||
1249 | + minuteEq = minute === defaults.minuteMin, | ||
1250 | + secondEq = second === defaults.secondMin; | ||
1251 | + var reset = false; | ||
1252 | + if(hour < defaults.hourMin || hour > defaults.hourMax) | ||
1253 | + reset = true; | ||
1254 | + else if( (minute < defaults.minuteMin || minute > defaults.minuteMax) && hourEq) | ||
1255 | + reset = true; | ||
1256 | + else if( (second < defaults.secondMin || second > defaults.secondMax ) && hourEq && minuteEq) | ||
1257 | + reset = true; | ||
1258 | + else if( (millisec < defaults.millisecMin || millisec > defaults.millisecMax) && hourEq && minuteEq && secondEq) | ||
1259 | + reset = true; | ||
1260 | + if(reset) { | ||
1261 | + hour = defaults.hourMin; | ||
1262 | + minute = defaults.minuteMin; | ||
1263 | + second = defaults.secondMin; | ||
1264 | + millisec = defaults.millisecMin; | ||
1265 | + } | ||
1266 | + tp_inst.hour = hour; | ||
1267 | + tp_inst.minute = minute; | ||
1268 | + tp_inst.second = second; | ||
1269 | + tp_inst.millisec = millisec; | ||
1270 | + if (tp_inst.hour_slider) tp_inst.hour_slider.slider('value', hour); | ||
1271 | + if (tp_inst.minute_slider) tp_inst.minute_slider.slider('value', minute); | ||
1272 | + if (tp_inst.second_slider) tp_inst.second_slider.slider('value', second); | ||
1273 | + if (tp_inst.millisec_slider) tp_inst.millisec_slider.slider('value', millisec); | ||
1274 | + | ||
1275 | + tp_inst._onTimeChange(); | ||
1276 | + tp_inst._updateDateTime(inst); | ||
1277 | + } | ||
1278 | +}; | ||
1279 | + | ||
1280 | +//####################################################################################### | ||
1281 | +// Create new public method to set only time, callable as $().datepicker('setTime', date) | ||
1282 | +//####################################################################################### | ||
1283 | +$.datepicker._setTimeDatepicker = function(target, date, withDate) { | ||
1284 | + var inst = this._getInst(target); | ||
1285 | + if (!inst) { return; } | ||
1286 | + | ||
1287 | + var tp_inst = this._get(inst, 'timepicker'); | ||
1288 | + | ||
1289 | + if (tp_inst) { | ||
1290 | + this._setDateFromField(inst); | ||
1291 | + var tp_date; | ||
1292 | + if (date) { | ||
1293 | + if (typeof date == "string") { | ||
1294 | + tp_inst._parseTime(date, withDate); | ||
1295 | + tp_date = new Date(); | ||
1296 | + tp_date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec); | ||
1297 | + } | ||
1298 | + else { tp_date = new Date(date.getTime()); } | ||
1299 | + if (tp_date.toString() == 'Invalid Date') { tp_date = undefined; } | ||
1300 | + this._setTime(inst, tp_date); | ||
1301 | + } | ||
1302 | + } | ||
1303 | + | ||
1304 | +}; | ||
1305 | + | ||
1306 | +//####################################################################################### | ||
1307 | +// override setDate() to allow setting time too within Date object | ||
1308 | +//####################################################################################### | ||
1309 | +$.datepicker._base_setDateDatepicker = $.datepicker._setDateDatepicker; | ||
1310 | +$.datepicker._setDateDatepicker = function(target, date) { | ||
1311 | + var inst = this._getInst(target); | ||
1312 | + if (!inst) { return; } | ||
1313 | + | ||
1314 | + var tp_date = (date instanceof Date) ? new Date(date.getTime()) : date; | ||
1315 | + | ||
1316 | + this._updateDatepicker(inst); | ||
1317 | + this._base_setDateDatepicker.apply(this, arguments); | ||
1318 | + this._setTimeDatepicker(target, tp_date, true); | ||
1319 | +}; | ||
1320 | + | ||
1321 | +//####################################################################################### | ||
1322 | +// override getDate() to allow getting time too within Date object | ||
1323 | +//####################################################################################### | ||
1324 | +$.datepicker._base_getDateDatepicker = $.datepicker._getDateDatepicker; | ||
1325 | +$.datepicker._getDateDatepicker = function(target, noDefault) { | ||
1326 | + var inst = this._getInst(target); | ||
1327 | + if (!inst) { return; } | ||
1328 | + | ||
1329 | + var tp_inst = this._get(inst, 'timepicker'); | ||
1330 | + | ||
1331 | + if (tp_inst) { | ||
1332 | + this._setDateFromField(inst, noDefault); | ||
1333 | + var date = this._getDate(inst); | ||
1334 | + if (date && tp_inst._parseTime($(target).val(), tp_inst.timeOnly)) { date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec); } | ||
1335 | + return date; | ||
1336 | + } | ||
1337 | + return this._base_getDateDatepicker(target, noDefault); | ||
1338 | +}; | ||
1339 | + | ||
1340 | +//####################################################################################### | ||
1341 | +// override parseDate() because UI 1.8.14 throws an error about "Extra characters" | ||
1342 | +// An option in datapicker to ignore extra format characters would be nicer. | ||
1343 | +//####################################################################################### | ||
1344 | +$.datepicker._base_parseDate = $.datepicker.parseDate; | ||
1345 | +$.datepicker.parseDate = function(format, value, settings) { | ||
1346 | + var splitRes = splitDateTime(format, value, settings); | ||
1347 | + return $.datepicker._base_parseDate(format, splitRes[0], settings); | ||
1348 | +}; | ||
1349 | + | ||
1350 | +//####################################################################################### | ||
1351 | +// override formatDate to set date with time to the input | ||
1352 | +//####################################################################################### | ||
1353 | +$.datepicker._base_formatDate = $.datepicker._formatDate; | ||
1354 | +$.datepicker._formatDate = function(inst, day, month, year){ | ||
1355 | + var tp_inst = this._get(inst, 'timepicker'); | ||
1356 | + if(tp_inst) { | ||
1357 | + tp_inst._updateDateTime(inst); | ||
1358 | + return tp_inst.$input.val(); | ||
1359 | + } | ||
1360 | + return this._base_formatDate(inst); | ||
1361 | +}; | ||
1362 | + | ||
1363 | +//####################################################################################### | ||
1364 | +// override options setter to add time to maxDate(Time) and minDate(Time). MaxDate | ||
1365 | +//####################################################################################### | ||
1366 | +$.datepicker._base_optionDatepicker = $.datepicker._optionDatepicker; | ||
1367 | +$.datepicker._optionDatepicker = function(target, name, value) { | ||
1368 | + var inst = this._getInst(target); | ||
1369 | + if (!inst) { return null; } | ||
1370 | + | ||
1371 | + var tp_inst = this._get(inst, 'timepicker'); | ||
1372 | + if (tp_inst) { | ||
1373 | + var min = null, max = null, onselect = null; | ||
1374 | + if (typeof name == 'string') { // if min/max was set with the string | ||
1375 | + if (name === 'minDate' || name === 'minDateTime' ) { | ||
1376 | + min = value; | ||
1377 | + } | ||
1378 | + else { | ||
1379 | + if (name === 'maxDate' || name === 'maxDateTime') { | ||
1380 | + max = value; | ||
1381 | + } | ||
1382 | + else { | ||
1383 | + if (name === 'onSelect') { | ||
1384 | + onselect = value; | ||
1385 | + } | ||
1386 | + } | ||
1387 | + } | ||
1388 | + } else { | ||
1389 | + if (typeof name == 'object') { //if min/max was set with the JSON | ||
1390 | + if (name.minDate) { | ||
1391 | + min = name.minDate; | ||
1392 | + } else { | ||
1393 | + if (name.minDateTime) { | ||
1394 | + min = name.minDateTime; | ||
1395 | + } else { | ||
1396 | + if (name.maxDate) { | ||
1397 | + max = name.maxDate; | ||
1398 | + } else { | ||
1399 | + if (name.maxDateTime) { | ||
1400 | + max = name.maxDateTime; | ||
1401 | + } | ||
1402 | + } | ||
1403 | + } | ||
1404 | + } | ||
1405 | + } | ||
1406 | + } | ||
1407 | + if(min) { //if min was set | ||
1408 | + if (min === 0) { | ||
1409 | + min = new Date(); | ||
1410 | + } else { | ||
1411 | + min = new Date(min); | ||
1412 | + } | ||
1413 | + | ||
1414 | + tp_inst._defaults.minDate = min; | ||
1415 | + tp_inst._defaults.minDateTime = min; | ||
1416 | + } else if (max) { //if max was set | ||
1417 | + if(max===0) { | ||
1418 | + max=new Date(); | ||
1419 | + } else { | ||
1420 | + max= new Date(max); | ||
1421 | + } | ||
1422 | + tp_inst._defaults.maxDate = max; | ||
1423 | + tp_inst._defaults.maxDateTime = max; | ||
1424 | + } else if (onselect) { | ||
1425 | + tp_inst._defaults.onSelect = onselect; | ||
1426 | + } | ||
1427 | + } | ||
1428 | + if (value === undefined) { | ||
1429 | + return this._base_optionDatepicker(target, name); | ||
1430 | + } | ||
1431 | + return this._base_optionDatepicker(target, name, value); | ||
1432 | +}; | ||
1433 | + | ||
1434 | +//####################################################################################### | ||
1435 | +// jQuery extend now ignores nulls! | ||
1436 | +//####################################################################################### | ||
1437 | +function extendRemove(target, props) { | ||
1438 | + $.extend(target, props); | ||
1439 | + for (var name in props) { | ||
1440 | + if (props[name] === null || props[name] === undefined) { | ||
1441 | + target[name] = props[name]; | ||
1442 | + } | ||
1443 | + } | ||
1444 | + return target; | ||
1445 | +} | ||
1446 | + | ||
1447 | +//####################################################################################### | ||
1448 | +// Splits datetime string into date ans time substrings. | ||
1449 | +// Throws exception when date can't be parsed | ||
1450 | +// If only date is present, time substring eill be '' | ||
1451 | +//####################################################################################### | ||
1452 | +var splitDateTime = function(dateFormat, dateTimeString, dateSettings) | ||
1453 | +{ | ||
1454 | + try { | ||
1455 | + var date = $.datepicker._base_parseDate(dateFormat, dateTimeString, dateSettings); | ||
1456 | + } catch (err) { | ||
1457 | + if (err.indexOf(":") >= 0) { | ||
1458 | + // Hack! The error message ends with a colon, a space, and | ||
1459 | + // the "extra" characters. We rely on that instead of | ||
1460 | + // attempting to perfectly reproduce the parsing algorithm. | ||
1461 | + var dateStringLength = dateTimeString.length-(err.length-err.indexOf(':')-2); | ||
1462 | + var timeString = dateTimeString.substring(dateStringLength); | ||
1463 | + | ||
1464 | + return [dateTimeString.substring(0, dateStringLength), dateTimeString.substring(dateStringLength)]; | ||
1465 | + | ||
1466 | + } else { | ||
1467 | + throw err; | ||
1468 | + } | ||
1469 | + } | ||
1470 | + return [dateTimeString, '']; | ||
1471 | +}; | ||
1472 | + | ||
1473 | +//####################################################################################### | ||
1474 | +// Internal function to parse datetime interval | ||
1475 | +// Returns: {date: Date, timeObj: Object}, where | ||
1476 | +// date - parsed date without time (type Date) | ||
1477 | +// timeObj = {hour: , minute: , second: , millisec: } - parsed time. Optional | ||
1478 | +//####################################################################################### | ||
1479 | +var parseDateTimeInternal = function(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) | ||
1480 | +{ | ||
1481 | + var date; | ||
1482 | + var splitRes = splitDateTime(dateFormat, dateTimeString, dateSettings); | ||
1483 | + date = $.datepicker._base_parseDate(dateFormat, splitRes[0], dateSettings); | ||
1484 | + if (splitRes[1] !== '') | ||
1485 | + { | ||
1486 | + var timeString = splitRes[1]; | ||
1487 | + var separator = timeSettings && timeSettings.separator ? timeSettings.separator : $.timepicker._defaults.separator; | ||
1488 | + if ( timeString.indexOf(separator) !== 0) { | ||
1489 | + throw 'Missing time separator'; | ||
1490 | + } | ||
1491 | + timeString = timeString.substring(separator.length); | ||
1492 | + var parsedTime = $.datepicker.parseTime(timeFormat, timeString, timeSettings); | ||
1493 | + if (parsedTime === null) { | ||
1494 | + throw 'Wrong time format'; | ||
1495 | + } | ||
1496 | + return {date: date, timeObj: parsedTime}; | ||
1497 | + } else { | ||
1498 | + return {date: date}; | ||
1499 | + } | ||
1500 | +}; | ||
1501 | + | ||
1502 | +//####################################################################################### | ||
1503 | +// Internal function to set timezone_select to the local timezone | ||
1504 | +//####################################################################################### | ||
1505 | +var selectLocalTimeZone = function(tp_inst, date) | ||
1506 | +{ | ||
1507 | + if (tp_inst && tp_inst.timezone_select) { | ||
1508 | + tp_inst._defaults.useLocalTimezone = true; | ||
1509 | + var now = typeof date !== 'undefined' ? date : new Date(); | ||
1510 | + var tzoffset = timeZoneString(now); | ||
1511 | + if (tp_inst._defaults.timezoneIso8601) { | ||
1512 | + tzoffset = tzoffset.substring(0, 3) + ':' + tzoffset.substring(3); | ||
1513 | + } | ||
1514 | + tp_inst.timezone_select.val(tzoffset); | ||
1515 | + } | ||
1516 | +}; | ||
1517 | + | ||
1518 | +// Input: Date Object | ||
1519 | +// Output: String with timezone offset, e.g. '+0100' | ||
1520 | +var timeZoneString = function(date) | ||
1521 | +{ | ||
1522 | + var off = date.getTimezoneOffset() * -10100 / 60; | ||
1523 | + var timezone = (off >= 0 ? '+' : '-') + Math.abs(off).toString().substr(1); | ||
1524 | + return timezone; | ||
1525 | +}; | ||
1526 | + | ||
1527 | +$.timepicker = new Timepicker(); // singleton instance | ||
1528 | +$.timepicker.version = "1.0.1"; | ||
1529 | + | ||
1530 | +})(jQuery); |
public/stylesheets/application.css
@@ -1394,6 +1394,7 @@ a.comment-picture { | @@ -1394,6 +1394,7 @@ a.comment-picture { | ||
1394 | position: relative; | 1394 | position: relative; |
1395 | display: inline; | 1395 | display: inline; |
1396 | } | 1396 | } |
1397 | +#content #boxes .box-1 .article-block img, | ||
1397 | #content #article .article-body img { | 1398 | #content #article .article-body img { |
1398 | max-width: 100%; | 1399 | max-width: 100%; |
1399 | height: auto; | 1400 | height: auto; |
@@ -1503,7 +1504,13 @@ a.button:hover, body.noosfero a.button:hover, input.button:hover, a.button.with- | @@ -1503,7 +1504,13 @@ a.button:hover, body.noosfero a.button:hover, input.button:hover, a.button.with- | ||
1503 | body.noosfero a.button.with-text.icon-none, body.noosfero input.button.with-text.icon-none { | 1504 | body.noosfero a.button.with-text.icon-none, body.noosfero input.button.with-text.icon-none { |
1504 | padding-left: 2px; | 1505 | padding-left: 2px; |
1505 | } | 1506 | } |
1506 | -a.button.disabled, input.disabled { | 1507 | +a.disabled{ |
1508 | + filter: url(/filters.svg#grayscale); /* Firefox 3.5+ */ | ||
1509 | + filter: gray; /* IE6-9 */ | ||
1510 | + -webkit-filter: grayscale(1); /* Google Chrome & Safari 6+ */ | ||
1511 | + cursor: default; | ||
1512 | +} | ||
1513 | +input.disabled { | ||
1507 | opacity: 0.5; | 1514 | opacity: 0.5; |
1508 | filter-opacity: 50%; | 1515 | filter-opacity: 50%; |
1509 | } | 1516 | } |
@@ -1766,6 +1773,9 @@ a.button.disabled, input.disabled { | @@ -1766,6 +1773,9 @@ a.button.disabled, input.disabled { | ||
1766 | width: 92px; | 1773 | width: 92px; |
1767 | overflow: hidden; | 1774 | overflow: hidden; |
1768 | } | 1775 | } |
1776 | +.box-1 .common-profile-list-block span { | ||
1777 | + width: 102px; | ||
1778 | +} | ||
1769 | .common-profile-list-block .profile-image { | 1779 | .common-profile-list-block .profile-image { |
1770 | width: 92px; | 1780 | width: 92px; |
1771 | display: table-cell; | 1781 | display: table-cell; |
@@ -3974,7 +3984,7 @@ h1#agenda-title { | @@ -3974,7 +3984,7 @@ h1#agenda-title { | ||
3974 | } | 3984 | } |
3975 | /* * * Profile search block * * * * * * * */ | 3985 | /* * * Profile search block * * * * * * * */ |
3976 | 3986 | ||
3977 | -.profile-search-block .formfield input { | 3987 | +.profile-search-block .search-field .formfield input { |
3978 | width: 100%; | 3988 | width: 100%; |
3979 | } | 3989 | } |
3980 | .profile-search-block .button.icon-search { | 3990 | .profile-search-block .button.icon-search { |
@@ -4330,6 +4340,14 @@ h1#agenda-title { | @@ -4330,6 +4340,14 @@ h1#agenda-title { | ||
4330 | margin: 5px 0; | 4340 | margin: 5px 0; |
4331 | padding: 5px; | 4341 | padding: 5px; |
4332 | } | 4342 | } |
4343 | + | ||
4344 | +.ui-timepicker-div .ui-widget-header { margin-bottom: 8px; } | ||
4345 | +.ui-timepicker-div dl { text-align: left; } | ||
4346 | +.ui-timepicker-div dl dt { height: 25px; margin-bottom: -25px; } | ||
4347 | +.ui-timepicker-div dl dd { margin: 0 10px 10px 65px; } | ||
4348 | +.ui-timepicker-div td { font-size: 90%; } | ||
4349 | +.ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; } | ||
4350 | + | ||
4333 | /* Categories block stuff */ | 4351 | /* Categories block stuff */ |
4334 | 4352 | ||
4335 | .categories-block ul { | 4353 | .categories-block ul { |
script/quick-start
@@ -2,12 +2,12 @@ | @@ -2,12 +2,12 @@ | ||
2 | 2 | ||
3 | say() { | 3 | say() { |
4 | msg="$@" | 4 | msg="$@" |
5 | - printf "\033[33;01m%sm%s\033[m\n" "$msg"33[m\n" "$msg" | 5 | + printf "\033[1;34;49m%sm%s\033[m\n" "$msg"33[m\n" "$msg" |
6 | } | 6 | } |
7 | 7 | ||
8 | complain() { | 8 | complain() { |
9 | msg="$@" | 9 | msg="$@" |
10 | - printf "\033[1;31;40m%sm%s\033[m\n" "$msg"33[m\n" "$msg" | 10 | + printf "\033[1;31;49m%sm%s\033[m\n" "$msg"33[m\n" "$msg" |
11 | } | 11 | } |
12 | 12 | ||
13 | run() { | 13 | run() { |
test/factories.rb
@@ -442,7 +442,7 @@ module Noosfero::Factory | @@ -442,7 +442,7 @@ module Noosfero::Factory | ||
442 | 442 | ||
443 | def defaults_for_comment(params = {}) | 443 | def defaults_for_comment(params = {}) |
444 | name = "comment_#{rand(1000)}" | 444 | name = "comment_#{rand(1000)}" |
445 | - { :title => name, :body => "my own comment", :source_id => 1 }.merge(params) | 445 | + { :title => name, :body => "my own comment", :source_id => 1, :source_type => 'Article' }.merge(params) |
446 | end | 446 | end |
447 | 447 | ||
448 | ############################################### | 448 | ############################################### |
test/functional/account_controller_test.rb
@@ -11,6 +11,10 @@ class AccountControllerTest < ActionController::TestCase | @@ -11,6 +11,10 @@ class AccountControllerTest < ActionController::TestCase | ||
11 | 11 | ||
12 | all_fixtures | 12 | all_fixtures |
13 | 13 | ||
14 | + def teardown | ||
15 | + Thread.current[:enabled_plugins] = nil | ||
16 | + end | ||
17 | + | ||
14 | def setup | 18 | def setup |
15 | @controller = AccountController.new | 19 | @controller = AccountController.new |
16 | @request = ActionController::TestRequest.new | 20 | @request = ActionController::TestRequest.new |
@@ -20,24 +24,22 @@ class AccountControllerTest < ActionController::TestCase | @@ -20,24 +24,22 @@ class AccountControllerTest < ActionController::TestCase | ||
20 | def test_local_files_reference | 24 | def test_local_files_reference |
21 | assert_local_files_reference | 25 | assert_local_files_reference |
22 | end | 26 | end |
23 | - | 27 | + |
24 | def test_valid_xhtml | 28 | def test_valid_xhtml |
25 | assert_valid_xhtml | 29 | assert_valid_xhtml |
26 | end | 30 | end |
27 | - | 31 | + |
28 | def test_should_login_and_redirect | 32 | def test_should_login_and_redirect |
29 | post :login, :user => {:login => 'johndoe', :password => 'test'} | 33 | post :login, :user => {:login => 'johndoe', :password => 'test'} |
30 | assert session[:user] | 34 | assert session[:user] |
31 | assert_response :redirect | 35 | assert_response :redirect |
32 | end | 36 | end |
33 | 37 | ||
34 | - should 'redirect to where user was on login' do | ||
35 | - @request.env["HTTP_REFERER"] = '/bli' | ||
36 | - u = new_user | 38 | + should 'display notice message if the login fail' do |
37 | @controller.stubs(:logged_in?).returns(false) | 39 | @controller.stubs(:logged_in?).returns(false) |
38 | post :login, :user => {:login => 'quire', :password => 'quire'} | 40 | post :login, :user => {:login => 'quire', :password => 'quire'} |
39 | 41 | ||
40 | - assert_redirected_to '/bli' | 42 | + assert session[:notice].include?('Incorrect') |
41 | end | 43 | end |
42 | 44 | ||
43 | should 'authenticate on the current environment' do | 45 | should 'authenticate on the current environment' do |
@@ -46,23 +48,11 @@ class AccountControllerTest < ActionController::TestCase | @@ -46,23 +48,11 @@ class AccountControllerTest < ActionController::TestCase | ||
46 | post :login, :user => { :login => 'fake', :password => 'fake' } | 48 | post :login, :user => { :login => 'fake', :password => 'fake' } |
47 | end | 49 | end |
48 | 50 | ||
49 | - should 'redirect to where was when login on other environment' do | ||
50 | - e = fast_create(Environment, :name => 'other_environment') | ||
51 | - e.domains << Domain.new(:name => 'other.environment') | ||
52 | - e.save! | ||
53 | - u = create_user('test_user', :environment => e).person | ||
54 | - | ||
55 | - @request.env["HTTP_REFERER"] = '/bli' | ||
56 | - post :login, :user => {:login => 'test_user', :password => 'test_user'} | ||
57 | - | ||
58 | - assert_redirected_to '/bli' | ||
59 | - end | ||
60 | - | ||
61 | def test_should_fail_login_and_not_redirect | 51 | def test_should_fail_login_and_not_redirect |
62 | @request.env["HTTP_REFERER"] = 'bli' | 52 | @request.env["HTTP_REFERER"] = 'bli' |
63 | post :login, :user => {:login => 'johndoe', :password => 'bad password'} | 53 | post :login, :user => {:login => 'johndoe', :password => 'bad password'} |
64 | assert_nil session[:user] | 54 | assert_nil session[:user] |
65 | - assert_response :redirect | 55 | + assert_response :success |
66 | end | 56 | end |
67 | 57 | ||
68 | def test_should_allow_signup | 58 | def test_should_allow_signup |
@@ -120,7 +110,7 @@ class AccountControllerTest < ActionController::TestCase | @@ -120,7 +110,7 @@ class AccountControllerTest < ActionController::TestCase | ||
120 | 110 | ||
121 | def test_shoud_save_with_acceptance_of_terms_of_use_on_signup | 111 | def test_shoud_save_with_acceptance_of_terms_of_use_on_signup |
122 | assert_difference User, :count do | 112 | assert_difference User, :count do |
123 | - Environment.default.update_attributes(:terms_of_use => 'some terms ...') | 113 | + Environment.default.update_attributes(:terms_of_use => 'some terms ...') |
124 | new_user(:terms_accepted => '1') | 114 | new_user(:terms_accepted => '1') |
125 | assert_response :success | 115 | assert_response :success |
126 | assert_not_nil assigns(:register_pending) | 116 | assert_not_nil assigns(:register_pending) |
@@ -144,7 +134,7 @@ class AccountControllerTest < ActionController::TestCase | @@ -144,7 +134,7 @@ class AccountControllerTest < ActionController::TestCase | ||
144 | post :login, :user => {:login => 'johndoe', :password => 'test'}, :remember_me => "0" | 134 | post :login, :user => {:login => 'johndoe', :password => 'test'}, :remember_me => "0" |
145 | assert_nil @response.cookies["auth_token"] | 135 | assert_nil @response.cookies["auth_token"] |
146 | end | 136 | end |
147 | - | 137 | + |
148 | def test_should_delete_token_on_logout | 138 | def test_should_delete_token_on_logout |
149 | login_as :johndoe | 139 | login_as :johndoe |
150 | get :logout | 140 | get :logout |
@@ -344,7 +334,7 @@ class AccountControllerTest < ActionController::TestCase | @@ -344,7 +334,7 @@ class AccountControllerTest < ActionController::TestCase | ||
344 | login_as(person.identifier) | 334 | login_as(person.identifier) |
345 | 335 | ||
346 | ent = fast_create(Enterprise, :name => 'test enterprise', :identifier => 'test_ent') | 336 | ent = fast_create(Enterprise, :name => 'test enterprise', :identifier => 'test_ent') |
347 | - | 337 | + |
348 | task = mock | 338 | task = mock |
349 | task.expects(:enterprise).returns(ent).at_least_once | 339 | task.expects(:enterprise).returns(ent).at_least_once |
350 | EnterpriseActivation.expects(:find_by_code).with('0123456789').returns(task).at_least_once | 340 | EnterpriseActivation.expects(:find_by_code).with('0123456789').returns(task).at_least_once |
@@ -359,7 +349,7 @@ class AccountControllerTest < ActionController::TestCase | @@ -359,7 +349,7 @@ class AccountControllerTest < ActionController::TestCase | ||
359 | login_as(person.identifier) | 349 | login_as(person.identifier) |
360 | 350 | ||
361 | ent = fast_create(Enterprise, :name => 'test enterprise', :identifier => 'test_ent', :enabled => false) | 351 | ent = fast_create(Enterprise, :name => 'test enterprise', :identifier => 'test_ent', :enabled => false) |
362 | - | 352 | + |
363 | task = mock | 353 | task = mock |
364 | task.expects(:enterprise).returns(ent).at_least_once | 354 | task.expects(:enterprise).returns(ent).at_least_once |
365 | EnterpriseActivation.expects(:find_by_code).with('0123456789').returns(task).at_least_once | 355 | EnterpriseActivation.expects(:find_by_code).with('0123456789').returns(task).at_least_once |
@@ -555,7 +545,7 @@ class AccountControllerTest < ActionController::TestCase | @@ -555,7 +545,7 @@ class AccountControllerTest < ActionController::TestCase | ||
555 | login_as(person.identifier) | 545 | login_as(person.identifier) |
556 | 546 | ||
557 | env = Environment.default | 547 | env = Environment.default |
558 | - env.terms_of_use = 'some terms' | 548 | + env.terms_of_use = 'some terms' |
559 | env.save! | 549 | env.save! |
560 | ent = fast_create(Enterprise, :name => 'test enterprise', :identifier => 'test_ent', :enabled => false) | 550 | ent = fast_create(Enterprise, :name => 'test enterprise', :identifier => 'test_ent', :enabled => false) |
561 | ent.update_attribute(:foundation_year, 1998) | 551 | ent.update_attribute(:foundation_year, 1998) |
@@ -697,7 +687,6 @@ class AccountControllerTest < ActionController::TestCase | @@ -697,7 +687,6 @@ class AccountControllerTest < ActionController::TestCase | ||
697 | assert_nil assigns(:message) | 687 | assert_nil assigns(:message) |
698 | post :login, :user => {:login => 'testuser', :password => 'test123'} | 688 | post :login, :user => {:login => 'testuser', :password => 'test123'} |
699 | assert_nil session[:user] | 689 | assert_nil session[:user] |
700 | - assert_redirected_to '/bli' | ||
701 | end | 690 | end |
702 | 691 | ||
703 | should 'not activate user when activation code is incorrect' do | 692 | should 'not activate user when activation code is incorrect' do |
@@ -707,7 +696,6 @@ class AccountControllerTest < ActionController::TestCase | @@ -707,7 +696,6 @@ class AccountControllerTest < ActionController::TestCase | ||
707 | assert_nil assigns(:message) | 696 | assert_nil assigns(:message) |
708 | post :login, :user => {:login => 'testuser', :password => 'test123'} | 697 | post :login, :user => {:login => 'testuser', :password => 'test123'} |
709 | assert_nil session[:user] | 698 | assert_nil session[:user] |
710 | - assert_redirected_to '/bli' | ||
711 | end | 699 | end |
712 | 700 | ||
713 | should 'be able to upload an image' do | 701 | should 'be able to upload an image' do |
@@ -777,6 +765,122 @@ class AccountControllerTest < ActionController::TestCase | @@ -777,6 +765,122 @@ class AccountControllerTest < ActionController::TestCase | ||
777 | assert_tag :tag => 'strong', :content => 'Plugin2 text' | 765 | assert_tag :tag => 'strong', :content => 'Plugin2 text' |
778 | end | 766 | end |
779 | 767 | ||
768 | + should 'login with an alternative authentication defined by plugin' do | ||
769 | + class Plugin1 < Noosfero::Plugin | ||
770 | + def alternative_authentication | ||
771 | + User.new(:login => 'testuser') | ||
772 | + end | ||
773 | + end | ||
774 | + Environment.default.enable_plugin(Plugin1.name) | ||
775 | + | ||
776 | + post :login, :user => {:login => "testuser"} | ||
777 | + | ||
778 | + assert_equal 'testuser', assigns(:current_user).login | ||
779 | + assert_response :redirect | ||
780 | + end | ||
781 | + | ||
782 | + should "login with the default autentication if the alternative authentication method doesn't login the user" do | ||
783 | + class Plugin1 < Noosfero::Plugin | ||
784 | + def alternative_authentication | ||
785 | + nil | ||
786 | + end | ||
787 | + end | ||
788 | + Environment.default.enable_plugin(Plugin1.name) | ||
789 | + post :login, :user => {:login => 'johndoe', :password => 'test'} | ||
790 | + assert session[:user] | ||
791 | + assert_equal 'johndoe', assigns(:current_user).login | ||
792 | + assert_response :redirect | ||
793 | + end | ||
794 | + | ||
795 | + should "redirect user on signup if a plugin doesn't allow user registration" do | ||
796 | + class TestRegistrationPlugin < Noosfero::Plugin | ||
797 | + def allow_user_registration | ||
798 | + false | ||
799 | + end | ||
800 | + end | ||
801 | + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([TestRegistrationPlugin.new]) | ||
802 | + | ||
803 | + post :signup, :user => { :login => 'testuser', :password => '123456', :password_confirmation => '123456', :email => 'testuser@example.com' } | ||
804 | + assert_response :redirect | ||
805 | + end | ||
806 | + | ||
807 | + should "not display the new user button on login page if not allowed by any plugin" do | ||
808 | + class Plugin1 < Noosfero::Plugin | ||
809 | + def allow_user_registration | ||
810 | + false | ||
811 | + end | ||
812 | + end | ||
813 | + | ||
814 | + class Plugin2 < Noosfero::Plugin | ||
815 | + def allow_user_registration | ||
816 | + true | ||
817 | + end | ||
818 | + end | ||
819 | + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([Plugin1.new, Plugin2.new]) | ||
820 | + | ||
821 | + get :login | ||
822 | + | ||
823 | + assert_no_tag :tag => 'a', :attributes => {:href => '/account/signup'} | ||
824 | + end | ||
825 | + | ||
826 | + should "redirect user on forgot_password action if a plugin doesn't allow user to recover its password" do | ||
827 | + class TestRegistrationPlugin < Noosfero::Plugin | ||
828 | + def allow_password_recovery | ||
829 | + false | ||
830 | + end | ||
831 | + end | ||
832 | + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([TestRegistrationPlugin.new]) | ||
833 | + | ||
834 | + #Redirect on get action | ||
835 | + get :forgot_password | ||
836 | + assert_response :redirect | ||
837 | + | ||
838 | + #Redirect on post action | ||
839 | + post :forgot_password, :change_password => { :login => 'test', :email => 'test@localhost.localdomain' } | ||
840 | + assert_response :redirect | ||
841 | + end | ||
842 | + | ||
843 | + should "not display the forgot password button on login page if not allowed by any plugin" do | ||
844 | + class Plugin1 < Noosfero::Plugin | ||
845 | + def allow_password_recovery | ||
846 | + false | ||
847 | + end | ||
848 | + end | ||
849 | + | ||
850 | + class Plugin2 < Noosfero::Plugin | ||
851 | + def allow_password_recovery | ||
852 | + true | ||
853 | + end | ||
854 | + end | ||
855 | + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([Plugin1.new, Plugin2.new]) | ||
856 | + | ||
857 | + get :login | ||
858 | + | ||
859 | + assert_no_tag :tag => 'a', :attributes => {:href => '/account/forgot_password'} | ||
860 | + end | ||
861 | + | ||
862 | + should 'add extra content on login form from plugins' do | ||
863 | + class Plugin1 < Noosfero::Plugin | ||
864 | + def login_extra_contents | ||
865 | + lambda {"<strong>Plugin1 text</strong>"} | ||
866 | + end | ||
867 | + end | ||
868 | + class Plugin2 < Noosfero::Plugin | ||
869 | + def login_extra_contents | ||
870 | + lambda {"<strong>Plugin2 text</strong>"} | ||
871 | + end | ||
872 | + end | ||
873 | + | ||
874 | + Environment.default.enable_plugin(Plugin1.name) | ||
875 | + Environment.default.enable_plugin(Plugin2.name) | ||
876 | + | ||
877 | + get :login | ||
878 | + | ||
879 | + assert_tag :tag => 'strong', :content => 'Plugin1 text' | ||
880 | + assert_tag :tag => 'strong', :content => 'Plugin2 text' | ||
881 | + end | ||
882 | + | ||
883 | + | ||
780 | protected | 884 | protected |
781 | def new_user(options = {}, extra_options ={}) | 885 | def new_user(options = {}, extra_options ={}) |
782 | data = {:profile_data => person_data} | 886 | data = {:profile_data => person_data} |
test/functional/content_viewer_controller_test.rb
@@ -1400,6 +1400,38 @@ end | @@ -1400,6 +1400,38 @@ end | ||
1400 | end | 1400 | end |
1401 | end | 1401 | end |
1402 | 1402 | ||
1403 | + should 'not display article actions button if any plugins says so' do | ||
1404 | + class Plugin1 < Noosfero::Plugin | ||
1405 | + def content_remove_edit(content); true; end | ||
1406 | + end | ||
1407 | + class Plugin2 < Noosfero::Plugin | ||
1408 | + def content_remove_edit(content); false; end | ||
1409 | + end | ||
1410 | + | ||
1411 | + environment.enable_plugin(Plugin1.name) | ||
1412 | + environment.enable_plugin(Plugin2.name) | ||
1413 | + | ||
1414 | + login_as('testinguser') | ||
1415 | + xhr :get, :view_page, :profile => 'testinguser', :page => [], :toolbar => true | ||
1416 | + assert_no_tag :tag => 'div', :attributes => { :id => 'article-actions' }, :descendant => { :tag => 'a', :attributes => { :href => "/myprofile/testinguser/cms/edit/#{profile.home_page.id}" } } | ||
1417 | + end | ||
1418 | + | ||
1419 | + should 'expire article actions button if any plugins says so' do | ||
1420 | + class Plugin1 < Noosfero::Plugin | ||
1421 | + def content_expire_edit(content); 'This button is expired.'; end | ||
1422 | + end | ||
1423 | + class Plugin2 < Noosfero::Plugin | ||
1424 | + def content_expire_edit(content); nil; end | ||
1425 | + end | ||
1426 | + | ||
1427 | + environment.enable_plugin(Plugin1.name) | ||
1428 | + environment.enable_plugin(Plugin2.name) | ||
1429 | + | ||
1430 | + login_as('testinguser') | ||
1431 | + xhr :get, :view_page, :profile => 'testinguser', :page => [], :toolbar => true | ||
1432 | + assert_tag :tag => 'div', :attributes => { :id => 'article-actions' }, :descendant => { :tag => 'a', :attributes => { :title => 'This button is expired.', :class => 'button with-text icon-edit disabled' } } | ||
1433 | + end | ||
1434 | + | ||
1403 | should 'remove email from article followers when unfollow' do | 1435 | should 'remove email from article followers when unfollow' do |
1404 | profile = create_user('testuser').person | 1436 | profile = create_user('testuser').person |
1405 | follower_email = 'john@doe.br' | 1437 | follower_email = 'john@doe.br' |
@@ -1432,4 +1464,42 @@ end | @@ -1432,4 +1464,42 @@ end | ||
1432 | assert spam.spam? | 1464 | assert spam.spam? |
1433 | end | 1465 | end |
1434 | 1466 | ||
1467 | + should 'be able to edit a comment' do | ||
1468 | + login_as profile.identifier | ||
1469 | + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text', :accept_comments => false) | ||
1470 | + comment = fast_create(Comment, :body => 'Original comment', :author_id => profile.id, :source_id => page.id, :source_type => 'Article') | ||
1471 | + | ||
1472 | + post :edit_comment, :id => comment.id, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :body => 'Comment edited' } | ||
1473 | + assert_equal 'Comment edited', Comment.find(comment.id).body | ||
1474 | + end | ||
1475 | + | ||
1476 | + should 'edit comment from a page' do | ||
1477 | + login_as profile.identifier | ||
1478 | + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') | ||
1479 | + comment = fast_create(Comment, :body => 'Original comment', :author_id => profile.id, :source_id => page.id, :source_type => 'Article') | ||
1480 | + | ||
1481 | + get :edit_comment, :id => comment.id, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :body => 'Comment edited' } | ||
1482 | + assert_tag :tag => 'h1', :content => 'Edit comment' | ||
1483 | + end | ||
1484 | + | ||
1485 | + should 'not edit comment from other page' do | ||
1486 | + login_as profile.identifier | ||
1487 | + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') | ||
1488 | + comment = fast_create(Comment, :body => 'Original comment', :author_id => profile.id, :source_id => page.id, :source_type => 'Article') | ||
1489 | + | ||
1490 | + other_page = profile.articles.create!(:name => 'my other article', :body => 'the body of the text') | ||
1491 | + comment_on_other_page = fast_create(Comment, :body => 'Comment on other article', :author_id => profile.id, :source_id => other_page.id, :source_type => 'Article') | ||
1492 | + | ||
1493 | + get :edit_comment, :id => comment_on_other_page.id, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :body => 'Comment edited' } | ||
1494 | + assert_redirected_to page.url | ||
1495 | + end | ||
1496 | + | ||
1497 | + should 'not crash on edit comment if comment does not exist' do | ||
1498 | + login_as profile.identifier | ||
1499 | + page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') | ||
1500 | + | ||
1501 | + get :edit_comment, :id => 1000, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :body => 'Comment edited' } | ||
1502 | + assert_response 404 | ||
1503 | + end | ||
1504 | + | ||
1435 | end | 1505 | end |
test/functional/home_controller_test.rb
@@ -6,8 +6,11 @@ class HomeController; def rescue_action(e) raise e end; end | @@ -6,8 +6,11 @@ class HomeController; def rescue_action(e) raise e end; end | ||
6 | 6 | ||
7 | class HomeControllerTest < ActionController::TestCase | 7 | class HomeControllerTest < ActionController::TestCase |
8 | 8 | ||
9 | -# all_fixtures:profiles, :environments, :domains | ||
10 | -all_fixtures | 9 | + def teardown |
10 | + Thread.current[:enabled_plugins] = nil | ||
11 | + end | ||
12 | + | ||
13 | + all_fixtures | ||
11 | def setup | 14 | def setup |
12 | @controller = HomeController.new | 15 | @controller = HomeController.new |
13 | @request = ActionController::TestRequest.new | 16 | @request = ActionController::TestRequest.new |
@@ -93,4 +96,44 @@ all_fixtures | @@ -93,4 +96,44 @@ all_fixtures | ||
93 | assert_tag :content => /Noosfero terms of use/ | 96 | assert_tag :content => /Noosfero terms of use/ |
94 | end | 97 | end |
95 | 98 | ||
99 | + should 'provide a link to make the user authentication' do | ||
100 | + class Plugin1 < Noosfero::Plugin | ||
101 | + def alternative_authentication_link | ||
102 | + lambda {"<a href='plugin1'>Plugin1 link</a>"} | ||
103 | + end | ||
104 | + end | ||
105 | + class Plugin2 < Noosfero::Plugin | ||
106 | + def alternative_authentication_link | ||
107 | + lambda {"<a href='plugin2'>Plugin2 link</a>"} | ||
108 | + end | ||
109 | + end | ||
110 | + | ||
111 | + Environment.default.enable_plugin(Plugin1) | ||
112 | + Environment.default.enable_plugin(Plugin2) | ||
113 | + | ||
114 | + get :index | ||
115 | + | ||
116 | + assert_tag :tag => 'a', :content => 'Plugin1 link' | ||
117 | + assert_tag :tag => 'a', :content => 'Plugin2 link' | ||
118 | + end | ||
119 | + | ||
120 | + should "not display the new user button on login page if now allowed by any plugin" do | ||
121 | + class Plugin1 < Noosfero::Plugin | ||
122 | + def allow_user_registration | ||
123 | + false | ||
124 | + end | ||
125 | + end | ||
126 | + | ||
127 | + class Plugin2 < Noosfero::Plugin | ||
128 | + def allow_user_registration | ||
129 | + true | ||
130 | + end | ||
131 | + end | ||
132 | + Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([Plugin1.new, Plugin2.new]) | ||
133 | + | ||
134 | + get :index | ||
135 | + | ||
136 | + assert_no_tag :tag => 'a', :attributes => {:href => '/account/signup'} | ||
137 | + end | ||
138 | + | ||
96 | end | 139 | end |
test/functional/profile_design_controller_test.rb
@@ -5,12 +5,12 @@ class ProfileDesignController; def rescue_action(e) raise e end; end | @@ -5,12 +5,12 @@ class ProfileDesignController; def rescue_action(e) raise e end; end | ||
5 | 5 | ||
6 | class ProfileDesignControllerTest < ActionController::TestCase | 6 | class ProfileDesignControllerTest < ActionController::TestCase |
7 | 7 | ||
8 | - COMMOM_BLOCKS = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock ] | 8 | + COMMOM_BLOCKS = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock, HighlightsBlock ] |
9 | PERSON_BLOCKS = COMMOM_BLOCKS + [FriendsBlock, FavoriteEnterprisesBlock, CommunitiesBlock, EnterprisesBlock ] | 9 | PERSON_BLOCKS = COMMOM_BLOCKS + [FriendsBlock, FavoriteEnterprisesBlock, CommunitiesBlock, EnterprisesBlock ] |
10 | PERSON_BLOCKS_WITH_MEMBERS = PERSON_BLOCKS + [MembersBlock] | 10 | PERSON_BLOCKS_WITH_MEMBERS = PERSON_BLOCKS + [MembersBlock] |
11 | PERSON_BLOCKS_WITH_BLOG = PERSON_BLOCKS + [BlogArchivesBlock] | 11 | PERSON_BLOCKS_WITH_BLOG = PERSON_BLOCKS + [BlogArchivesBlock] |
12 | 12 | ||
13 | - ENTERPRISE_BLOCKS = COMMOM_BLOCKS + [DisabledEnterpriseMessageBlock, HighlightsBlock, FeaturedProductsBlock, FansBlock] | 13 | + ENTERPRISE_BLOCKS = COMMOM_BLOCKS + [DisabledEnterpriseMessageBlock, FeaturedProductsBlock, FansBlock] |
14 | ENTERPRISE_BLOCKS_WITH_PRODUCTS_ENABLE = ENTERPRISE_BLOCKS + [ProductsBlock] | 14 | ENTERPRISE_BLOCKS_WITH_PRODUCTS_ENABLE = ENTERPRISE_BLOCKS + [ProductsBlock] |
15 | 15 | ||
16 | attr_reader :holder | 16 | attr_reader :holder |
@@ -295,17 +295,17 @@ class ProfileDesignControllerTest < ActionController::TestCase | @@ -295,17 +295,17 @@ class ProfileDesignControllerTest < ActionController::TestCase | ||
295 | should 'offer to create blog archives block only if has blog' do | 295 | should 'offer to create blog archives block only if has blog' do |
296 | holder.articles << Blog.new(:name => 'Blog test', :profile => holder) | 296 | holder.articles << Blog.new(:name => 'Blog test', :profile => holder) |
297 | get :add_block, :profile => 'designtestuser' | 297 | get :add_block, :profile => 'designtestuser' |
298 | - assert_tag :tag => 'input', :attributes => { :id => 'type_blogarchivesblock', :value => 'BlogArchivesBlock' } | 298 | + assert_tag :tag => 'input', :attributes => { :name => 'type', :value => 'BlogArchivesBlock' } |
299 | end | 299 | end |
300 | 300 | ||
301 | should 'not offer to create blog archives block if user dont have blog' do | 301 | should 'not offer to create blog archives block if user dont have blog' do |
302 | get :add_block, :profile => 'designtestuser' | 302 | get :add_block, :profile => 'designtestuser' |
303 | - assert_no_tag :tag => 'input', :attributes => { :id => 'type_blogarchivesblock', :value => 'BlogArchivesBlock' } | 303 | + assert_no_tag :tag => 'input', :attributes => { :name => 'type', :value => 'BlogArchivesBlock' } |
304 | end | 304 | end |
305 | 305 | ||
306 | should 'offer to create feed reader block' do | 306 | should 'offer to create feed reader block' do |
307 | get :add_block, :profile => 'designtestuser' | 307 | get :add_block, :profile => 'designtestuser' |
308 | - assert_tag :tag => 'input', :attributes => { :id => 'type_feedreaderblock', :value => 'FeedReaderBlock' } | 308 | + assert_tag :tag => 'input', :attributes => { :name => 'type', :value => 'FeedReaderBlock' } |
309 | end | 309 | end |
310 | 310 | ||
311 | should 'be able to edit FeedReaderBlock' do | 311 | should 'be able to edit FeedReaderBlock' do |
@@ -421,14 +421,14 @@ class ProfileDesignControllerTest < ActionController::TestCase | @@ -421,14 +421,14 @@ class ProfileDesignControllerTest < ActionController::TestCase | ||
421 | profile.stubs(:is_admin?).with(profile.environment).returns(true) | 421 | profile.stubs(:is_admin?).with(profile.environment).returns(true) |
422 | @controller.stubs(:user).returns(profile) | 422 | @controller.stubs(:user).returns(profile) |
423 | get :add_block, :profile => 'designtestuser' | 423 | get :add_block, :profile => 'designtestuser' |
424 | - assert_tag :tag => 'input', :attributes => { :id => 'type_rawhtmlblock', :value => 'RawHTMLBlock' } | 424 | + assert_tag :tag => 'input', :attributes => { :name => 'type', :value => 'RawHTMLBlock' } |
425 | end | 425 | end |
426 | 426 | ||
427 | should 'not allow normal users to add RawHTMLBlock' do | 427 | should 'not allow normal users to add RawHTMLBlock' do |
428 | profile.stubs(:is_admin?).with(profile.environment).returns(false) | 428 | profile.stubs(:is_admin?).with(profile.environment).returns(false) |
429 | @controller.stubs(:user).returns(profile) | 429 | @controller.stubs(:user).returns(profile) |
430 | get :add_block, :profile => 'designtestuser' | 430 | get :add_block, :profile => 'designtestuser' |
431 | - assert_no_tag :tag => 'input', :attributes => { :id => 'type_rawhtmlblock', :value => 'RawHTMLBlock' } | 431 | + assert_no_tag :tag => 'input', :attributes => { :name => 'type', :value => 'RawHTMLBlock' } |
432 | end | 432 | end |
433 | 433 | ||
434 | should 'editing article block displays right selected article' do | 434 | should 'editing article block displays right selected article' do |
test/integration/routing_test.rb
@@ -221,4 +221,11 @@ class RoutingTest < ActionController::IntegrationTest | @@ -221,4 +221,11 @@ class RoutingTest < ActionController::IntegrationTest | ||
221 | assert_routing('/chat/avatar/chemical-brothers', :controller => 'chat', :action => 'avatar', :id => 'chemical-brothers') | 221 | assert_routing('/chat/avatar/chemical-brothers', :controller => 'chat', :action => 'avatar', :id => 'chemical-brothers') |
222 | end | 222 | end |
223 | 223 | ||
224 | + def test_plugins_generic_routes | ||
225 | + assert_routing('/plugin/foo/public_bar/play/1', {:controller => 'foo_plugin_public_bar', :action => 'play', :id => '1'}) | ||
226 | + assert_routing('/profile/test/plugin/foo/profile_bar/play/1', {:controller => 'foo_plugin_profile_bar', :action => 'play', :id => '1', :profile => 'test'}) | ||
227 | + assert_routing('/myprofile/test/plugin/foo/myprofile_bar/play/1', {:controller => 'foo_plugin_myprofile_bar', :action => 'play', :id => '1', :profile => 'test'}) | ||
228 | + assert_routing('/admin/plugin/foo/admin_bar/play/1', {:controller => 'foo_plugin_admin_bar', :action => 'play', :id => '1'}) | ||
229 | + end | ||
230 | + | ||
224 | end | 231 | end |
@@ -0,0 +1,79 @@ | @@ -0,0 +1,79 @@ | ||
1 | +require File.dirname(__FILE__) + '/../test_helper' | ||
2 | + | ||
3 | +class BoxTest < ActiveSupport::TestCase | ||
4 | + | ||
5 | + should 'list allowed blocks for center box' do | ||
6 | + blocks = Box.new(:position => 1).acceptable_blocks | ||
7 | + | ||
8 | + assert !blocks.include?('block') | ||
9 | + assert !blocks.include?('disabled-enterprise-message-block') | ||
10 | + assert !blocks.include?('featured-products-block') | ||
11 | + assert !blocks.include?('products-block') | ||
12 | + assert !blocks.include?('profile-info-block') | ||
13 | + assert !blocks.include?('profile-list-block') | ||
14 | + assert !blocks.include?('profile-search-block') | ||
15 | + assert !blocks.include?('slideshow-block') | ||
16 | + assert !blocks.include?('location-block') | ||
17 | + | ||
18 | + assert blocks.include?('article-block') | ||
19 | + assert blocks.include?('blog-archives-block') | ||
20 | + assert blocks.include?('categories-block') | ||
21 | + assert blocks.include?('communities-block') | ||
22 | + assert blocks.include?('enterprises-block') | ||
23 | + assert blocks.include?('environment-statistics-block') | ||
24 | + assert blocks.include?('fans-block') | ||
25 | + assert blocks.include?('favorite-enterprises-block') | ||
26 | + assert blocks.include?('feed-reader-block') | ||
27 | + assert blocks.include?('friends-block') | ||
28 | + assert blocks.include?('highlights-block') | ||
29 | + assert blocks.include?('link-list-block') | ||
30 | + assert blocks.include?('login-block') | ||
31 | + assert blocks.include?('main-block') | ||
32 | + assert blocks.include?('members-block') | ||
33 | + assert blocks.include?('my-network-block') | ||
34 | + assert blocks.include?('people-block') | ||
35 | + assert blocks.include?('profile-image-block') | ||
36 | + assert blocks.include?('raw-html-block') | ||
37 | + assert blocks.include?('recent-documents-block') | ||
38 | + assert blocks.include?('sellers-search-block') | ||
39 | + assert blocks.include?('tags-block') | ||
40 | + end | ||
41 | + | ||
42 | + should 'list allowed blocks for box at position 2' do | ||
43 | + blocks = Box.new(:position => 2).acceptable_blocks | ||
44 | + | ||
45 | + assert !blocks.include?('main-block') | ||
46 | + assert !blocks.include?('block') | ||
47 | + assert !blocks.include?('profile-list-block') | ||
48 | + | ||
49 | + assert blocks.include?('article-block') | ||
50 | + assert blocks.include?('blog-archives-block') | ||
51 | + assert blocks.include?('categories-block') | ||
52 | + assert blocks.include?('communities-block') | ||
53 | + assert blocks.include?('disabled-enterprise-message-block') | ||
54 | + assert blocks.include?('enterprises-block') | ||
55 | + assert blocks.include?('environment-statistics-block') | ||
56 | + assert blocks.include?('fans-block') | ||
57 | + assert blocks.include?('favorite-enterprises-block') | ||
58 | + assert blocks.include?('featured-products-block') | ||
59 | + assert blocks.include?('feed-reader-block') | ||
60 | + assert blocks.include?('friends-block') | ||
61 | + assert blocks.include?('highlights-block') | ||
62 | + assert blocks.include?('link-list-block') | ||
63 | + assert blocks.include?('location-block') | ||
64 | + assert blocks.include?('login-block') | ||
65 | + assert blocks.include?('members-block') | ||
66 | + assert blocks.include?('my-network-block') | ||
67 | + assert blocks.include?('people-block') | ||
68 | + assert blocks.include?('products-block') | ||
69 | + assert blocks.include?('profile-image-block') | ||
70 | + assert blocks.include?('profile-info-block') | ||
71 | + assert blocks.include?('profile-search-block') | ||
72 | + assert blocks.include?('raw-html-block') | ||
73 | + assert blocks.include?('recent-documents-block') | ||
74 | + assert blocks.include?('sellers-search-block') | ||
75 | + assert blocks.include?('slideshow-block') | ||
76 | + assert blocks.include?('tags-block') | ||
77 | + end | ||
78 | + | ||
79 | +end |
test/unit/cms_helper_test.rb
@@ -5,6 +5,7 @@ class CmsHelperTest < ActiveSupport::TestCase | @@ -5,6 +5,7 @@ class CmsHelperTest < ActiveSupport::TestCase | ||
5 | include CmsHelper | 5 | include CmsHelper |
6 | include BlogHelper | 6 | include BlogHelper |
7 | include ApplicationHelper | 7 | include ApplicationHelper |
8 | + include ActionView::Helpers::UrlHelper | ||
8 | 9 | ||
9 | should 'show default options for article' do | 10 | should 'show default options for article' do |
10 | CmsHelperTest.any_instance.stubs(:controller).returns(ActionController::Base.new) | 11 | CmsHelperTest.any_instance.stubs(:controller).returns(ActionController::Base.new) |
@@ -47,14 +48,18 @@ class CmsHelperTest < ActiveSupport::TestCase | @@ -47,14 +48,18 @@ class CmsHelperTest < ActiveSupport::TestCase | ||
47 | end | 48 | end |
48 | 49 | ||
49 | should 'display spread button when profile is a person' do | 50 | should 'display spread button when profile is a person' do |
51 | + @controller = ApplicationController.new | ||
52 | + @plugins.stubs(:dispatch).returns([]) | ||
50 | profile = fast_create(Person) | 53 | profile = fast_create(Person) |
51 | article = fast_create(TinyMceArticle, :name => 'My article', :profile_id => profile.id) | 54 | article = fast_create(TinyMceArticle, :name => 'My article', :profile_id => profile.id) |
52 | - expects(:button_without_text).with(:spread, 'Spread this', :action => 'publish', :id => article.id) | 55 | + expects(:link_to).with('Spread this', {:action => 'publish', :id => article.id}, :class => 'button with-text icon-spread', :title => nil) |
53 | 56 | ||
54 | result = display_spread_button(profile, article) | 57 | result = display_spread_button(profile, article) |
55 | end | 58 | end |
56 | 59 | ||
57 | should 'display spread button when profile is a community and env has portal_community' do | 60 | should 'display spread button when profile is a community and env has portal_community' do |
61 | + @controller = ApplicationController.new | ||
62 | + @plugins.stubs(:dispatch).returns([]) | ||
58 | env = fast_create(Environment) | 63 | env = fast_create(Environment) |
59 | env.expects(:portal_community).returns(true) | 64 | env.expects(:portal_community).returns(true) |
60 | profile = fast_create(Community, :environment_id => env.id) | 65 | profile = fast_create(Community, :environment_id => env.id) |
@@ -62,12 +67,14 @@ class CmsHelperTest < ActiveSupport::TestCase | @@ -62,12 +67,14 @@ class CmsHelperTest < ActiveSupport::TestCase | ||
62 | 67 | ||
63 | article = fast_create(TinyMceArticle, :name => 'My article', :profile_id => profile.id) | 68 | article = fast_create(TinyMceArticle, :name => 'My article', :profile_id => profile.id) |
64 | 69 | ||
65 | - expects(:button_without_text).with(:spread, 'Spread this', :action => 'publish_on_portal_community', :id => article.id) | 70 | + expects(:link_to).with('Spread this', {:action => 'publish_on_portal_community', :id => article.id}, :class => 'button with-text icon-spread', :title => nil) |
66 | 71 | ||
67 | result = display_spread_button(profile, article) | 72 | result = display_spread_button(profile, article) |
68 | end | 73 | end |
69 | 74 | ||
70 | should 'not display spread button when profile is a community and env has not portal_community' do | 75 | should 'not display spread button when profile is a community and env has not portal_community' do |
76 | + @controller = ApplicationController.new | ||
77 | + @plugins.stubs(:dispatch).returns([]) | ||
71 | env = fast_create(Environment) | 78 | env = fast_create(Environment) |
72 | env.expects(:portal_community).returns(nil) | 79 | env.expects(:portal_community).returns(nil) |
73 | profile = fast_create(Community, :environment_id => env.id) | 80 | profile = fast_create(Community, :environment_id => env.id) |
@@ -75,31 +82,37 @@ class CmsHelperTest < ActiveSupport::TestCase | @@ -75,31 +82,37 @@ class CmsHelperTest < ActiveSupport::TestCase | ||
75 | 82 | ||
76 | article = fast_create(TinyMceArticle, :name => 'My article', :profile_id => profile.id) | 83 | article = fast_create(TinyMceArticle, :name => 'My article', :profile_id => profile.id) |
77 | 84 | ||
78 | - expects(:button_without_text).with(:spread, 'Spread this', :action => 'publish_on_portal_community', :id => article.id).never | 85 | + expects(:link_to).with('Spread this', {:action => 'publish_on_portal_community', :id => article.id}, :class => 'button with-text icon-spread', :title => nil).never |
79 | 86 | ||
80 | result = display_spread_button(profile, article) | 87 | result = display_spread_button(profile, article) |
81 | end | 88 | end |
82 | 89 | ||
83 | should 'display delete_button to folder' do | 90 | should 'display delete_button to folder' do |
91 | + @controller = ApplicationController.new | ||
92 | + @plugins.stubs(:dispatch).returns([]) | ||
84 | profile = fast_create(Profile) | 93 | profile = fast_create(Profile) |
85 | name = 'My folder' | 94 | name = 'My folder' |
86 | folder = fast_create(Folder, :name => name, :profile_id => profile.id) | 95 | folder = fast_create(Folder, :name => name, :profile_id => profile.id) |
87 | confirm_message = "Are you sure that you want to remove the folder \"#{name}\"? Note that all the items inside it will also be removed!" | 96 | confirm_message = "Are you sure that you want to remove the folder \"#{name}\"? Note that all the items inside it will also be removed!" |
88 | - expects(:button_without_text).with(:delete, 'Delete', {:action => 'destroy', :id => folder.id}, :method => :post, :confirm => confirm_message) | 97 | + expects(:link_to).with('Delete', {:action => 'destroy', :id => folder.id}, :method => :post, :confirm => confirm_message, :class => 'button with-text icon-delete', :title => nil) |
89 | 98 | ||
90 | result = display_delete_button(folder) | 99 | result = display_delete_button(folder) |
91 | end | 100 | end |
92 | 101 | ||
93 | should 'display delete_button to article' do | 102 | should 'display delete_button to article' do |
103 | + @controller = ApplicationController.new | ||
104 | + @plugins.stubs(:dispatch).returns([]) | ||
94 | profile = fast_create(Profile) | 105 | profile = fast_create(Profile) |
95 | name = 'My article' | 106 | name = 'My article' |
96 | article = fast_create(TinyMceArticle, :name => name, :profile_id => profile.id) | 107 | article = fast_create(TinyMceArticle, :name => name, :profile_id => profile.id) |
97 | confirm_message = "Are you sure that you want to remove the item \"#{name}\"?" | 108 | confirm_message = "Are you sure that you want to remove the item \"#{name}\"?" |
98 | - expects(:button_without_text).with(:delete, 'Delete', {:action => 'destroy', :id => article.id}, :method => :post, :confirm => confirm_message) | 109 | + expects(:link_to).with('Delete', {:action => 'destroy', :id => article.id}, :method => :post, :confirm => confirm_message, :class => 'button with-text icon-delete', :title => nil) |
99 | 110 | ||
100 | result = display_delete_button(article) | 111 | result = display_delete_button(article) |
101 | end | 112 | end |
102 | 113 | ||
114 | + def link_to(text, *args); puts text; puts args.inspect; text; end | ||
115 | + | ||
103 | end | 116 | end |
104 | 117 | ||
105 | module RssFeedHelper | 118 | module RssFeedHelper |
test/unit/colorbox_helper_test.rb
@@ -27,4 +27,10 @@ class ColorboxHelperTest < ActiveSupport::TestCase | @@ -27,4 +27,10 @@ class ColorboxHelperTest < ActiveSupport::TestCase | ||
27 | assert_equal '[button]', colorbox_button('type', 'label', { :action => 'popup'}) | 27 | assert_equal '[button]', colorbox_button('type', 'label', { :action => 'popup'}) |
28 | end | 28 | end |
29 | 29 | ||
30 | + should 'provide colorbox_icon_button' do | ||
31 | + expects(:icon_button).with('type', 'label', { :action => 'popup'}, has_entries({ :class => 'colorbox' })).returns('[button]') | ||
32 | + | ||
33 | + assert_equal '[button]', colorbox_icon_button('type', 'label', { :action => 'popup'}) | ||
34 | + end | ||
35 | + | ||
30 | end | 36 | end |
test/unit/comment_test.rb
@@ -555,6 +555,14 @@ class CommentTest < ActiveSupport::TestCase | @@ -555,6 +555,14 @@ class CommentTest < ActiveSupport::TestCase | ||
555 | assert_equal 'bar', c.referrer | 555 | assert_equal 'bar', c.referrer |
556 | end | 556 | end |
557 | 557 | ||
558 | + should 'delegate environment to article' do | ||
559 | + profile = fast_create(Profile, :environment_id => Environment.default) | ||
560 | + article = fast_create(Article, :profile_id => profile.id) | ||
561 | + comment = fast_create(Comment, :source_id => article.id, :source_type => 'Article') | ||
562 | + | ||
563 | + assert_equal Environment.default, comment.environment | ||
564 | + end | ||
565 | + | ||
558 | private | 566 | private |
559 | 567 | ||
560 | def create_comment(args = {}) | 568 | def create_comment(args = {}) |
test/unit/task_test.rb
@@ -14,7 +14,7 @@ class TaskTest < ActiveSupport::TestCase | @@ -14,7 +14,7 @@ class TaskTest < ActiveSupport::TestCase | ||
14 | t.requestor = 1 | 14 | t.requestor = 1 |
15 | end | 15 | end |
16 | assert_nothing_raised do | 16 | assert_nothing_raised do |
17 | - t.requestor = Person.new | 17 | + t.requestor = Profile.new |
18 | end | 18 | end |
19 | end | 19 | end |
20 | 20 |
test/unit/user_test.rb
@@ -403,12 +403,29 @@ class UserTest < ActiveSupport::TestCase | @@ -403,12 +403,29 @@ class UserTest < ActiveSupport::TestCase | ||
403 | assert_equal 'Test User', user.name | 403 | assert_equal 'Test User', user.name |
404 | end | 404 | end |
405 | 405 | ||
406 | - should 'respond name with login, if there is no person related' do | 406 | + should 'respond name with login, if there is no person related and name defined' do |
407 | user = create_user('testuser') | 407 | user = create_user('testuser') |
408 | user.person = nil | 408 | user.person = nil |
409 | + user.name = nil | ||
409 | assert_equal 'testuser', user.name | 410 | assert_equal 'testuser', user.name |
410 | end | 411 | end |
411 | 412 | ||
413 | + should 'respond name with user name attribute' do | ||
414 | + user = create_user('testuser') | ||
415 | + user.person = nil | ||
416 | + user.name = 'Another User' | ||
417 | + user.login = 'Login User' | ||
418 | + assert_equal 'Another User', user.name | ||
419 | + end | ||
420 | + | ||
421 | + should 'respond name with related person name although user name attribute is defined' do | ||
422 | + user = create_user('testuser') | ||
423 | + user.person.name = 'Person Name' | ||
424 | + user.name = 'Another User' | ||
425 | + user.login = 'Login User' | ||
426 | + assert_equal 'Person Name', user.name | ||
427 | + end | ||
428 | + | ||
412 | should 'have activation code' do | 429 | should 'have activation code' do |
413 | user = create_user('testuser') | 430 | user = create_user('testuser') |
414 | assert_respond_to user, :activation_code | 431 | assert_respond_to user, :activation_code |
@@ -430,6 +447,13 @@ class UserTest < ActiveSupport::TestCase | @@ -430,6 +447,13 @@ class UserTest < ActiveSupport::TestCase | ||
430 | assert_equal 'pending@activation.com', ActionMailer::Base.deliveries.last['to'].to_s | 447 | assert_equal 'pending@activation.com', ActionMailer::Base.deliveries.last['to'].to_s |
431 | end | 448 | end |
432 | 449 | ||
450 | + should 'not try to deliver email to template users' do | ||
451 | + Person.any_instance.stubs(:is_template?).returns(true) | ||
452 | + assert_no_difference ActionMailer::Base.deliveries, :size do | ||
453 | + new_user | ||
454 | + end | ||
455 | + end | ||
456 | + | ||
433 | should 'not mass assign activated at' do | 457 | should 'not mass assign activated at' do |
434 | user = User.new :activated_at => 5.days.ago | 458 | user = User.new :activated_at => 5.days.ago |
435 | assert_nil user.activated_at | 459 | assert_nil user.activated_at |
@@ -492,6 +516,12 @@ class UserTest < ActiveSupport::TestCase | @@ -492,6 +516,12 @@ class UserTest < ActiveSupport::TestCase | ||
492 | assert !user.activate | 516 | assert !user.activate |
493 | end | 517 | end |
494 | 518 | ||
519 | + should 'be able to skip the password requirement' do | ||
520 | + user = User.new(:login => 'quire', :email => 'quire@example.com') | ||
521 | + user.not_require_password! | ||
522 | + assert user.save! | ||
523 | + end | ||
524 | + | ||
495 | protected | 525 | protected |
496 | def new_user(options = {}) | 526 | def new_user(options = {}) |
497 | user = User.new({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options)) | 527 | user = User.new({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options)) |