stuff_to_do_plugin 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. data/COPYRIGHT.txt +18 -0
  2. data/CREDITS.txt +6 -0
  3. data/GPL.txt +339 -0
  4. data/README.rdoc +61 -0
  5. data/Rakefile +38 -0
  6. data/VERSION +1 -0
  7. data/app/controllers/stuff_to_do_controller.rb +161 -0
  8. data/app/helpers/stuff_to_do_helper.rb +88 -0
  9. data/app/models/stuff_to_do.rb +208 -0
  10. data/app/models/stuff_to_do_filter.rb +32 -0
  11. data/app/models/stuff_to_do_mailer.rb +16 -0
  12. data/app/views/settings/_stuff_to_do_settings.html.erb +27 -0
  13. data/app/views/stuff_to_do/_issue.html.erb +16 -0
  14. data/app/views/stuff_to_do/_item.html.erb +5 -0
  15. data/app/views/stuff_to_do/_left_panes.html.erb +51 -0
  16. data/app/views/stuff_to_do/_panes.html.erb +11 -0
  17. data/app/views/stuff_to_do/_project.html.erb +6 -0
  18. data/app/views/stuff_to_do/_right_panes.html.erb +25 -0
  19. data/app/views/stuff_to_do/_time_grid.html.erb +113 -0
  20. data/app/views/stuff_to_do/_time_grid_form.html.erb +32 -0
  21. data/app/views/stuff_to_do/index.html.erb +44 -0
  22. data/app/views/stuff_to_do_mailer/recommended_below_threshold.erb +3 -0
  23. data/app/views/stuff_to_do_mailer/recommended_below_threshold.text.html.rhtml +1 -0
  24. data/assets/images/b.png +0 -0
  25. data/assets/images/bl.png +0 -0
  26. data/assets/images/br.png +0 -0
  27. data/assets/images/closelabel.gif +0 -0
  28. data/assets/images/loading.gif +0 -0
  29. data/assets/images/tl.png +0 -0
  30. data/assets/images/tr.png +0 -0
  31. data/assets/javascripts/facebox.js +319 -0
  32. data/assets/javascripts/jquery-1.2.6.min.js +32 -0
  33. data/assets/javascripts/jquery-ui.js +2839 -0
  34. data/assets/javascripts/jquery.contextMenu.js +212 -0
  35. data/assets/javascripts/semantic.cache +15 -0
  36. data/assets/javascripts/stuff-to-do.js +270 -0
  37. data/assets/javascripts/ui/build.xml +24 -0
  38. data/assets/javascripts/ui/effects.blind.js +49 -0
  39. data/assets/javascripts/ui/effects.bounce.js +78 -0
  40. data/assets/javascripts/ui/effects.clip.js +54 -0
  41. data/assets/javascripts/ui/effects.core.js +510 -0
  42. data/assets/javascripts/ui/effects.drop.js +50 -0
  43. data/assets/javascripts/ui/effects.explode.js +79 -0
  44. data/assets/javascripts/ui/effects.fold.js +55 -0
  45. data/assets/javascripts/ui/effects.highlight.js +48 -0
  46. data/assets/javascripts/ui/effects.pulsate.js +55 -0
  47. data/assets/javascripts/ui/effects.scale.js +180 -0
  48. data/assets/javascripts/ui/effects.shake.js +57 -0
  49. data/assets/javascripts/ui/effects.slide.js +50 -0
  50. data/assets/javascripts/ui/effects.transfer.js +59 -0
  51. data/assets/javascripts/ui/i18n/ui.datepicker-ar.js +26 -0
  52. data/assets/javascripts/ui/i18n/ui.datepicker-bg.js +25 -0
  53. data/assets/javascripts/ui/i18n/ui.datepicker-ca.js +25 -0
  54. data/assets/javascripts/ui/i18n/ui.datepicker-cs.js +25 -0
  55. data/assets/javascripts/ui/i18n/ui.datepicker-da.js +25 -0
  56. data/assets/javascripts/ui/i18n/ui.datepicker-de.js +25 -0
  57. data/assets/javascripts/ui/i18n/ui.datepicker-eo.js +25 -0
  58. data/assets/javascripts/ui/i18n/ui.datepicker-es.js +25 -0
  59. data/assets/javascripts/ui/i18n/ui.datepicker-fa.js +25 -0
  60. data/assets/javascripts/ui/i18n/ui.datepicker-fi.js +25 -0
  61. data/assets/javascripts/ui/i18n/ui.datepicker-fr.js +25 -0
  62. data/assets/javascripts/ui/i18n/ui.datepicker-he.js +25 -0
  63. data/assets/javascripts/ui/i18n/ui.datepicker-hr.js +25 -0
  64. data/assets/javascripts/ui/i18n/ui.datepicker-hu.js +25 -0
  65. data/assets/javascripts/ui/i18n/ui.datepicker-hy.js +25 -0
  66. data/assets/javascripts/ui/i18n/ui.datepicker-id.js +25 -0
  67. data/assets/javascripts/ui/i18n/ui.datepicker-is.js +25 -0
  68. data/assets/javascripts/ui/i18n/ui.datepicker-it.js +25 -0
  69. data/assets/javascripts/ui/i18n/ui.datepicker-ja.js +26 -0
  70. data/assets/javascripts/ui/i18n/ui.datepicker-ko.js +25 -0
  71. data/assets/javascripts/ui/i18n/ui.datepicker-lt.js +25 -0
  72. data/assets/javascripts/ui/i18n/ui.datepicker-lv.js +25 -0
  73. data/assets/javascripts/ui/i18n/ui.datepicker-nl.js +25 -0
  74. data/assets/javascripts/ui/i18n/ui.datepicker-no.js +25 -0
  75. data/assets/javascripts/ui/i18n/ui.datepicker-pl.js +25 -0
  76. data/assets/javascripts/ui/i18n/ui.datepicker-pt-BR.js +25 -0
  77. data/assets/javascripts/ui/i18n/ui.datepicker-ro.js +25 -0
  78. data/assets/javascripts/ui/i18n/ui.datepicker-ru.js +25 -0
  79. data/assets/javascripts/ui/i18n/ui.datepicker-sk.js +25 -0
  80. data/assets/javascripts/ui/i18n/ui.datepicker-sl.js +26 -0
  81. data/assets/javascripts/ui/i18n/ui.datepicker-sq.js +25 -0
  82. data/assets/javascripts/ui/i18n/ui.datepicker-sv.js +25 -0
  83. data/assets/javascripts/ui/i18n/ui.datepicker-th.js +25 -0
  84. data/assets/javascripts/ui/i18n/ui.datepicker-tr.js +25 -0
  85. data/assets/javascripts/ui/i18n/ui.datepicker-uk.js +25 -0
  86. data/assets/javascripts/ui/i18n/ui.datepicker-zh-CN.js +25 -0
  87. data/assets/javascripts/ui/i18n/ui.datepicker-zh-TW.js +25 -0
  88. data/assets/javascripts/ui/svn.log +11 -0
  89. data/assets/javascripts/ui/ui.accordion.js +400 -0
  90. data/assets/javascripts/ui/ui.core.js +533 -0
  91. data/assets/javascripts/ui/ui.datepicker.js +1754 -0
  92. data/assets/javascripts/ui/ui.dialog.js +630 -0
  93. data/assets/javascripts/ui/ui.draggable.js +696 -0
  94. data/assets/javascripts/ui/ui.droppable.js +314 -0
  95. data/assets/javascripts/ui/ui.progressbar.js +114 -0
  96. data/assets/javascripts/ui/ui.resizable.js +805 -0
  97. data/assets/javascripts/ui/ui.selectable.js +266 -0
  98. data/assets/javascripts/ui/ui.slider.js +552 -0
  99. data/assets/javascripts/ui/ui.sortable.js +1012 -0
  100. data/assets/javascripts/ui/ui.tabs.js +572 -0
  101. data/assets/stylesheets/stuff_to_do.css +216 -0
  102. data/config/locales/bg.yml +18 -0
  103. data/config/locales/ca-fr.yml +18 -0
  104. data/config/locales/cs.yml +16 -0
  105. data/config/locales/da.yml +16 -0
  106. data/config/locales/de.yml +18 -0
  107. data/config/locales/en.yml +24 -0
  108. data/config/locales/es.yml +19 -0
  109. data/config/locales/fr.yml +17 -0
  110. data/config/locales/hu.yml +16 -0
  111. data/config/locales/it.yml +16 -0
  112. data/config/locales/ja.yml +18 -0
  113. data/config/locales/ko.yml +18 -0
  114. data/config/locales/lt.yml +18 -0
  115. data/config/locales/nl.yml +20 -0
  116. data/config/locales/pt-BR.yml +18 -0
  117. data/config/locales/ru.yml +19 -0
  118. data/config/locales/sv.yml +19 -0
  119. data/config/locales/tr.yml +18 -0
  120. data/config/routes.rb +3 -0
  121. data/init.rb +54 -0
  122. data/lang/bg.yml +17 -0
  123. data/lang/ca-fr.yml +17 -0
  124. data/lang/cs.yml +15 -0
  125. data/lang/da.yml +15 -0
  126. data/lang/de.yml +17 -0
  127. data/lang/en.yml +21 -0
  128. data/lang/es.yml +18 -0
  129. data/lang/fr.yml +16 -0
  130. data/lang/hu.yml +15 -0
  131. data/lang/it.yml +15 -0
  132. data/lang/ja.yml +17 -0
  133. data/lang/ko.yml +17 -0
  134. data/lang/lt.yml +17 -0
  135. data/lang/pt-br.yml +17 -0
  136. data/lang/ru.yml +15 -0
  137. data/lang/sv.yml +18 -0
  138. data/lang/tr.yml +17 -0
  139. data/lib/redmine_stuff_to_do/stuff_to_do_compatibility.rb +15 -0
  140. data/lib/stuff_to_do_array_patch.rb +8 -0
  141. data/lib/stuff_to_do_issue_patch.rb +57 -0
  142. data/lib/stuff_to_do_project_patch.rb +31 -0
  143. data/lib/stuff_to_do_user_patch.rb +10 -0
  144. data/rails/init.rb +1 -0
  145. data/spec/controllers/stuff_to_do_controller_add_to_time_grid_spec.rb +58 -0
  146. data/spec/controllers/stuff_to_do_controller_index_spec.rb +155 -0
  147. data/spec/controllers/stuff_to_do_controller_remove_from_time_grid_spec.rb +56 -0
  148. data/spec/controllers/stuff_to_do_controller_reorder_spec.rb +179 -0
  149. data/spec/controllers/stuff_to_do_controller_save_time_entries_spec.rb +56 -0
  150. data/spec/controllers/stuff_to_do_private_methods_spec.rb +82 -0
  151. data/spec/lib/stuff_to_do_issue_patch_spec.rb +60 -0
  152. data/spec/lib/stuff_to_do_project_patch_spec.rb +50 -0
  153. data/spec/lib/stuff_to_do_user_patch_spec.rb +8 -0
  154. data/spec/models/stuff_to_do_filter_spec.rb +3 -0
  155. data/spec/models/stuff_to_do_mailer_spec.rb +42 -0
  156. data/spec/models/stuff_to_do_spec.rb +426 -0
  157. data/spec/sanity_spec.rb +7 -0
  158. data/spec/spec_helper.rb +130 -0
  159. metadata +211 -0
