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
Too many changes.
To preserve performance only 100 of 159 files displayed.
app/controllers/application_controller.rb
... | ... | @@ -114,7 +114,9 @@ class ApplicationController < ActionController::Base |
114 | 114 | # plugin to the current controller being initialized. |
115 | 115 | def init_noosfero_plugins_controller_filters |
116 | 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 | 120 | self.class.send(plugin_filter[:type], plugin.class.name.underscore + '_' + plugin_filter[:method_name], (plugin_filter[:options] || {})) |
119 | 121 | self.class.send(:define_method, plugin.class.name.underscore + '_' + plugin_filter[:method_name], plugin_filter[:block]) |
120 | 122 | end | ... | ... |
app/controllers/box_organizer_controller.rb
... | ... | @@ -68,7 +68,8 @@ class BoxOrganizerController < ApplicationController |
68 | 68 | raise ArgumentError.new("Type %s is not allowed. Go away." % type) |
69 | 69 | end |
70 | 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 | 73 | @boxes = boxes_holder.boxes |
73 | 74 | render :action => 'add_block', :layout => false |
74 | 75 | end | ... | ... |
app/controllers/my_profile/profile_design_controller.rb
... | ... | @@ -5,7 +5,7 @@ class ProfileDesignController < BoxOrganizerController |
5 | 5 | protect 'edit_profile_design', :profile |
6 | 6 | |
7 | 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 | 10 | # blocks exclusive for organizations |
11 | 11 | if profile.has_members? | ... | ... |
app/controllers/public/account_controller.rb
... | ... | @@ -25,11 +25,13 @@ class AccountController < ApplicationController |
25 | 25 | |
26 | 26 | # action to perform login to the application |
27 | 27 | def login |
28 | - @user = User.new | |
29 | - @person = @user.build_person | |
30 | 28 | store_location(request.referer) unless session[:return_to] |
31 | 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 | 35 | if logged_in? |
34 | 36 | if params[:remember_me] == "1" |
35 | 37 | self.current_user.remember_me |
... | ... | @@ -41,7 +43,6 @@ class AccountController < ApplicationController |
41 | 43 | end |
42 | 44 | else |
43 | 45 | session[:notice] = _('Incorrect username or password') if redirect? |
44 | - redirect_to :back if redirect? | |
45 | 46 | end |
46 | 47 | end |
47 | 48 | |
... | ... | @@ -56,6 +57,11 @@ class AccountController < ApplicationController |
56 | 57 | |
57 | 58 | # action to register an user to the application |
58 | 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 | 65 | @invitation_code = params[:invitation_code] |
60 | 66 | begin |
61 | 67 | if params[:user] |
... | ... | @@ -125,6 +131,10 @@ class AccountController < ApplicationController |
125 | 131 | # |
126 | 132 | # Posts back. |
127 | 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 | 138 | @change_password = ChangePassword.new(params[:change_password]) |
129 | 139 | |
130 | 140 | if request.post? |
... | ... | @@ -316,4 +326,13 @@ class AccountController < ApplicationController |
316 | 326 | end |
317 | 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 | 338 | end | ... | ... |
app/controllers/public/content_viewer_controller.rb
... | ... | @@ -2,6 +2,8 @@ class ContentViewerController < ApplicationController |
2 | 2 | |
3 | 3 | needs_profile |
4 | 4 | |
5 | + before_filter :comment_author, :only => :edit_comment | |
6 | + | |
5 | 7 | helper ProfileHelper |
6 | 8 | helper TagsHelper |
7 | 9 | |
... | ... | @@ -121,6 +123,27 @@ class ContentViewerController < ApplicationController |
121 | 123 | end |
122 | 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 | 147 | protected |
125 | 148 | |
126 | 149 | def add_comment |
... | ... | @@ -198,4 +221,13 @@ class ContentViewerController < ApplicationController |
198 | 221 | end |
199 | 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 | 233 | end | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -265,9 +265,9 @@ module ApplicationHelper |
265 | 265 | |
266 | 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 | 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 | 272 | search_name = String.new(name) |
273 | 273 | if search_name.include?("/") |
... | ... | @@ -285,28 +285,17 @@ module ApplicationHelper |
285 | 285 | partial_for_class_in_view_path(klass.superclass, view_path) |
286 | 286 | end |
287 | 287 | |
288 | - def partial_for_class(klass) | |
288 | + def partial_for_class(klass, suffix=nil) | |
289 | 289 | raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil? |
290 | 290 | name = klass.name.underscore |
291 | 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 | 293 | return partial if partial |
294 | 294 | end |
295 | 295 | |
296 | 296 | raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' |
297 | 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 | 299 | def view_for_profile_actions(klass) |
311 | 300 | raise ArgumentError, 'No profile actions view for this class.' if klass.nil? |
312 | 301 | |
... | ... | @@ -1323,6 +1312,19 @@ module ApplicationHelper |
1323 | 1312 | end |
1324 | 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 | 1328 | def template_options(klass, field_name) |
1327 | 1329 | return '' if klass.templates.count == 0 |
1328 | 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 | 1390 | result |
1389 | 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 | 1408 | end | ... | ... |
app/helpers/boxes_helper.rb
... | ... | @@ -162,9 +162,6 @@ module BoxesHelper |
162 | 162 | # |
163 | 163 | # +box+ is always needed |
164 | 164 | def block_target(box, block = nil) |
165 | - # FIXME hardcoded | |
166 | - return '' if box.position == 1 | |
167 | - | |
168 | 165 | id = |
169 | 166 | if block.nil? |
170 | 167 | "end-of-box-#{box.id}" |
... | ... | @@ -172,14 +169,11 @@ module BoxesHelper |
172 | 169 | "before-block-#{block.id}" |
173 | 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 | 173 | end |
177 | 174 | |
178 | 175 | # makes the given block draggable so it can be moved away. |
179 | 176 | def block_handle(block) |
180 | - # FIXME hardcoded | |
181 | - return '' if block.box.position == 1 | |
182 | - | |
183 | 177 | draggable_element("block-#{block.id}", :revert => true) |
184 | 178 | end |
185 | 179 | |
... | ... | @@ -211,7 +205,7 @@ module BoxesHelper |
211 | 205 | end |
212 | 206 | |
213 | 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 | 209 | end |
216 | 210 | |
217 | 211 | if !block.main? | ... | ... |
app/helpers/cms_helper.rb
... | ... | @@ -42,13 +42,25 @@ module CmsHelper |
42 | 42 | |
43 | 43 | def display_spread_button(profile, article) |
44 | 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 | 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 | 48 | end |
49 | 49 | end |
50 | 50 | |
51 | 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 | 65 | end |
54 | 66 | end | ... | ... |
app/helpers/colorbox_helper.rb
... | ... | @@ -8,6 +8,10 @@ module ColorboxHelper |
8 | 8 | button(type, label, url, colorbox_options(options)) |
9 | 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 | 15 | # options must be an HTML options hash as passed to link_to etc. |
12 | 16 | # |
13 | 17 | # returns a new hash with colorbox class added. Keeps existing classes. | ... | ... |
app/helpers/forms_helper.rb
... | ... | @@ -155,6 +155,119 @@ module FormsHelper |
155 | 155 | return result |
156 | 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 | 271 | protected |
159 | 272 | def self.next_id_number |
160 | 273 | if defined? @@id_num | ... | ... |
app/models/box.rb
... | ... | @@ -2,4 +2,76 @@ class Box < ActiveRecord::Base |
2 | 2 | belongs_to :owner, :polymorphic => true |
3 | 3 | acts_as_list :scope => 'owner_id = #{owner_id} and owner_type = \'#{owner_type}\'' |
4 | 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 | 77 | end | ... | ... |
app/models/comment.rb
app/models/person.rb
... | ... | @@ -71,10 +71,7 @@ class Person < Profile |
71 | 71 | Friendship.find(:all, :conditions => { :friend_id => person.id}).each { |friendship| friendship.destroy } |
72 | 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 | 76 | def can_control_scrap?(scrap) |
80 | 77 | begin | ... | ... |
app/models/task.rb
... | ... | @@ -31,7 +31,7 @@ class Task < ActiveRecord::Base |
31 | 31 | end |
32 | 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 | 35 | belongs_to :target, :foreign_key => :target_id, :polymorphic => true |
36 | 36 | |
37 | 37 | validates_uniqueness_of :code, :on => :create | ... | ... |
app/models/user.rb
... | ... | @@ -30,7 +30,7 @@ class User < ActiveRecord::Base |
30 | 30 | |
31 | 31 | after_create do |user| |
32 | 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 | 34 | user.person.name ||= user.login |
35 | 35 | user.person.visible = false unless user.activated? |
36 | 36 | user.person.save! |
... | ... | @@ -88,13 +88,13 @@ class User < ActiveRecord::Base |
88 | 88 | attr_protected :activated_at |
89 | 89 | |
90 | 90 | # Virtual attribute for the unencrypted password |
91 | - attr_accessor :password | |
91 | + attr_accessor :password, :name | |
92 | 92 | |
93 | 93 | validates_presence_of :login, :email |
94 | 94 | validates_format_of :login, :with => Profile::IDENTIFIER_FORMAT, :if => (lambda {|user| !user.login.blank?}) |
95 | 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 | 98 | validates_confirmation_of :password, :if => :password_required? |
99 | 99 | validates_length_of :login, :within => 2..40, :if => (lambda {|user| !user.login.blank?}) |
100 | 100 | validates_length_of :email, :within => 3..100, :if => (lambda {|user| !user.email.blank?}) |
... | ... | @@ -228,7 +228,12 @@ class User < ActiveRecord::Base |
228 | 228 | end |
229 | 229 | |
230 | 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 | 237 | end |
233 | 238 | |
234 | 239 | def enable_email! |
... | ... | @@ -274,6 +279,11 @@ class User < ActiveRecord::Base |
274 | 279 | 15 # in minutes |
275 | 280 | end |
276 | 281 | |
282 | + | |
283 | + def not_require_password! | |
284 | + @is_password_required = false | |
285 | + end | |
286 | + | |
277 | 287 | protected |
278 | 288 | # before filter |
279 | 289 | def encrypt_password |
... | ... | @@ -282,9 +292,13 @@ class User < ActiveRecord::Base |
282 | 292 | self.password_type ||= User.system_encryption_method.to_s |
283 | 293 | self.crypted_password = encrypt(password) |
284 | 294 | end |
285 | - | |
295 | + | |
286 | 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 | 302 | end |
289 | 303 | |
290 | 304 | def make_activation_code |
... | ... | @@ -292,6 +306,7 @@ class User < ActiveRecord::Base |
292 | 306 | end |
293 | 307 | |
294 | 308 | def deliver_activation_code |
309 | + return if person.is_template? | |
295 | 310 | User::Mailer.deliver_activation_code(self) unless self.activation_code.blank? |
296 | 311 | end |
297 | 312 | ... | ... |
app/views/account/login.rhtml
... | ... | @@ -13,6 +13,8 @@ |
13 | 13 | |
14 | 14 | <%= f.password_field :password %> |
15 | 15 | |
16 | + <%= @plugins.dispatch(:login_extra_contents).collect { |content| instance_eval(&content) }.join("") %> | |
17 | + | |
16 | 18 | <% button_bar do %> |
17 | 19 | <%= submit_button( 'login', _('Log in') )%> |
18 | 20 | <% if is_thickbox %> |
... | ... | @@ -23,8 +25,13 @@ |
23 | 25 | <% end %> |
24 | 26 | |
25 | 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 | 35 | <% end %> |
29 | 36 | |
30 | 37 | </div><!-- end class="login-box" --> | ... | ... |
app/views/account/login_block.rhtml
... | ... | @@ -9,25 +9,30 @@ |
9 | 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 | 20 | <% button_bar do %> |
20 | 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 | 27 | <% end %> |
25 | 28 | |
26 | 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 | 37 | </div> |
33 | 38 | ... | ... |
... | ... | @@ -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 | 1 | <strong><%= _('Highlights') %></strong> |
2 | -<div id='edit-highlights-block'> | |
2 | +<div id='edit-highlights-block' style='width:450px'> | |
3 | 3 | <table id='highlights' class='noborder'> |
4 | 4 | <tr><th><%= _('Image') %></th><th><%= _('Address') %></th><th><%= _('Position') %></th><th><%= _('Title') %></th></tr> |
5 | 5 | <% for image in @block.images do %> | ... | ... |
app/views/box_organizer/_link_list_block.rhtml
1 | 1 | <strong><%= _('Links') %></strong> |
2 | -<div id='edit-link-list-block'> | |
2 | +<div id='edit-link-list-block' style='width:450px'> | |
3 | 3 | <table id='links' class='noborder'> |
4 | 4 | <tr><th><%= _('Icon') %></th><th><%= _('Name') %></th><th><%= _('Address') %></th></tr> |
5 | 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 | 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 | 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 | 1 | <h1><%= _('Editing sideboxes')%></h1> |
2 | 2 | |
3 | 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 | 5 | <%= button(:back, _('Back to control panel'), :controller => (profile.nil? ? 'admin_panel': 'profile_editor')) %> |
6 | 6 | <% end %> | ... | ... |
app/views/cms/view.rhtml
... | ... | @@ -49,13 +49,13 @@ |
49 | 49 | <%= article.class.short_description %> |
50 | 50 | </td> |
51 | 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 | 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 | 57 | <% end %> |
58 | - <%= display_delete_button(article) %> | |
58 | + <%= display_delete_button(article) if !remove_content_button(:delete) %> | |
59 | 59 | </td> |
60 | 60 | </tr> |
61 | 61 | <% end %> | ... | ... |
app/views/content_viewer/_article_toolbar.rhtml
1 | 1 | <div<%= user && " class='logged-in'" %>> |
2 | 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 | 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 | 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 | 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 | 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 | 25 | <% end %> |
26 | + <%= expirable_button @page, :spread, content, url if url %> | |
28 | 27 | <% end %> |
29 | 28 | |
30 | 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 | 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 | 38 | <% end %> |
38 | 39 | |
... | ... | @@ -40,8 +41,11 @@ |
40 | 41 | <%= button('upload-file', _('Upload files'), profile.admin_url.merge(:controller => 'cms', :action => 'upload_files', :parent_id => (@page.folder? ? @page : @page.parent))) %> |
41 | 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 | 49 | <% end %> |
46 | 50 | |
47 | 51 | <%= report_abuse(profile, :link, @page) %> | ... | ... |
app/views/content_viewer/_comment.rhtml
... | ... | @@ -63,6 +63,11 @@ |
63 | 63 | <% end %> |
64 | 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 | 71 | <% if logged_in? && (user == profile || user == comment.author || user.has_permission?(:moderate_comments, profile)) %> |
67 | 72 | |
68 | 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 | 32 | |
33 | 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 | 47 | <% unless pass_without_comment_captcha? %> |
46 | 48 | <div id="recaptcha-container" style="display: none"> |
... | ... | @@ -59,7 +61,7 @@ function submit_comment_form(button) { |
59 | 61 | </script> |
60 | 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 | 65 | <%= hidden_field_tag(:confirm, 'false') %> |
64 | 66 | |
65 | 67 | <%= required_fields_message %> |
... | ... | @@ -84,7 +86,11 @@ function submit_comment_form(button) { |
84 | 86 | |
85 | 87 | <% button_bar do %> |
86 | 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 | 94 | <% end %> |
89 | 95 | <% end %> |
90 | 96 | ... | ... |
app/views/content_viewer/view_page.rhtml
... | ... | @@ -98,7 +98,7 @@ |
98 | 98 | </ul> |
99 | 99 | |
100 | 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 | 102 | <% end %> |
103 | 103 | </div><!-- end class="comments" --> |
104 | 104 | ... | ... |
app/views/layouts/_javascript.rhtml
... | ... | @@ -2,7 +2,8 @@ |
2 | 2 | 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox', |
3 | 3 | 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate', |
4 | 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 | 8 | <% language = FastGettext.locale %> |
8 | 9 | <% %w{messages methods}.each do |type| %> | ... | ... |
app/views/layouts/application-ng.rhtml
... | ... | @@ -56,10 +56,18 @@ |
56 | 56 | <%= usermenu_logged_in %> |
57 | 57 | </span> |
58 | 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 | 63 | <div id='inlineLoginBox' style='display: none;'> |
61 | 64 | <%= render :file => 'account/login', :locals => { :is_thickbox => true } %> |
62 | 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 | 71 | </span> |
64 | 72 | <form action="/search" class="search_form" method="get" class="clean"> |
65 | 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 | 62 | </script> |
63 | 63 | <% end %> |
64 | 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 | 69 | <%= link_to_function _('Reply'), |
66 | 70 | "var f = add_comment_reply_form(this, %s); f.find('input[name=comment[title]], textarea').val(''); return false" % comment.id, |
67 | 71 | :class => 'comment-footer comment-footer-link comment-footer-hide', | ... | ... |
app/views/tasks/_task.rhtml
... | ... | @@ -50,13 +50,13 @@ |
50 | 50 | <% fields_for "tasks[#{task.id}][task]", task do |f| %> |
51 | 51 | <% if task.accept_details %> |
52 | 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 | 54 | </div> |
55 | 55 | <% end %> |
56 | 56 | |
57 | 57 | <% if task.reject_details %> |
58 | 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 | 60 | </div> |
61 | 61 | <% end %> |
62 | 62 | <% end %> | ... | ... |
config/routes.rb
... | ... | @@ -19,6 +19,7 @@ ActionController::Routing::Routes.draw do |map| |
19 | 19 | |
20 | 20 | # -- just remember to delete public/index.html. |
21 | 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 | 23 | map.connect '', :controller => "home", :conditions => { :if => lambda { |env| !Domain.hosting_profile_at(env[:host]) } } |
23 | 24 | map.home 'site/:action', :controller => 'home' |
24 | 25 | |
... | ... | @@ -121,9 +122,12 @@ ActionController::Routing::Routes.draw do |map| |
121 | 122 | # cache stuff - hack |
122 | 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 | 127 | # match requests for profiles that don't have a custom domain |
125 | 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 | 131 | # match requests for content in domains hosted for profiles |
128 | 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 | 12 | end |
13 | 13 | |
14 | 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 | 20 | File.directory?(entry) |
17 | 21 | end.each do |dir| |
18 | 22 | plugin_name = File.basename(dir) |
... | ... | @@ -31,6 +35,11 @@ class Noosfero::Plugin |
31 | 35 | if plugin_dependencies_ok |
32 | 36 | Rails.configuration.controller_paths << File.join(dir, 'controllers') |
33 | 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 | 43 | [ ActiveSupport::Dependencies.load_paths, $:].each do |path| |
35 | 44 | path << File.join(dir, 'models') |
36 | 45 | path << File.join(dir, 'lib') |
... | ... | @@ -211,28 +220,6 @@ class Noosfero::Plugin |
211 | 220 | nil |
212 | 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 | 223 | # This method will be called just before a comment is saved to the database. |
237 | 224 | # |
238 | 225 | # It can modify the comment in several ways. In special, a plugin can call |
... | ... | @@ -333,4 +320,71 @@ class Noosfero::Plugin |
333 | 320 | nil |
334 | 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 | 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 | 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 | 24 | map.connect 'myprofile/:profile/plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_myprofile' |
6 | 25 | map.connect 'admin/plugin/' + plugin_name + '/:action/:id', :controller => plugin_name + '_plugin_admin' |
7 | 26 | end |
8 | - | ... | ... |
plugins/custom_forms/controllers/custom_forms_plugin_myprofile_controller.rb
0 → 100644
... | ... | @@ -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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 @@ |
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 | 2 | |
3 | 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 | 14 | def choose_base_tool |
7 | 15 | @configuration_content = profile.articles.find(params[:id]) |
8 | 16 | @base_tools = Kalibro::BaseTool.all_names |
... | ... | @@ -11,19 +19,23 @@ class MezuroPluginMyprofileController < ProfileController |
11 | 19 | def choose_metric |
12 | 20 | @configuration_content = profile.articles.find(params[:id]) |
13 | 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 | 24 | end |
16 | - | |
25 | + | |
17 | 26 | def new_metric_configuration |
18 | 27 | @configuration_content = profile.articles.find(params[:id]) |
19 | 28 | @metric = Kalibro::BaseTool.find_by_name(params[:base_tool]).metric params[:metric_name] |
20 | 29 | end |
21 | - | |
30 | + | |
22 | 31 | def new_compound_metric_configuration |
23 | 32 | @configuration_content = profile.articles.find(params[:id]) |
24 | 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 | 37 | end |
26 | - | |
38 | + | |
27 | 39 | def edit_metric_configuration |
28 | 40 | @configuration_content = profile.articles.find(params[:id]) |
29 | 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 | 48 | @metric_configurations = @configuration_content.metric_configurations |
37 | 49 | @metric = @metric_configuration.metric |
38 | 50 | end |
39 | - | |
51 | + | |
40 | 52 | def create_metric_configuration |
41 | 53 | id = params[:id] |
42 | 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 | 62 | end |
46 | - | |
63 | + | |
47 | 64 | def create_compound_metric_configuration |
48 | 65 | id = params[:id] |
49 | 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 | 74 | end |
53 | 75 | |
54 | 76 | def update_metric_configuration |
... | ... | @@ -56,7 +78,11 @@ class MezuroPluginMyprofileController < ProfileController |
56 | 78 | metric_name = params[:metric_configuration][:metric][:name] |
57 | 79 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name) |
58 | 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 | 86 | end |
61 | 87 | |
62 | 88 | def update_compound_metric_configuration |
... | ... | @@ -64,7 +90,11 @@ class MezuroPluginMyprofileController < ProfileController |
64 | 90 | metric_name = params[:metric_configuration][:metric][:name] |
65 | 91 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, metric_name) |
66 | 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 | 98 | end |
69 | 99 | |
70 | 100 | def remove_metric_configuration |
... | ... | @@ -72,32 +102,41 @@ class MezuroPluginMyprofileController < ProfileController |
72 | 102 | metric_name = params[:metric_name] |
73 | 103 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(configuration_content.name, metric_name) |
74 | 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 | 110 | end |
77 | - | |
111 | + | |
78 | 112 | def new_range |
79 | 113 | @configuration_content = profile.articles.find(params[:id]) |
80 | 114 | @metric_name = params[:metric_name] |
81 | 115 | @range = Kalibro::Range.new |
116 | + @range_color = "#000000" | |
82 | 117 | end |
83 | - | |
118 | + | |
84 | 119 | def edit_range |
85 | 120 | @configuration_content = profile.articles.find(params[:id]) |
86 | 121 | @metric_name = params[:metric_name] |
87 | 122 | @beginning_id = params[:beginning_id] |
88 | 123 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(@configuration_content.name, @metric_name) |
89 | 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 | 126 | end |
91 | 127 | |
92 | 128 | def create_range |
93 | 129 | @configuration_content = profile.articles.find(params[:id]) |
94 | 130 | @range = Kalibro::Range.new params[:range] |
95 | 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 | 133 | metric_configuration.add_range(@range) |
98 | 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 | 138 | end |
100 | - | |
139 | + | |
101 | 140 | def update_range |
102 | 141 | configuration_content = profile.articles.find(params[:id]) |
103 | 142 | metric_name = params[:metric_name] |
... | ... | @@ -106,8 +145,11 @@ class MezuroPluginMyprofileController < ProfileController |
106 | 145 | index = metric_configuration.ranges.index{ |range| range.beginning == beginning_id.to_f || beginning_id == "-INF" } |
107 | 146 | metric_configuration.ranges[index] = Kalibro::Range.new params[:range] |
108 | 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 | 151 | end |
110 | - | |
152 | + | |
111 | 153 | def remove_range |
112 | 154 | configuration_content = profile.articles.find(params[:id]) |
113 | 155 | metric_name = params[:metric_name] |
... | ... | @@ -115,12 +157,31 @@ class MezuroPluginMyprofileController < ProfileController |
115 | 157 | metric_configuration = Kalibro::MetricConfiguration.find_by_configuration_name_and_metric_name(configuration_content.name, metric_name) |
116 | 158 | metric_configuration.ranges.delete_if { |range| range.beginning == beginning_id.to_f || beginning_id == "-INF" } |
117 | 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 | 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 | 169 | end |
124 | 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 | 187 | end | ... | ... |
plugins/mezuro/controllers/mezuro_plugin_profile_controller.rb
1 | 1 | class MezuroPluginProfileController < ProfileController |
2 | 2 | |
3 | 3 | append_view_path File.join(File.dirname(__FILE__) + '/../views') |
4 | - | |
4 | + | |
5 | + def error_page | |
6 | + @message = params[:message] | |
7 | + end | |
8 | + | |
5 | 9 | def project_state |
6 | 10 | @content = profile.articles.find(params[:id]) |
7 | 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 | 18 | end |
11 | 19 | |
12 | 20 | def project_error |
13 | 21 | @content = profile.articles.find(params[:id]) |
14 | 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 | 28 | end |
17 | 29 | |
18 | 30 | def project_result |
19 | 31 | @content = profile.articles.find(params[:id]) |
20 | 32 | date = params[:date] |
21 | 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 | 41 | def module_result |
26 | 42 | @content = profile.articles.find(params[:id]) |
27 | 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 | 51 | end |
30 | 52 | |
31 | 53 | def project_tree |
32 | 54 | @content = profile.articles.find(params[:id]) |
33 | 55 | date = params[:date] |
34 | 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 | 64 | end |
39 | 65 | |
40 | 66 | def module_metrics_history |
41 | 67 | metric_name = params[:metric_name] |
42 | 68 | @content = profile.articles.find(params[:id]) |
43 | 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 | 76 | end |
47 | 77 | |
48 | 78 | def module_grade_history |
49 | 79 | @content = profile.articles.find(params[:id]) |
50 | 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 | 89 | end |
54 | - | |
90 | + | |
55 | 91 | private |
56 | - | |
92 | + | |
57 | 93 | def filtering_metric_history(metric_name, module_history) |
58 | 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 | 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 | 99 | metric_result.metric.name.delete("() ") == metric_name |
64 | - end).first | |
100 | + end).first, metric_results_with_date.last] | |
65 | 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 | 104 | end |
69 | 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 | 120 | end | ... | ... |
plugins/mezuro/lib/kalibro/configuration.rb
... | ... | @@ -7,11 +7,7 @@ class Kalibro::Configuration < Kalibro::Model |
7 | 7 | end |
8 | 8 | |
9 | 9 | def metric_configurations |
10 | - if @metric_configuration != nil | |
11 | - @metric_configuration | |
12 | - else | |
13 | - [] | |
14 | - end | |
10 | + @metric_configuration.nil? ? [] : @metric_configuration | |
15 | 11 | end |
16 | 12 | |
17 | 13 | def metric_configurations=(metric_configurations) |
... | ... | @@ -19,19 +15,11 @@ class Kalibro::Configuration < Kalibro::Model |
19 | 15 | end |
20 | 16 | |
21 | 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 | 19 | end |
28 | 20 | |
29 | 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 | 23 | end |
36 | 24 | |
37 | 25 | def update_attributes(attributes={}) | ... | ... |
plugins/mezuro/lib/kalibro/metric_configuration.rb
... | ... | @@ -49,10 +49,14 @@ class Kalibro::MetricConfiguration < Kalibro::Model |
49 | 49 | end |
50 | 50 | |
51 | 51 | def destroy |
52 | - self.class.request("MetricConfiguration", :remove_metric_configuration, { | |
52 | + begin | |
53 | + self.class.request("MetricConfiguration", :remove_metric_configuration, { | |
53 | 54 | :configuration_name => configuration_name, |
54 | 55 | :metric_name=> metric.name |
55 | 56 | }) |
57 | + rescue Exception => exception | |
58 | + add_error exception | |
59 | + end | |
56 | 60 | end |
57 | 61 | |
58 | 62 | def to_hash | ... | ... |
plugins/mezuro/lib/kalibro/model.rb
1 | 1 | class Kalibro::Model |
2 | 2 | |
3 | + attr_accessor :errors | |
4 | + | |
3 | 5 | def initialize(attributes={}) |
4 | 6 | attributes.each { |field, value| send("#{field}=", value) if self.class.is_valid?(field) } |
7 | + @errors = [] | |
5 | 8 | end |
6 | 9 | |
7 | 10 | def to_hash(options={}) |
8 | 11 | hash = Hash.new |
9 | 12 | excepts = !options[:except].nil? ? options[:except] : [] |
13 | + excepts << :errors | |
10 | 14 | fields.each do |field| |
11 | 15 | if(!excepts.include?(field)) |
12 | 16 | field_value = send(field) |
... | ... | @@ -46,15 +50,17 @@ class Kalibro::Model |
46 | 50 | begin |
47 | 51 | self.class.request(save_endpoint, save_action, save_params) |
48 | 52 | true |
49 | - rescue Exception => error | |
50 | - false | |
53 | + rescue Exception => exception | |
54 | + add_error exception | |
55 | + false | |
51 | 56 | end |
52 | 57 | end |
53 | 58 | |
54 | 59 | def destroy |
55 | 60 | begin |
56 | 61 | self.class.request(destroy_endpoint, destroy_action, destroy_params) |
57 | - rescue Exception | |
62 | + rescue Exception => exception | |
63 | + add_error exception | |
58 | 64 | end |
59 | 65 | end |
60 | 66 | |
... | ... | @@ -123,4 +129,9 @@ class Kalibro::Model |
123 | 129 | {"#{class_name.underscore}_name".to_sym => self.name} |
124 | 130 | end |
125 | 131 | |
132 | + def add_error(exception) | |
133 | + @errors << exception | |
134 | + end | |
135 | + | |
126 | 136 | end |
137 | + | ... | ... |
plugins/mezuro/lib/kalibro/project.rb
1 | 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 | 5 | def self.all_names |
6 | 6 | request("Project", :get_project_names)[:project_name] |
... | ... | @@ -15,23 +15,35 @@ class Kalibro::Project < Kalibro::Model |
15 | 15 | end |
16 | 16 | |
17 | 17 | def error=(value) |
18 | - @error = Kalibro::Error.to_object value | |
18 | + @kalibro_error = Kalibro::Error.to_object value | |
19 | 19 | end |
20 | 20 | |
21 | 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 | 31 | end |
28 | 32 | |
29 | 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 | 39 | end |
32 | 40 | |
33 | 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 | 47 | end |
36 | 48 | |
37 | 49 | end | ... | ... |
plugins/mezuro/lib/kalibro/project_result.rb
... | ... | @@ -75,21 +75,17 @@ class Kalibro::ProjectResult < Kalibro::Model |
75 | 75 | ('%2d' % amount).sub(/\s/, '0') |
76 | 76 | end |
77 | 77 | |
78 | - def node_of(module_name) | |
78 | + def node(module_name) | |
79 | 79 | if module_name.nil? or module_name == project.name |
80 | 80 | node = source_tree |
81 | 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 | 89 | end |
94 | 90 | |
95 | 91 | private | ... | ... |
plugins/mezuro/lib/kalibro/range.rb
... | ... | @@ -34,4 +34,12 @@ class Kalibro::Range < Kalibro::Model |
34 | 34 | @grade = value.to_f |
35 | 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 | 45 | end | ... | ... |
plugins/mezuro/lib/mezuro_plugin/configuration_content.rb
... | ... | @@ -3,8 +3,8 @@ class MezuroPlugin::ConfigurationContent < Article |
3 | 3 | |
4 | 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 | 9 | def self.short_description |
10 | 10 | 'Kalibro configuration' |
... | ... | @@ -21,54 +21,60 @@ class MezuroPlugin::ConfigurationContent < Article |
21 | 21 | end |
22 | 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 | 29 | end |
29 | - @configuration | |
30 | + @kalibro_configuration | |
30 | 31 | end |
31 | 32 | |
32 | 33 | def metric_configurations |
33 | - configuration.metric_configurations | |
34 | + kalibro_configuration.metric_configurations | |
34 | 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 | 44 | end |
39 | 45 | |
40 | 46 | private |
41 | 47 | |
42 | 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 | 51 | if existing.include?(name.downcase) |
46 | 52 | errors.add_to_base("Configuration name already exists in Kalibro") |
47 | 53 | end |
48 | 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 | 59 | else |
54 | 60 | create_kalibro_configuration |
55 | 61 | end |
56 | 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 | 66 | end |
61 | 67 | |
62 | 68 | def create_kalibro_configuration |
63 | 69 | attributes = {:name => name, :description => description} |
64 | - if cloning_configuration? | |
70 | + if cloning_kalibro_configuration? | |
65 | 71 | attributes[:metric_configuration] = configuration_to_clone.metric_configurations_hash |
66 | 72 | end |
67 | 73 | Kalibro::Configuration.create attributes |
68 | 74 | end |
69 | 75 | |
70 | - def editing_configuration? | |
71 | - configuration.present? | |
76 | + def editing_kalibro_configuration? | |
77 | + kalibro_configuration.present? | |
72 | 78 | end |
73 | 79 | |
74 | 80 | def configuration_to_clone |
... | ... | @@ -76,10 +82,10 @@ class MezuroPlugin::ConfigurationContent < Article |
76 | 82 | end |
77 | 83 | |
78 | 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 | 86 | end |
81 | 87 | |
82 | - def cloning_configuration? | |
88 | + def cloning_kalibro_configuration? | |
83 | 89 | configuration_to_clone.present? |
84 | 90 | end |
85 | 91 | ... | ... |
plugins/mezuro/lib/mezuro_plugin/helpers/content_viewer_helper.rb
1 | 1 | class MezuroPlugin::Helpers::ContentViewerHelper |
2 | + | |
3 | + MAX_NUMBER_OF_LABELS = 5 | |
4 | + | |
2 | 5 | def self.format_grade(grade) |
3 | 6 | sprintf("%.2f", grade.to_f) |
4 | 7 | end |
... | ... | @@ -6,15 +9,31 @@ class MezuroPlugin::Helpers::ContentViewerHelper |
6 | 9 | def self.create_periodicity_options |
7 | 10 | [["Not Periodically", 0], ["1 day", 1], ["2 days", 2], ["Weekly", 7], ["Biweeky", 15], ["Monthly", 30]] |
8 | 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 | 30 | :title_color => 'FF0000', |
13 | 31 | :size => '600x180', |
14 | 32 | :bg => {:color => 'efefef', :type => 'stripes'}, |
15 | 33 | :line_colors => 'c4a000', |
16 | 34 | :data => values, |
17 | - :axis_with_labels => 'y', | |
35 | + :labels => labels, | |
36 | + :axis_with_labels => ['y','x'], | |
18 | 37 | :max_value => values.max, |
19 | 38 | :min_value => values.min |
20 | 39 | ) |
... | ... | @@ -25,8 +44,33 @@ class MezuroPlugin::Helpers::ContentViewerHelper |
25 | 44 | selected_option = options.find { |option| option.last == index.to_i } |
26 | 45 | selected_option.first |
27 | 46 | end |
28 | - | |
47 | + | |
29 | 48 | def self.format_name(metric_result) |
30 | 49 | metric_result.metric.name.delete("() ") |
31 | 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 | 76 | end | ... | ... |
plugins/mezuro/lib/mezuro_plugin/project_content.rb
1 | 1 | class MezuroPlugin::ProjectContent < Article |
2 | 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 | 6 | validate_on_create :validate_kalibro_project_name |
7 | 7 | validate_on_create :validate_repository_url |
... | ... | @@ -26,6 +26,7 @@ class MezuroPlugin::ProjectContent < Article |
26 | 26 | rescue Exception => error |
27 | 27 | errors.add_to_base(error.message) |
28 | 28 | end |
29 | + @project | |
29 | 30 | end |
30 | 31 | |
31 | 32 | def project_result |
... | ... | @@ -34,6 +35,7 @@ class MezuroPlugin::ProjectContent < Article |
34 | 35 | rescue Exception => error |
35 | 36 | errors.add_to_base(error.message) |
36 | 37 | end |
38 | + @project_result | |
37 | 39 | end |
38 | 40 | |
39 | 41 | def project_result_with_date(date) |
... | ... | @@ -43,6 +45,7 @@ Kalibro::ProjectResult.first_result_after(name, date) |
43 | 45 | rescue Exception => error |
44 | 46 | errors.add_to_base(error.message) |
45 | 47 | end |
48 | + @project_result | |
46 | 49 | end |
47 | 50 | |
48 | 51 | def module_result(attributes) |
... | ... | @@ -53,6 +56,7 @@ Kalibro::ProjectResult.first_result_after(name, date) |
53 | 56 | rescue Exception => error |
54 | 57 | errors.add_to_base(error.message) |
55 | 58 | end |
59 | + @module_result | |
56 | 60 | end |
57 | 61 | |
58 | 62 | def result_history(module_name) |
... | ... | @@ -73,6 +77,7 @@ Kalibro::ProjectResult.first_result_after(name, date) |
73 | 77 | existing = Kalibro::Project.all_names |
74 | 78 | rescue Exception => error |
75 | 79 | errors.add_to_base(error.message) |
80 | + existing = [] | |
76 | 81 | end |
77 | 82 | |
78 | 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 | 99 | def create_kalibro_project |
95 | 100 | Kalibro::Project.create( |
96 | 101 | :name => name, |
97 | - :license => license, | |
102 | + :license => project_license, | |
98 | 103 | :description => description, |
99 | 104 | :repository => { |
100 | 105 | :type => repository_type, |
... | ... | @@ -105,7 +110,7 @@ Kalibro::ProjectResult.first_result_after(name, date) |
105 | 110 | end |
106 | 111 | |
107 | 112 | def destroy_project_from_service |
108 | - project.destroy | |
113 | + project.destroy unless project.nil? | |
109 | 114 | end |
110 | 115 | |
111 | 116 | end | ... | ... |
... | ... | @@ -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 @@ |
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 @@ |
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. | ... | ... |