@@ -0,0 +1,32 @@
1
+ class StuffToDoFilter
2
+ attr_accessor :users
3
+ attr_accessor :priorities
4
+ attr_accessor :statuses
5
+
6
+ def initialize
7
+ self.users = User.active
8
+ self.priorities = get_priorites
9
+ self.statuses = IssueStatus.find(:all)
10
+ end
11
+
12
+ def each
13
+ if StuffToDo.using_issues_as_items?
14
+ {
15
+ :users => self.users.sort,
16
+ :priorities => self.priorities.sort,
17
+ :statuses => self.statuses.sort
18
+ }.each do |group, items|
19
+ yield group, items
20
+ end
21
+ end
22
+
23
+ # Finally projects
24
+ yield :projects if StuffToDo.using_projects_as_items?
25
+ end
26
+
27
+ private
28
+ # Wrapper around Redmine's API since Enumerations changed in r2472
29
+ def get_priorites
30
+ RedmineStuffToDo::StuffToDoCompatibility::IssuePriority.all
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ class StuffToDoMailer < Mailer
2
+ def recommended_below_threshold(user, number_of_next_items)
3
+ recipients Setting.plugin_stuff_to_do_plugin['email_to'].split(',')
4
+ subject "What's Recommended is below the threshold"
5
+
6
+ body(
7
+ :threshold => Setting.plugin_stuff_to_do_plugin['threshold'],
8
+ :count => number_of_next_items,
9
+ :user => user
10
+ )
11
+
12
+ part :content_type => "text/plain", :body => render_message("recommended_below_threshold.erb", body)
13
+ part :content_type => "text/html", :body => render_message("recommended_below_threshold.text.html.rhtml", body)
14
+
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ <fieldset>
2
+ <legend><%= l(:stuff_to_do_label_project_manager_emails) %></legend>
3
+ <p>
4
+ <%= l(:stuff_to_do_text_settings) %>
5
+ </p>
6
+
7
+ <p>
8
+ <label><%= l(:stuff_to_do_label_threshold) %></label><%= text_field_tag 'settings[threshold]', @settings['threshold'] %>
9
+ </p>
10
+
11
+ <p>
12
+ <label><%= l(:stuff_to_do_label_email_to) %></label><%= text_field_tag 'settings[email_to]', @settings['email_to'] %>
13
+ </p>
14
+
15
+ </fieldset>
16
+
17
+ <p>
18
+ <label><%= l(:stuff_to_do_use_as_stuff_to_do) %></label>
19
+ <%= select_tag('settings[use_as_stuff_to_do]',
20
+ options_for_select(StuffToDo::USE.to_a, @settings['use_as_stuff_to_do'])) %>
21
+ </p>
22
+
23
+ <p>
24
+ <label><%= l(:stuff_to_do_use_time_grid) %></label>
25
+ <%= hidden_field_tag 'settings[use_time_grid]', 0 %>
26
+ <%= check_box_tag 'settings[use_time_grid]', 1, @settings['use_time_grid'].to_i == 1 %>
27
+ </p>
@@ -0,0 +1,16 @@
1
+ <li class="stuff-to-do-item <%= issue.css_classes if issue.respond_to?(:css_classes) %> <%= issue_counter.odd? ? "odd" : "even" %>" id="stuff_<%= issue.id %>">
2
+ <div class="issue-details">
3
+ <%= link_to(image_tag('ticket.png'), :controller => 'issues', :action => 'show', :id => issue) %>
4
+ #<%= h(issue.id) %>
5
+ - <%= h(issue.project.name) %> -
6
+ <%= h(issue.subject) %>
7
+ </div>
8
+
9
+ <% if issue.estimated_hours && issue.estimated_hours > 0 %>
10
+ <div class="estimate"><%= h(l_hours(issue.estimated_hours)) %> &nbsp;</div>
11
+ <% end %>
12
+
13
+ <div class="progress"><%= progress_bar(issue.done_ratio, :width => '100%') %></div>
14
+
15
+ <div style="clear:left;"></div>
16
+ </li>
@@ -0,0 +1,5 @@
1
+ <% if item.class == Issue %>
2
+ <%= render :partial => 'issue', :locals => {:issue_counter => item_counter}, :object => item %>
3
+ <% else %>
4
+ <%= render :partial => 'project', :locals => {:project_counter => item_counter}, :object => item %>
5
+ <% end %>
@@ -0,0 +1,51 @@
1
+
2
+ <div class="pane" id="doing-now-pane">
3
+ <h3>
4
+ <%= l(:stuff_to_do_what_im_doing_now) %>
5
+ <span class="subtitle"><%= l(:stuff_to_do_text_order_im_doing_it) %></span>
6
+ </h3>
7
+
8
+ <ol id="doing-now">
9
+ <% unless stuff_for(@doing_now).empty? %>
10
+ <%= render :partial => 'item', :collection => stuff_for(@doing_now) %>
11
+ <% else %>
12
+ <li class="empty-list">
13
+ <%= l(:stuff_to_do_drag_issue_create_list) %>
14
+ </li>
15
+ <% end %>
16
+ </ol>
17
+
18
+ <div class="pane-summary">
19
+ <div id="doing-now-total-progress">
20
+ <%= progress_bar_sum(issues_for(@doing_now), :done_ratio, { :width => '200px', :legend => l(:stuff_to_do_text_total_progress) }) %>
21
+ </div>
22
+
23
+ <div id="doing-now-estimates">
24
+ <p><%= l(:stuff_to_do_text_total_estimates) %> <%= l_hours(total_estimates(issues_for(@doing_now))) %></p>
25
+ </div>
26
+ </div>
27
+
28
+ </div>
29
+
30
+ <div class="pane" id="recommended-pane">
31
+ <h3>
32
+ <%= l(:stuff_to_do_what_is_recommended) %>
33
+ <span class="subtitle"><%= l(:stuff_to_do_text_by_project_manager) %></span>
34
+ </h3>
35
+
36
+ <ol id="recommended">
37
+ <%= render :partial => 'item', :collection => stuff_for(@recommended) %>
38
+ </ol>
39
+
40
+ <div class="pane-summary">
41
+ <div id="recommended-total-progress">
42
+ <%= progress_bar_sum(issues_for(@recommended), :done_ratio, { :width => '200px', :legend => l(:stuff_to_do_text_total_progress) }) %>
43
+ </div>
44
+
45
+ <div id="recommended-estimates">
46
+ <p><%= l(:stuff_to_do_text_total_estimates) %> <%= l_hours(total_estimates(issues_for(@recommended))) %></p>
47
+ </div>
48
+ </div>
49
+ </div>
50
+
51
+
@@ -0,0 +1,11 @@
1
+ <div id="panes">
2
+
3
+ <div class="splitcontentleft">
4
+ <%= render :partial => 'left_panes' %>
5
+ </div>
6
+
7
+ <div class="splitcontentright">
8
+ <%= render :partial => 'right_panes' %>
9
+ </div>
10
+
11
+ </div>
@@ -0,0 +1,6 @@
1
+ <li class="stuff-to-do-item project <%= project_counter.odd? ? "odd" : "even" %>" id="stuff_project<%= project.id %>">
2
+ <div class="project-details">
3
+ <%= link_to(image_tag('package.png'), :controller => 'projects', :action => 'show', :id => project) %> <%= h project.name %>
4
+ </div>
5
+ <div style="clear:left;"></div>
6
+ </li>
@@ -0,0 +1,25 @@
1
+ <div class="pane" id="available-pane">
2
+ <h3><%= l(:stuff_to_do_what_is_available) %></h3>
3
+
4
+ <ol id="available">
5
+ <% unless @available.empty? %>
6
+ <%= render :partial => 'item', :collection => @available %>
7
+ <% else %>
8
+ <li class="empty-list">
9
+ <%= l(:stuff_to_do_drag_issue_remove_list) %>
10
+ </li>
11
+ <% end %>
12
+ </ol>
13
+
14
+ <div class="pane-summary">
15
+ <div id="available-total-progress">
16
+ <%= progress_bar_sum(@available, :done_ratio, { :width => '200px', :legend => l(:stuff_to_do_text_total_progress) }) %>
17
+ </div>
18
+
19
+ <div id="available-estimates">
20
+ <p><%= l(:stuff_to_do_text_total_estimates) %> <%= l_hours(total_estimates(@available)) %></p>
21
+ </div>
22
+ </div>
23
+
24
+ </div>
25
+ <%= javascript_tag("var filter = #{params[:filter].nil? ? 'null' : "'" + params[:filter].to_s + "'" }") %>
@@ -0,0 +1,113 @@
1
+ <div id="time-grid">
2
+ <h2><%= l(:stuff_to_do_time_grid) %></h2>
3
+
4
+ <div id="time-grid-error" class="flash error time-grid-flash" style="display:none"><%= h flash[:time_grid_error] %></div>
5
+ <div id="time-grid-warning" class="flash warning time-grid-flash" style="display:none"><%= h flash[:time_grid_warning] %></div>
6
+ <div id="time-grid-notice" class="flash notice time-grid-flash" style="display:none"><%= h flash[:time_grid_notice] %></div>
7
+
8
+ <%= render :partial => 'time_grid_form' %>
9
+
10
+ <%= submit_tag l(:button_save), :class => 'save-time-grid', :style => 'display:none;' %>
11
+
12
+ <table class="cal" id="time-grid-table">
13
+ <thead>
14
+ <tr>
15
+ <th class="time-grid-project"><%= l(:field_project) %></th>
16
+ <th class="time-grid-issue"><%= l(:field_issue) %></th>
17
+ <% day = @calendar.startdt
18
+ while day <= @calendar.enddt %>
19
+ <th class="time-grid-date">
20
+ <%= day_name(day.cwday) %>
21
+ <br />
22
+ <%= day.day %>
23
+ </th>
24
+ <% day = day + 1 %>
25
+ <% end %>
26
+ <th class="time-grid-running-total">Running Total</th>
27
+ </tr>
28
+ </thead>
29
+ <tbody>
30
+ <% @issues.each do |issue| %>
31
+ <% content_tag_for(:tr, issue) do %>
32
+ <td class="time-grid-project">
33
+ <%= link_to(h(issue.project), :controller => 'projects', :action => 'show', :id => issue.project) %>
34
+ </td>
35
+ <td class="time-grid-issue">
36
+ <%= link_to("##{issue.id} " + h(issue.subject), :controller => 'issues', :action => 'show', :id => issue) %>
37
+ </td>
38
+ <% day = @calendar.startdt
39
+ while day <= @calendar.enddt %>
40
+ <td class="time-grid-date <%= h day -%> hascontextmenu">
41
+ <%= h total_hours_for_user_on_day(issue, User.current, day) %>
42
+ </td>
43
+ <% day = day + 1 %>
44
+ <% end %>
45
+ <td class="time-grid-running-total issue-<%= h issue.id -%>">
46
+ <%= h total_hours_for_issue_for_user(issue, User.current) %>
47
+ </td>
48
+ <% end %>
49
+ <% end %>
50
+ <tr class="daily-totals">
51
+ <td colspan="2" class="label">
52
+ Daily Totals
53
+ </td>
54
+ <% day = @calendar.startdt
55
+ while day <= @calendar.enddt %>
56
+ <td class="time-grid-date <%= h day -%> totals hascontextmenu">
57
+ <%= h total_hours_for_date(@issues, User.current, day) %>
58
+ </td>
59
+ <% day = day + 1 %>
60
+ <% end %>
61
+ <td class="time-grid-grand-total">
62
+ <%= h total_hours_for_user(@issues, User.current) %>
63
+ </td>
64
+ </tr>
65
+
66
+ </tbody>
67
+ </table>
68
+
69
+ <%= submit_tag l(:button_save), :class => 'save-time-grid', :style => 'display:none;' %>
70
+
71
+ </div>
72
+
73
+ <ul id="time-grid-menu" class="context-menu" style="display:none;">
74
+ <li>
75
+ <%= content_tag(:a, l(:button_log_time), :class => 'icon-time-add', :id => 'log-time', :href => '#logtime') %>
76
+ </li>
77
+ </ul>
78
+
79
+ <div id="logtime" style="display:none;">
80
+ <h2><%= l(:label_spent_time) %></h2>
81
+
82
+ <% labelled_tabular_form_for 'time_entry[]'.to_sym, @time_entry, :url => {}, :html => {:id => 'new-time-entry'} do |f| %>
83
+ <%= error_messages_for 'time_entry' %>
84
+ <%= back_url_hidden_field_tag %>
85
+
86
+ <div class="box">
87
+ <p><%= f.text_field :issue_id, :required => true, :size => 6 %> <em><%= h("#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}") if @time_entry.issue %></em></p>
88
+ <p>
89
+ <%= f.text_field :spent_on, :size => 10, :required => true, :id => 'time_entry__core' %>
90
+ <% include_calendar_headers_tags %>
91
+ <%= image_tag("calendar.png", {:id => "time_entry__core_trigger",:class => "calendar-trigger"}) -%>
92
+ </p>
93
+ <p><%= f.text_field :hours, :size => 6, :required => true %></p>
94
+ <p><%= f.text_field :comments, :size => 100, :required => true %></p>
95
+ <p><%= f.select :activity_id, activity_collection_for_select_options, :required => true %></p>
96
+ <% @time_entry.custom_field_values.each do |value| %>
97
+ <p><%= custom_field_tag_with_label :time_entry, value %></p>
98
+ <% end %>
99
+ <%= call_hook(:view_timelog_edit_form_bottom, { :time_entry => @time_entry, :form => f }) %>
100
+ </div>
101
+
102
+ <%= submit_tag l(:button_save) %>
103
+
104
+ <% end %>
105
+
106
+ </div>
107
+
108
+ <script type="text/javascript">
109
+ jQuery(function($) {
110
+ initTimeGrid();
111
+ attachSortables();
112
+ });
113
+ </script>
@@ -0,0 +1,32 @@
1
+ <% form_tag({:action => 'time_grid'}, :id => 'query_form') do %>
2
+ <fieldset id="date-range"><legend><%= l(:label_date_range) %></legend>
3
+ <%= select_month(@date.month, :prefix => "month", :discard_type => true) %>
4
+ <%= select_day(@date.day, :prefix => "day", :discard_type => true) %>
5
+ <%= select_year(@date.year, :prefix => "year", :discard_type => true) %>
6
+ </fieldset>
7
+
8
+ <p style="float:right; margin:0px;">
9
+ <%= link_to_remote("&#171; #{l(:label_previous)} #{l(:label_week).downcase}",
10
+ {:update => "time-grid", :url => { :action => 'time_grid', :date => @date - 7 }},
11
+ {:href => url_for(:action => 'time_grid', :date => @date - 7)})
12
+ %> |
13
+ <%= link_to_remote("#{l(:label_next)} #{l(:label_week).downcase} &#187;",
14
+ {:update => "time-grid", :url => { :action => 'time_grid', :date => @date + 7 }},
15
+ {:href => url_for(:action => 'time_grid', :date => @date + 7)})
16
+ %>
17
+ </p>
18
+
19
+ <p class="buttons">
20
+ <%= link_to_remote l(:button_apply),
21
+ { :url => { :action => 'time_grid' },
22
+ :update => "time-grid",
23
+ :with => "Form.serialize('query_form')"
24
+ }, :class => 'icon icon-checked' %>
25
+
26
+ <%= link_to_remote l(:button_clear),
27
+ { :url => { :action => 'time_grid' },
28
+ :update => "time-grid",
29
+ }, :class => 'icon icon-reload' %>
30
+ </p>
31
+ <% end %>
32
+
@@ -0,0 +1,44 @@
1
+ <% html_title(l(:stuff_to_do_title)) %>
2
+ <h1><%= l(:stuff_to_do_title) %></h1>
3
+
4
+ <div id="stuff-to-do-error" class="flash error" style="display:none">
5
+
6
+ </div>
7
+
8
+ <div class="menu">
9
+ <% if User.current.admin? %>
10
+ <div class="user">
11
+ <% form_tag({ :controller => 'stuff_to_do', :action => 'index'}, :method => :get, :id => 'user_switch' ) do %>
12
+ <label for="user_id"><%= l(:stuff_to_do_label_view_user_list) %></label>
13
+ <%= collection_select(:user, :id, @users.sort, :id, :name, { }, { :name => 'user_id'}) %>
14
+ <% end %>
15
+ </div>
16
+
17
+ <div class="filter">
18
+ <% form_tag({}, :id => 'filter_form') do %>
19
+ <label for="filter"><%= l(:stuff_to_do_label_filter) %></label>
20
+ <%= select_tag("filter", filter_options(@filters, params[:filter])) %>
21
+ <% end %>
22
+ </div>
23
+ <% end %>
24
+ </div>
25
+ <div style="clear:right;"></div>
26
+
27
+ <%= render :partial => 'panes' %>
28
+
29
+ <div style="clear:both;"></div>
30
+
31
+ <%= render :partial => 'time_grid' if !Setting.plugin_stuff_to_do_plugin['use_time_grid'].blank? && Setting.plugin_stuff_to_do_plugin['use_time_grid'] != "0" %>
32
+
33
+ <% content_for :header_tags do %>
34
+ <%= stylesheet_link_tag "stuff_to_do.css", :plugin => "stuff_to_do_plugin", :media => 'all' %>
35
+ <%= javascript_include_tag 'jquery-1.2.6.min.js', :plugin => 'stuff_to_do_plugin' %>
36
+ <%= javascript_include_tag 'ui/ui.core.js', :plugin => 'stuff_to_do_plugin' %>
37
+ <%= javascript_include_tag 'ui/ui.sortable.js', :plugin => 'stuff_to_do_plugin' %>
38
+ <%= javascript_include_tag 'jquery.contextMenu.js', :plugin => 'stuff_to_do_plugin' %>
39
+ <%= javascript_include_tag 'facebox.js', :plugin => 'stuff_to_do_plugin' %>
40
+ <%= javascript_tag("jQuery.noConflict();") %>
41
+ <%= javascript_include_tag 'stuff-to-do.js', :plugin => 'stuff_to_do_plugin' %>
42
+ <%= javascript_tag("var user_id = #{@user.id};") %>
43
+ <%= javascript_tag "window._token = '#{form_authenticity_token}'" %>
44
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= @user.name %> has only <%= @count %> recommended items left in their Stuff To Do which is below the threshold of <%= @threshold %>.
2
+
3
+ <%= url_for :controller => 'stuff_to_do', :action => 'index', :user_id => @user.id, :only_path => false %>
@@ -0,0 +1 @@
1
+ <%= link_to @user.name, :controller => 'stuff_to_do', :action => 'index', :user_id => @user.id, :only_path => false %> has only <%= @count %> recommended items left in their <%= link_to "Stuff to Do", :controller => 'stuff_to_do', :action => 'index', :user_id => @user.id, :only_path => false %> which is below the threshold of <%= @threshold %>.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,319 @@
1
+ /*
2
+ * Facebox (for jQuery)
3
+ * version: 1.2 (05/05/2008)
4
+ * @requires jQuery v1.2 or later
5
+ *
6
+ * Examples at http://famspam.com/facebox/
7
+ *
8
+ * Licensed under the MIT:
9
+ * http://www.opensource.org/licenses/mit-license.php
10
+ *
11
+ * Copyright 2007, 2008 Chris Wanstrath [ chris@ozmm.org ]
12
+ *
13
+ * Usage:
14
+ *
15
+ * jQuery(document).ready(function() {
16
+ * jQuery('a[rel*=facebox]').facebox()
17
+ * })
18
+ *
19
+ * <a href="#terms" rel="facebox">Terms</a>
20
+ * Loads the #terms div in the box
21
+ *
22
+ * <a href="terms.html" rel="facebox">Terms</a>
23
+ * Loads the terms.html page in the box
24
+ *
25
+ * <a href="terms.png" rel="facebox">Terms</a>
26
+ * Loads the terms.png image in the box
27
+ *
28
+ *
29
+ * You can also use it programmatically:
30
+ *
31
+ * jQuery.facebox('some html')
32
+ *
33
+ * The above will open a facebox with "some html" as the content.
34
+ *
35
+ * jQuery.facebox(function($) {
36
+ * $.get('blah.html', function(data) { $.facebox(data) })
37
+ * })
38
+ *
39
+ * The above will show a loading screen before the passed function is called,
40
+ * allowing for a better ajaxy experience.
41
+ *
42
+ * The facebox function can also display an ajax page or image:
43
+ *
44
+ * jQuery.facebox({ ajax: 'remote.html' })
45
+ * jQuery.facebox({ image: 'dude.jpg' })
46
+ *
47
+ * Want to close the facebox? Trigger the 'close.facebox' document event:
48
+ *
49
+ * jQuery(document).trigger('close.facebox')
50
+ *
51
+ * Facebox also has a bunch of other hooks:
52
+ *
53
+ * loading.facebox
54
+ * beforeReveal.facebox
55
+ * reveal.facebox (aliased as 'afterReveal.facebox')
56
+ * init.facebox
57
+ *
58
+ * Simply bind a function to any of these hooks:
59
+ *
60
+ * $(document).bind('reveal.facebox', function() { ...stuff to do after the facebox and contents are revealed... })
61
+ *
62
+ */
63
+ (function($) {
64
+ $.facebox = function(data, klass) {
65
+ $.facebox.loading()
66
+
67
+ if (data.ajax) fillFaceboxFromAjax(data.ajax)
68
+ else if (data.image) fillFaceboxFromImage(data.image)
69
+ else if (data.div) fillFaceboxFromHref(data.div)
70
+ else if ($.isFunction(data)) data.call($)
71
+ else $.facebox.reveal(data, klass)
72
+ }
73
+
74
+ /*
75
+ * Public, $.facebox methods
76
+ */
77
+
78
+ $.extend($.facebox, {
79
+ settings: {
80
+ opacity : 0,
81
+ overlay : true,
82
+ loadingImage : '/facebox/loading.gif',
83
+ closeImage : '/facebox/closelabel.gif',
84
+ imageTypes : [ 'png', 'jpg', 'jpeg', 'gif' ],
85
+ faceboxHtml : '\
86
+ <div id="facebox" style="display:none;"> \
87
+ <div class="popup"> \
88
+ <table> \
89
+ <tbody> \
90
+ <tr> \
91
+ <td class="tl"/><td class="b"/><td class="tr"/> \
92
+ </tr> \
93
+ <tr> \
94
+ <td class="b"/> \
95
+ <td class="body"> \
96
+ <div class="content"> \
97
+ </div> \
98
+ <div class="footer"> \
99
+ <a href="#" class="close"> \
100
+ <img src="/facebox/closelabel.gif" title="close" class="close_image" /> \
101
+ </a> \
102
+ </div> \
103
+ </td> \
104
+ <td class="b"/> \
105
+ </tr> \
106
+ <tr> \
107
+ <td class="bl"/><td class="b"/><td class="br"/> \
108
+ </tr> \
109
+ </tbody> \
110
+ </table> \
111
+ </div> \
112
+ </div>'
113
+ },
114
+
115
+ loading: function() {
116
+ init()
117
+ if ($('#facebox .loading').length == 1) return true
118
+ showOverlay()
119
+
120
+ $('#facebox .content').empty()
121
+ $('#facebox .body').children().hide().end().
122
+ append('<div class="loading"><img src="'+$.facebox.settings.loadingImage+'"/></div>')
123
+
124
+ $('#facebox').css({
125
+ top: getPageScroll()[1] + (getPageHeight() / 10),
126
+ left: 385.5
127
+ }).show()
128
+
129
+ $(document).bind('keydown.facebox', function(e) {
130
+ if (e.keyCode == 27) $.facebox.close()
131
+ return true
132
+ })
133
+ $(document).trigger('loading.facebox')
134
+ },
135
+
136
+ reveal: function(data, klass) {
137
+ $(document).trigger('beforeReveal.facebox')
138
+ if (klass) $('#facebox .content').addClass(klass)
139
+ $('#facebox .content').append(data)
140
+ $('#facebox .loading').remove()
141
+ $('#facebox .body').children().fadeIn('normal')
142
+ $('#facebox').css('left', $(window).width() / 2 - ($('#facebox table').width() / 2))
143
+ $(document).trigger('reveal.facebox').trigger('afterReveal.facebox')
144
+ },
145
+
146
+ close: function() {
147
+ $(document).trigger('close.facebox')
148
+ return false
149
+ }
150
+ })
151
+
152
+ /*
153
+ * Public, $.fn methods
154
+ */
155
+
156
+ $.fn.facebox = function(settings) {
157
+ init(settings)
158
+
159
+ function clickHandler() {
160
+ $.facebox.loading(true)
161
+
162
+ // support for rel="facebox.inline_popup" syntax, to add a class
163
+ // also supports deprecated "facebox[.inline_popup]" syntax
164
+ var klass = this.rel.match(/facebox\[?\.(\w+)\]?/)
165
+ if (klass) klass = klass[1]
166
+
167
+ fillFaceboxFromHref(this.href, klass)
168
+ return false
169
+ }
170
+
171
+ return this.click(clickHandler)
172
+ }
173
+
174
+ /*
175
+ * Private methods
176
+ */
177
+
178
+ // called one time to setup facebox on this page
179
+ function init(settings) {
180
+ if ($.facebox.settings.inited) return true
181
+ else $.facebox.settings.inited = true
182
+
183
+ $(document).trigger('init.facebox')
184
+ makeCompatible()
185
+
186
+ var imageTypes = $.facebox.settings.imageTypes.join('|')
187
+ $.facebox.settings.imageTypesRegexp = new RegExp('\.' + imageTypes + '$', 'i')
188
+
189
+ if (settings) $.extend($.facebox.settings, settings)
190
+ $('body').append($.facebox.settings.faceboxHtml)
191
+
192
+ var preload = [ new Image(), new Image() ]
193
+ preload[0].src = $.facebox.settings.closeImage
194
+ preload[1].src = $.facebox.settings.loadingImage
195
+
196
+ $('#facebox').find('.b:first, .bl, .br, .tl, .tr').each(function() {
197
+ preload.push(new Image())
198
+ preload.slice(-1).src = $(this).css('background-image').replace(/url\((.+)\)/, '$1')
199
+ })
200
+
201
+ $('#facebox .close').click($.facebox.close)
202
+ $('#facebox .close_image').attr('src', $.facebox.settings.closeImage)
203
+ }
204
+
205
+ // getPageScroll() by quirksmode.com
206
+ function getPageScroll() {
207
+ var xScroll, yScroll;
208
+ if (self.pageYOffset) {
209
+ yScroll = self.pageYOffset;
210
+ xScroll = self.pageXOffset;
211
+ } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
212
+ yScroll = document.documentElement.scrollTop;
213
+ xScroll = document.documentElement.scrollLeft;
214
+ } else if (document.body) {// all other Explorers
215
+ yScroll = document.body.scrollTop;
216
+ xScroll = document.body.scrollLeft;
217
+ }
218
+ return new Array(xScroll,yScroll)
219
+ }
220
+
221
+ // Adapted from getPageSize() by quirksmode.com
222
+ function getPageHeight() {
223
+ var windowHeight
224
+ if (self.innerHeight) { // all except Explorer
225
+ windowHeight = self.innerHeight;
226
+ } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
227
+ windowHeight = document.documentElement.clientHeight;
228
+ } else if (document.body) { // other Explorers
229
+ windowHeight = document.body.clientHeight;
230
+ }
231
+ return windowHeight
232
+ }
233
+
234
+ // Backwards compatibility
235
+ function makeCompatible() {
236
+ var $s = $.facebox.settings
237
+
238
+ $s.loadingImage = $s.loading_image || $s.loadingImage
239
+ $s.closeImage = $s.close_image || $s.closeImage
240
+ $s.imageTypes = $s.image_types || $s.imageTypes
241
+ $s.faceboxHtml = $s.facebox_html || $s.faceboxHtml
242
+ }
243
+
244
+ // Figures out what you want to display and displays it
245
+ // formats are:
246
+ // div: #id
247
+ // image: blah.extension
248
+ // ajax: anything else
249
+ function fillFaceboxFromHref(href, klass) {
250
+ // div
251
+ if (href.match(/#/)) {
252
+ var url = window.location.href.split('#')[0]
253
+ var target = href.replace(url,'')
254
+ $.facebox.reveal($(target).clone().show(), klass)
255
+
256
+ // image
257
+ } else if (href.match($.facebox.settings.imageTypesRegexp)) {
258
+ fillFaceboxFromImage(href, klass)
259
+ // ajax
260
+ } else {
261
+ fillFaceboxFromAjax(href, klass)
262
+ }
263
+ }
264
+
265
+ function fillFaceboxFromImage(href, klass) {
266
+ var image = new Image()
267
+ image.onload = function() {
268
+ $.facebox.reveal('<div class="image"><img src="' + image.src + '" /></div>', klass)
269
+ }
270
+ image.src = href
271
+ }
272
+
273
+ function fillFaceboxFromAjax(href, klass) {
274
+ $.get(href, function(data) { $.facebox.reveal(data, klass) })
275
+ }
276
+
277
+ function skipOverlay() {
278
+ return $.facebox.settings.overlay == false || $.facebox.settings.opacity === null
279
+ }
280
+
281
+ function showOverlay() {
282
+ if (skipOverlay()) return
283
+
284
+ if ($('facebox_overlay').length == 0)
285
+ $("body").append('<div id="facebox_overlay" class="facebox_hide"></div>')
286
+
287
+ $('#facebox_overlay').hide().addClass("facebox_overlayBG")
288
+ .css('opacity', $.facebox.settings.opacity)
289
+ .click(function() { $(document).trigger('close.facebox') })
290
+ .fadeIn(200)
291
+ return false
292
+ }
293
+
294
+ function hideOverlay() {
295
+ if (skipOverlay()) return
296
+
297
+ $('#facebox_overlay').fadeOut(200, function(){
298
+ $("#facebox_overlay").removeClass("facebox_overlayBG")
299
+ $("#facebox_overlay").addClass("facebox_hide")
300
+ $("#facebox_overlay").remove()
301
+ })
302
+
303
+ return false
304
+ }
305
+
306
+ /*
307
+ * Bindings
308
+ */
309
+
310
+ $(document).bind('close.facebox', function() {
311
+ $(document).unbind('keydown.facebox')
312
+ $('#facebox').fadeOut(function() {
313
+ $('#facebox .content').removeClass().addClass('content')
314
+ hideOverlay()
315
+ $('#facebox .loading').remove()
316
+ })
317
+ })
318
+
319
+ })(jQuery);