tawork 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (239) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +16 -0
  4. data/Gemfile +91 -0
  5. data/Gemfile.lock +295 -0
  6. data/MIT-LICENSE +20 -0
  7. data/Procfile +2 -0
  8. data/README.md +1 -0
  9. data/Rakefile +6 -0
  10. data/app/assets/images/.keep +0 -0
  11. data/app/assets/javascripts/application.js +31 -0
  12. data/app/assets/javascripts/backbone/models/.gitkeep +0 -0
  13. data/app/assets/javascripts/backbone/routers/.gitkeep +0 -0
  14. data/app/assets/javascripts/backbone/tawork.js.coffee +11 -0
  15. data/app/assets/javascripts/backbone/templates/.gitkeep +0 -0
  16. data/app/assets/javascripts/backbone/views/.gitkeep +0 -0
  17. data/app/assets/javascripts/backbone/views/page.js.coffee +75 -0
  18. data/app/assets/javascripts/backbone/views/ticket.js.coffee +110 -0
  19. data/app/assets/javascripts/backbone/views/tree_node_page.js.coffee +16 -0
  20. data/app/assets/javascripts/backbone/views/tree_node_ticket.js.coffee +14 -0
  21. data/app/assets/javascripts/wiki/pages.js.coffee +3 -0
  22. data/app/assets/stylesheets/application.css +22 -0
  23. data/app/assets/stylesheets/bootstrap_and_overrides.sass +208 -0
  24. data/app/assets/stylesheets/diff.css +14 -0
  25. data/app/assets/stylesheets/wiki/pages.css.scss +3 -0
  26. data/app/controllers/application_controller.rb +15 -0
  27. data/app/controllers/assignments_controller.rb +30 -0
  28. data/app/controllers/attachments_controller.rb +10 -0
  29. data/app/controllers/comments_controller.rb +24 -0
  30. data/app/controllers/concerns/.keep +0 -0
  31. data/app/controllers/home_controller.rb +7 -0
  32. data/app/controllers/projects_controller.rb +31 -0
  33. data/app/controllers/search_controller.rb +49 -0
  34. data/app/controllers/sink_controller.rb +10 -0
  35. data/app/controllers/tickets_controller.rb +147 -0
  36. data/app/controllers/users/omniauth_callbacks_controller.rb +23 -0
  37. data/app/controllers/wiki/pages_controller.rb +115 -0
  38. data/app/controllers/wiki/spaces_controller.rb +37 -0
  39. data/app/helpers/application_helper.rb +20 -0
  40. data/app/helpers/wiki/pages_helper.rb +2 -0
  41. data/app/inputs/fake_input.rb +6 -0
  42. data/app/mailers/.keep +0 -0
  43. data/app/models/.keep +0 -0
  44. data/app/models/assignment.rb +5 -0
  45. data/app/models/attachment.rb +54 -0
  46. data/app/models/bug.rb +2 -0
  47. data/app/models/comment.rb +6 -0
  48. data/app/models/concerns/.keep +0 -0
  49. data/app/models/page.rb +68 -0
  50. data/app/models/project.rb +2 -0
  51. data/app/models/space.rb +2 -0
  52. data/app/models/story.rb +29 -0
  53. data/app/models/story_detail.rb +3 -0
  54. data/app/models/task.rb +2 -0
  55. data/app/models/ticket.rb +127 -0
  56. data/app/models/user.rb +36 -0
  57. data/app/models/user_verifier.rb +2 -0
  58. data/app/views/comments/_comment.html.haml +6 -0
  59. data/app/views/comments/_form.html.haml +4 -0
  60. data/app/views/comments/create.js.coffee +7 -0
  61. data/app/views/devise/confirmations/new.html.erb +12 -0
  62. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  63. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  64. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  65. data/app/views/devise/passwords/edit.html.erb +16 -0
  66. data/app/views/devise/passwords/new.html.erb +12 -0
  67. data/app/views/devise/registrations/edit.html.erb +29 -0
  68. data/app/views/devise/registrations/new.html.erb +18 -0
  69. data/app/views/devise/sessions/new.html.erb +17 -0
  70. data/app/views/devise/shared/_links.erb +25 -0
  71. data/app/views/devise/unlocks/new.html.erb +12 -0
  72. data/app/views/home/index.html.haml +4 -0
  73. data/app/views/layouts/_activity_wrapper.html.haml +17 -0
  74. data/app/views/layouts/_nav_header.html.haml +26 -0
  75. data/app/views/layouts/_spaces.html.haml +34 -0
  76. data/app/views/layouts/application.html.haml +39 -0
  77. data/app/views/layouts/minimal.html.haml +22 -0
  78. data/app/views/projects/index.html.haml +16 -0
  79. data/app/views/projects/new.html.haml +6 -0
  80. data/app/views/public_activity/ticket/_assignment.html.haml +14 -0
  81. data/app/views/public_activity/ticket/_create_comment.html.haml +10 -0
  82. data/app/views/public_activity/ticket/_created.html.haml +3 -0
  83. data/app/views/public_activity/ticket/_reorder.html.haml +2 -0
  84. data/app/views/public_activity/ticket/_state_change.html.haml +2 -0
  85. data/app/views/public_activity/ticket/_update_details.html.haml +13 -0
  86. data/app/views/tickets/_activity.html.haml +3 -0
  87. data/app/views/tickets/_breadcrumb.html.haml +7 -0
  88. data/app/views/tickets/_bug_form.html.haml +1 -0
  89. data/app/views/tickets/_events_dropdown.html.haml +4 -0
  90. data/app/views/tickets/_form.html.haml +28 -0
  91. data/app/views/tickets/_project_form.html.haml +1 -0
  92. data/app/views/tickets/_story_form.html.haml +15 -0
  93. data/app/views/tickets/_task_form.html.haml +8 -0
  94. data/app/views/tickets/_tickets_listing.html.haml +5 -0
  95. data/app/views/tickets/_title_and_description.html.haml +8 -0
  96. data/app/views/tickets/_tree_node.html.haml +28 -0
  97. data/app/views/tickets/assignment.js.coffee +3 -0
  98. data/app/views/tickets/create.js.coffee +7 -0
  99. data/app/views/tickets/new.html.haml +1 -0
  100. data/app/views/tickets/show.html.haml +64 -0
  101. data/app/views/tickets/trigger_event.js.coffee +12 -0
  102. data/app/views/tickets/update.js.coffee +12 -0
  103. data/app/views/wiki/pages/_attachment.html.haml +5 -0
  104. data/app/views/wiki/pages/_attachments.html.haml +22 -0
  105. data/app/views/wiki/pages/_breadcrumb.html.haml +9 -0
  106. data/app/views/wiki/pages/_child_menu.html.haml +9 -0
  107. data/app/views/wiki/pages/_display.html.haml +138 -0
  108. data/app/views/wiki/pages/_page_header.html.haml +13 -0
  109. data/app/views/wiki/pages/_subpages_dropdown.html.haml +6 -0
  110. data/app/views/wiki/pages/_subtree.html.haml +7 -0
  111. data/app/views/wiki/pages/_tree_node.html.haml +21 -0
  112. data/app/views/wiki/pages/attach.js.coffee +2 -0
  113. data/app/views/wiki/pages/combined.html.haml +8 -0
  114. data/app/views/wiki/pages/create.js.coffee +5 -0
  115. data/app/views/wiki/pages/edit.html.haml +2 -0
  116. data/app/views/wiki/pages/history.html.haml +13 -0
  117. data/app/views/wiki/pages/index.html.haml +2 -0
  118. data/app/views/wiki/pages/new.html.haml +13 -0
  119. data/app/views/wiki/pages/show.html.haml +14 -0
  120. data/app/views/wiki/pages/update.html.haml +2 -0
  121. data/app/views/wiki/spaces/_page_list_item.html.haml +11 -0
  122. data/app/views/wiki/spaces/_space_list.html.haml +4 -0
  123. data/app/views/wiki/spaces/new.html.haml +5 -0
  124. data/app/views/wiki/spaces/space_list.html.haml +1 -0
  125. data/bin/bundle +3 -0
  126. data/bin/rails +4 -0
  127. data/bin/rake +4 -0
  128. data/config/application.rb +30 -0
  129. data/config/boot.rb +4 -0
  130. data/config/database.yml +39 -0
  131. data/config/environment.rb +5 -0
  132. data/config/environments/development.rb +35 -0
  133. data/config/environments/production.rb +80 -0
  134. data/config/environments/test.rb +36 -0
  135. data/config/initializers/backtrace_silencers.rb +7 -0
  136. data/config/initializers/config.rb +4 -0
  137. data/config/initializers/devise.rb +257 -0
  138. data/config/initializers/elasticsearch.rb +9 -0
  139. data/config/initializers/filter_parameter_logging.rb +4 -0
  140. data/config/initializers/gollum.rb +31 -0
  141. data/config/initializers/haml_gfm.rb +66 -0
  142. data/config/initializers/inflections.rb +16 -0
  143. data/config/initializers/mime_types.rb +5 -0
  144. data/config/initializers/secret_token.rb +12 -0
  145. data/config/initializers/session_store.rb +3 -0
  146. data/config/initializers/simple_form.rb +145 -0
  147. data/config/initializers/simple_form_bootstrap.rb +73 -0
  148. data/config/initializers/wrap_parameters.rb +14 -0
  149. data/config/locales/devise.en.yml +59 -0
  150. data/config/locales/en.yml +23 -0
  151. data/config/locales/simple_form.en.yml +26 -0
  152. data/config/routes.rb +101 -0
  153. data/config/tinymce.yml +10 -0
  154. data/config.ru +4 -0
  155. data/db/migrate/20131209041251_create_tickets.rb +13 -0
  156. data/db/migrate/20131210041050_devise_create_users.rb +42 -0
  157. data/db/migrate/20131210043014_add_username_to_users.rb +5 -0
  158. data/db/migrate/20131210045348_add_creator_to_ticket.rb +5 -0
  159. data/db/migrate/20131211034104_create_comments.rb +11 -0
  160. data/db/migrate/20131212073014_create_assignments.rb +12 -0
  161. data/db/migrate/20131215220825_add_position_to_ticket.rb +5 -0
  162. data/db/migrate/20131216044331_create_story_details.rb +12 -0
  163. data/db/migrate/20131218060557_create_activities.rb +23 -0
  164. data/db/migrate/20140109235844_create_pages.rb +14 -0
  165. data/db/migrate/20140112004346_create_attachments.rb +13 -0
  166. data/db/migrate/20140120030547_add_name_to_users.rb +5 -0
  167. data/db/schema.rb +125 -0
  168. data/db/seeds.rb +7 -0
  169. data/init.rb +1 -0
  170. data/lib/assets/.keep +0 -0
  171. data/lib/tasks/.keep +0 -0
  172. data/lib/tawork/engine.rb +13 -0
  173. data/lib/tawork/version.rb +3 -0
  174. data/lib/tawork.rb +4 -0
  175. data/lib/templates/haml/scaffold/_form.html.haml +10 -0
  176. data/lib/ticket_assignment.rb +25 -0
  177. data/log/.keep +0 -0
  178. data/public/401.html +57 -0
  179. data/public/404.html +58 -0
  180. data/public/422.html +58 -0
  181. data/public/500.html +57 -0
  182. data/public/favicon.ico +0 -0
  183. data/public/robots.txt +5 -0
  184. data/tags +279 -0
  185. data/tawork.gemspec +24 -0
  186. data/vendor/.DS_Store +0 -0
  187. data/vendor/assets/.DS_Store +0 -0
  188. data/vendor/assets/javascripts/.keep +0 -0
  189. data/vendor/assets/javascripts/bootstrap-tagsinput.js +503 -0
  190. data/vendor/assets/javascripts/bootstrap-tree.js +15 -0
  191. data/vendor/assets/javascripts/bootstrap-typeahead.js +335 -0
  192. data/vendor/assets/javascripts/hallo.js +2949 -0
  193. data/vendor/assets/javascripts/highlight.pack.js +1 -0
  194. data/vendor/assets/javascripts/jquery.autosize.js +250 -0
  195. data/vendor/assets/javascripts/jquery.mjs.nestedSortable.js +613 -0
  196. data/vendor/assets/javascripts/rangy-core.js +94 -0
  197. data/vendor/assets/javascripts/tinymce/mention/plugin.js +358 -0
  198. data/vendor/assets/stylesheets/.keep +0 -0
  199. data/vendor/assets/stylesheets/bootflat-extensions.css +356 -0
  200. data/vendor/assets/stylesheets/bootflat-square.css +69 -0
  201. data/vendor/assets/stylesheets/bootflat.css +1556 -0
  202. data/vendor/assets/stylesheets/bootstrap-tagsinput.css +45 -0
  203. data/vendor/assets/stylesheets/bootstrap-tree.sass +82 -0
  204. data/vendor/assets/stylesheets/highlight/arta.css +160 -0
  205. data/vendor/assets/stylesheets/highlight/ascetic.css +50 -0
  206. data/vendor/assets/stylesheets/highlight/brown_paper.css +105 -0
  207. data/vendor/assets/stylesheets/highlight/brown_papersq.png +0 -0
  208. data/vendor/assets/stylesheets/highlight/dark.css +105 -0
  209. data/vendor/assets/stylesheets/highlight/default.css +153 -0
  210. data/vendor/assets/stylesheets/highlight/docco.css +132 -0
  211. data/vendor/assets/stylesheets/highlight/far.css +113 -0
  212. data/vendor/assets/stylesheets/highlight/foundation.css +133 -0
  213. data/vendor/assets/stylesheets/highlight/github.css +130 -0
  214. data/vendor/assets/stylesheets/highlight/googlecode.css +146 -0
  215. data/vendor/assets/stylesheets/highlight/idea.css +122 -0
  216. data/vendor/assets/stylesheets/highlight/ir_black.css +105 -0
  217. data/vendor/assets/stylesheets/highlight/magula.css +123 -0
  218. data/vendor/assets/stylesheets/highlight/mono-blue.css +62 -0
  219. data/vendor/assets/stylesheets/highlight/monokai.css +127 -0
  220. data/vendor/assets/stylesheets/highlight/monokai_sublime.css +102 -0
  221. data/vendor/assets/stylesheets/highlight/obsidian.css +154 -0
  222. data/vendor/assets/stylesheets/highlight/pojoaque.css +105 -0
  223. data/vendor/assets/stylesheets/highlight/pojoaque.jpg +0 -0
  224. data/vendor/assets/stylesheets/highlight/railscasts.css +182 -0
  225. data/vendor/assets/stylesheets/highlight/rainbow.css +115 -0
  226. data/vendor/assets/stylesheets/highlight/school_book.css +113 -0
  227. data/vendor/assets/stylesheets/highlight/school_book.png +0 -0
  228. data/vendor/assets/stylesheets/highlight/solarized_dark.css +92 -0
  229. data/vendor/assets/stylesheets/highlight/solarized_light.css +92 -0
  230. data/vendor/assets/stylesheets/highlight/sunburst.css +160 -0
  231. data/vendor/assets/stylesheets/highlight/tomorrow-night-blue.css +52 -0
  232. data/vendor/assets/stylesheets/highlight/tomorrow-night-bright.css +51 -0
  233. data/vendor/assets/stylesheets/highlight/tomorrow-night-eighties.css +51 -0
  234. data/vendor/assets/stylesheets/highlight/tomorrow-night.css +52 -0
  235. data/vendor/assets/stylesheets/highlight/tomorrow.css +49 -0
  236. data/vendor/assets/stylesheets/highlight/vs.css +89 -0
  237. data/vendor/assets/stylesheets/highlight/xcode.css +157 -0
  238. data/vendor/assets/stylesheets/highlight/zenburn.css +117 -0
  239. metadata +299 -0
@@ -0,0 +1,115 @@
1
+ class Wiki::PagesController < ApplicationController
2
+ before_filter :authenticate_user!
3
+ before_filter :load_page
4
+
5
+ def index
6
+ end
7
+
8
+ def create
9
+ create_params = page_params
10
+ if params[:markdown_body].present?
11
+ create_params[:body] = redcarpet(params[:markdown_body])
12
+ end
13
+ @page = Page.new create_params.merge(
14
+ creator: current_user,
15
+ parent_id: params[:parent_id]
16
+ )
17
+ if @page.save
18
+ @page.update_to_gollum(current_user)
19
+ @page.create_activity key: "page.created", owner: current_user
20
+ end
21
+ end
22
+
23
+ def show
24
+ @subtree = @page.subtree.group_by(&:parent_id)
25
+ end
26
+
27
+ def history
28
+ end
29
+
30
+ def combined
31
+ @subtree = @page.subtree.group_by(&:parent_id)
32
+ @hide_extras = true
33
+ render layout: "minimal"
34
+ end
35
+
36
+ def update
37
+ update_params = params.require(params[:type]).permit(
38
+ :title, :body,
39
+ )
40
+ if params[:markdown_body].present?
41
+ update_params[:body] = redcarpet(params[:markdown_body])
42
+ end
43
+ if @page.update_attributes(update_params)
44
+ @page.update_to_gollum(current_user)
45
+ @page.create_activity key: "page.update_details", owner: current_user
46
+ end
47
+ end
48
+
49
+ def new
50
+ @page = Page.new creator: current_user
51
+ end
52
+
53
+ def reorder
54
+ old_parent_id = @page.parent_id
55
+ old_position = @page.position
56
+ if @page.reorder!(
57
+ parent_id: params[:parent_id],
58
+ position: params[:position]
59
+ )
60
+ @activity = @page.create_activity key: "page.reorder",
61
+ owner: current_user,
62
+ parameters: {
63
+ from_parent_id: old_parent_id,
64
+ old_position: old_position,
65
+ parent_id: params[:parent_id],
66
+ position: params[:position]
67
+ }
68
+ end
69
+
70
+ render json: {}
71
+ end
72
+
73
+ def attach
74
+ @attachment = Attachment.create_from_uploaded_file(params[:file], current_user, attachable: @page)
75
+
76
+ if @attachment.errors.none?
77
+ @activity = @ticket.page key: "ticket.attachment",
78
+ owner: current_user, recipient: @attachment
79
+ end
80
+ end
81
+
82
+ def subpages_dropdown
83
+ render partial: 'subpages_dropdown'
84
+ end
85
+
86
+ protected
87
+ def load_page
88
+ @page = Page.find(params[:id]) if params[:id]
89
+ end
90
+
91
+ private
92
+ def page_params
93
+ params.require(params[:type]).permit(:title, :body)
94
+ end
95
+
96
+ def redcarpet(text)
97
+ markdown = Redcarpet::Markdown.new(
98
+ Redcarpet::Render::HTML,
99
+ no_intra_emphasis: true,
100
+ tables: true,
101
+ fenced_code_blocks: true,
102
+ autolink: true,
103
+ # disable_indented_code_blocks: true,
104
+ strikethrough: true,
105
+ lax_spacing: true,
106
+ space_after_headers: true,
107
+ superscript: true,
108
+ # underline: true,
109
+ highlight: true,
110
+ quote: true,
111
+ footnotes: true,
112
+ )
113
+ markdown.render text
114
+ end
115
+ end
@@ -0,0 +1,37 @@
1
+ class Wiki::SpacesController < ApplicationController
2
+ before_filter :authenticate_user!
3
+
4
+ def new
5
+ @space = Space.new creator: current_user
6
+ end
7
+
8
+ def create
9
+ @space = Space.new space_params.merge(creator: current_user)
10
+
11
+ if @space.save
12
+ redirect_to wiki_page_path(@space)
13
+ else
14
+ render :new
15
+ end
16
+ end
17
+
18
+ def space_list
19
+ @page = Page.find params[:id]
20
+ @load_list = []
21
+ if params[:page_id].present?
22
+ @until_page = Page.find params[:page_id]
23
+ @load_list = @until_page.ancestor_ids
24
+ end
25
+ render layout: false
26
+ end
27
+
28
+ def load_till_space
29
+ @page = Page.find params[:page_id]
30
+ end
31
+
32
+ private
33
+ def space_params
34
+ params.require(:space).permit(:title)
35
+ end
36
+ end
37
+
@@ -0,0 +1,20 @@
1
+ module ApplicationHelper
2
+ [:project, :story, :bug, :task].each do |method|
3
+ define_method("#{method}_path") do |*args|
4
+ ticket_path(*args)
5
+ end
6
+
7
+ define_method("#{method.to_s.pluralize}_path") do |*args|
8
+ tickets_path(*args)
9
+ end
10
+ end
11
+
12
+ def ticket_types
13
+ Ticket::ALLOWED_TYPES.each_with_object({}) do |type, hash|
14
+ hash[type] = Ticket.filtered_type_class(type).new(
15
+ creator: current_user,
16
+ parent_id: params[:parent_id]
17
+ )
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,2 @@
1
+ module Wiki::PagesHelper
2
+ end
@@ -0,0 +1,6 @@
1
+ class FakeInput < SimpleForm::Inputs::StringInput
2
+ # This method only create a basic input without reading any value from object
3
+ def input
4
+ template.text_field_tag(attribute_name, nil, input_html_options)
5
+ end
6
+ end
data/app/mailers/.keep ADDED
File without changes
data/app/models/.keep ADDED
File without changes
@@ -0,0 +1,5 @@
1
+ class Assignment < ActiveRecord::Base
2
+ belongs_to :user
3
+ belongs_to :ticket
4
+ belongs_to :assigner, class_name: "User"
5
+ end
@@ -0,0 +1,54 @@
1
+ class Attachment < ActiveRecord::Base
2
+ include Tire::Model::Search
3
+ include Tire::Model::Callbacks
4
+
5
+ # after_save :save_to_git
6
+ belongs_to :attachable, :polymorphic => true
7
+ validates_presence_of :attachable
8
+
9
+ def self.create_from_uploaded_file(file, user, options = {})
10
+ filename = file.original_filename
11
+ attachment = Attachment.create( options.merge(
12
+ filename: filename,
13
+ content_type: file.content_type,
14
+ size: file.size,
15
+ ))
16
+
17
+ if attachment.errors.none?
18
+ attachment.save_to_gollum(
19
+ file, user,
20
+ "uploading file: #{filename}"
21
+ )
22
+ end
23
+
24
+ attachment
25
+ end
26
+
27
+ def file_data
28
+ format = File.extname(filename).gsub(".", "")
29
+ $wiki.file(wiki_dir + "/" + wiki_attachment_data + "." + format).raw_data
30
+ end
31
+
32
+ def save_to_gollum(file, user, message)
33
+ commit = {
34
+ message: message,
35
+ name: user.name,
36
+ email: user.email
37
+ }
38
+
39
+ file_name = file.try(:original_filename) || File.basename(file.path)
40
+ extension = File.extname(file_name).gsub(".", "")
41
+ data_file = file.try(:tempfile) || file
42
+ $wiki.write_page(wiki_attachment_data, extension, data_file, commit, wiki_dir)
43
+ end
44
+
45
+ protected
46
+
47
+ def wiki_attachment_data
48
+ "#{id}-data"
49
+ end
50
+
51
+ def wiki_dir
52
+ "attachments/#{id}"
53
+ end
54
+ end
data/app/models/bug.rb ADDED
@@ -0,0 +1,2 @@
1
+ class Bug < Ticket
2
+ end
@@ -0,0 +1,6 @@
1
+ class Comment < ActiveRecord::Base
2
+ belongs_to :user
3
+ belongs_to :ticket
4
+
5
+ validates :body, presence: true
6
+ end
File without changes
@@ -0,0 +1,68 @@
1
+ class Page < ActiveRecord::Base
2
+ include PublicActivity::Common
3
+
4
+ include Tire::Model::Search
5
+ include Tire::Model::Callbacks
6
+
7
+ has_ancestry #touch: true
8
+ acts_as_list scope: [:ancestry]
9
+
10
+ attr_accessor :old_ancestor_ids
11
+
12
+ belongs_to :creator_id
13
+ has_many :attachments, as: :attachable, dependent: :destroy
14
+
15
+ validates_presence_of :creator
16
+ validates_presence_of :title
17
+
18
+ belongs_to :creator, class_name: 'User'
19
+ after_save :touch_ancestry
20
+
21
+ def to_param
22
+ [id, title.parameterize].join("-")
23
+ end
24
+
25
+ def wiki_dir
26
+ "wiki/#{id}"
27
+ end
28
+
29
+ def update_to_gollum(user)
30
+ commit = {
31
+ message: 'no message',
32
+ name: user.name,
33
+ email: user.email
34
+ }
35
+
36
+ body_page = "#{id}-body"
37
+ title_page = "#{id}-title"
38
+
39
+ $wiki.write_page(wiki_dir + "/" + body_page, :textile, body, commit)
40
+ $wiki.write_page(wiki_dir + "/" + title_page, :textile, title, commit)
41
+ end
42
+
43
+ def type
44
+ @type || "Page"
45
+ end
46
+
47
+ def versions
48
+ $wiki.page("#{id}-body", nil, wiki_dir).try(:versions) || []
49
+ end
50
+
51
+ def reorder!(options)
52
+ if options[:parent_id]
53
+ self.old_ancestor_ids = self.ancestor_ids
54
+ self.parent_id = options[:parent_id]
55
+ self.save
56
+ end
57
+ self.insert_at options[:position].to_i
58
+ self.save
59
+ end
60
+
61
+ def touch_ancestry
62
+ self.old_ancestor_ids ||= []
63
+ Page.where(id: (ancestor_ids + old_ancestor_ids).uniq).update_all(updated_at: Time.now)
64
+ end
65
+
66
+ private
67
+
68
+ end
@@ -0,0 +1,2 @@
1
+ class Project < Ticket
2
+ end
@@ -0,0 +1,2 @@
1
+ class Space < Page
2
+ end
@@ -0,0 +1,29 @@
1
+ class Story < Ticket
2
+ has_one :story_detail, foreign_key: "ticket_id", dependent: :destroy
3
+
4
+ validates :who, :what, presence: true
5
+
6
+ after_save do
7
+ story_detail.save if story_detail.changed?
8
+ end
9
+
10
+ [:who, :what, :why].each do |method|
11
+ define_method(method) do |*args|
12
+ return nil unless story_detail
13
+ story_detail.send(method, *args)
14
+ end
15
+
16
+ define_method("#{method}=") do |*args|
17
+ build_story_detail unless story_detail
18
+ story_detail.send("#{method}=", *args)
19
+ end
20
+ end
21
+
22
+ def title
23
+ return super unless who.present? && what.present?
24
+ title = "#{who} wants to #{what}"
25
+ title += ", so: #{why}" if why.present?
26
+
27
+ title
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ class StoryDetail < ActiveRecord::Base
2
+ belongs_to :story, foreign_key: "ticket_id"
3
+ end
@@ -0,0 +1,2 @@
1
+ class Task < Ticket
2
+ end
@@ -0,0 +1,127 @@
1
+ class Ticket < ActiveRecord::Base
2
+ include AASM
3
+ include PublicActivity::Common
4
+
5
+ include Tire::Model::Search
6
+ include Tire::Model::Callbacks
7
+
8
+
9
+
10
+ has_ancestry #touch: true
11
+ acts_as_list scope: [:ancestry]
12
+
13
+ attr_accessor :old_ancestor_ids
14
+
15
+ class InvalidTypeError < StandardError; end
16
+ class UnknownEventError < StandardError; end
17
+
18
+ ALLOWED_TYPES = %w(story task bug)
19
+
20
+ belongs_to :creator, class_name: 'User'
21
+ validates_presence_of :creator
22
+ validates_presence_of :title
23
+
24
+ has_many :comments
25
+ has_many :assignments
26
+ has_many :attachments, as: :attachable, dependent: :destroy
27
+
28
+
29
+ after_save :touch_ancestry
30
+
31
+ ACTIVE_STATES = [:open, :current, :current, :completed]
32
+ scope :active, -> {where(state: ACTIVE_STATES)}
33
+
34
+ ARCHIVED_STATES = [:rejected, :verified]
35
+ scope :archived, -> {where(state: ARCHIVED_STATES)}
36
+
37
+ scope :with_state, ->(state) {where(state: state.to_s)}
38
+
39
+ aasm column: :state do
40
+ state :open, initial: true
41
+ state :current
42
+ state :completed
43
+
44
+ state :rejected
45
+ state :verified
46
+
47
+ event :reopen do
48
+ transitions to: :open, from: [:current, :completed, :rejected, :verified]
49
+ end
50
+
51
+ event :start do
52
+ transitions to: :current, from: :open
53
+ end
54
+
55
+ event :restart do
56
+ transitions to: :current, from: [:verified, :completed]
57
+ end
58
+
59
+ event :complete do
60
+ transitions to: :completed, from: [:open, :current]
61
+ end
62
+
63
+ event :reject do
64
+ transitions to: :rejected, from: [:open, :current]
65
+ end
66
+
67
+ event :verify do
68
+ transitions to: :verified, from: [:current, :completed]
69
+ end
70
+ end
71
+
72
+ def self.filtered_type_class(type)
73
+ raise InvalidTypeError unless ALLOWED_TYPES.include?(type)
74
+ type.classify.constantize
75
+ end
76
+
77
+ def self.ordered
78
+ order("position ASC")
79
+ end
80
+
81
+ def short_title(num_words = 2)
82
+ words = title.split
83
+ short = words[0..num_words].join(" ")
84
+ short += "..." if words.count > num_words
85
+
86
+ short
87
+ end
88
+
89
+
90
+ def assignment_users_hash
91
+ assignments.each_with_object([]) do |assignment, array|
92
+ array << {
93
+ id: assignment.user.id,
94
+ name: assignment.user.display_name,
95
+ }
96
+ end
97
+
98
+ end
99
+
100
+ def assignment_users
101
+ assignments.map{|assignment| [assignment.user.display_name, assignment.user.id]}
102
+ end
103
+
104
+ def tags
105
+ ""
106
+ end
107
+
108
+ def trigger_event!(event)
109
+ raise UnknownEventError unless self.aasm.events.include?(event.to_sym)
110
+ self.send("#{event}!")
111
+ end
112
+
113
+ def reorder!(options)
114
+ if options[:parent_id]
115
+ self.old_ancestor_ids = self.ancestor_ids
116
+ self.parent_id = options[:parent_id]
117
+ self.save
118
+ end
119
+ self.insert_at options[:position].to_i
120
+ self.save
121
+ end
122
+
123
+ def touch_ancestry
124
+ self.old_ancestor_ids ||= []
125
+ Ticket.where(id: (ancestor_ids + old_ancestor_ids).uniq).update_all(updated_at: Time.now)
126
+ end
127
+ end
@@ -0,0 +1,36 @@
1
+ class User < ActiveRecord::Base
2
+ include Tire::Model::Search
3
+ include Tire::Model::Callbacks
4
+
5
+ # Include default devise modules. Others available are:
6
+ # :confirmable, :lockable, :timeoutable and :omniauthable
7
+ devise :database_authenticatable, :registerable,
8
+ :recoverable, :rememberable, :trackable, :validatable
9
+ devise :omniauthable, omniauth_providers: [:google_apps]
10
+
11
+ has_many :tickets, foreign_key: 'creator_id'
12
+ has_many :projects, foreign_key: 'creator_id', class_name: "Project"
13
+
14
+ def self.find_for_open_id(access_token, signed_in_resource = nil)
15
+ data = access_token.info
16
+ name = data["name"]
17
+
18
+ if name.blank?
19
+ name = data["first_name"] + " " + data["last_name"]
20
+ end
21
+
22
+ if user = User.where(email: data["email"]).first
23
+ user
24
+ else
25
+ User.create!(
26
+ name: name,
27
+ email: data["email"],
28
+ password: Devise.friendly_token[0,20]
29
+ )
30
+ end
31
+ end
32
+
33
+ def display_name
34
+ name || username || email
35
+ end
36
+ end
@@ -0,0 +1,2 @@
1
+ class UserVerifier
2
+ end
@@ -0,0 +1,6 @@
1
+ %li.panel.panel-default.list-group-item
2
+ .panel-body
3
+ :markdown
4
+ #{comment.body}
5
+ %small
6
+ #{comment.user.display_name}
@@ -0,0 +1,4 @@
1
+ = simple_form_for comment, remote: true do |f|
2
+ = f.input :ticket_id, as: :hidden
3
+ = f.input :body, placeholder: "add a comment", label: false
4
+ = f.submit "Add Comment", class: 'btn btn-primary'
@@ -0,0 +1,7 @@
1
+ <% if @comment.errors.any? %>
2
+ $("#new_comment").replaceWith('<%=j render 'form', comment: @comment %>')
3
+ <% else %>
4
+ $("#new_comment").replaceWith('<%=j render 'form', comment: Comment.new(ticket_id: params[:comment][:ticket_id]) %>')
5
+ $(".ticket[data-ticket-id=<%= @ticket.id %>] > #activities")
6
+ .prepend('<%=j render 'tickets/activity', ticket: @ticket, activity: @activity %>')
7
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <h2>Resend confirmation instructions</h2>
2
+
3
+ <%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <div><%= f.label :email %><br />
7
+ <%= f.email_field :email, :autofocus => true %></div>
8
+
9
+ <div><%= f.submit "Resend confirmation instructions" %></div>
10
+ <% end %>
11
+
12
+ <%= render "devise/shared/links" %>
@@ -0,0 +1,5 @@
1
+ <p>Welcome <%= @email %>!</p>
2
+
3
+ <p>You can confirm your account email through the link below:</p>
4
+
5
+ <p><%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @token) %></p>
@@ -0,0 +1,8 @@
1
+ <p>Hello <%= @resource.email %>!</p>
2
+
3
+ <p>Someone has requested a link to change your password. You can do this through the link below.</p>
4
+
5
+ <p><%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @token) %></p>
6
+
7
+ <p>If you didn't request this, please ignore this email.</p>
8
+ <p>Your password won't change until you access the link above and create a new one.</p>
@@ -0,0 +1,7 @@
1
+ <p>Hello <%= @resource.email %>!</p>
2
+
3
+ <p>Your account has been locked due to an excessive number of unsuccessful sign in attempts.</p>
4
+
5
+ <p>Click the link below to unlock your account:</p>
6
+
7
+ <p><%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @token) %></p>
@@ -0,0 +1,16 @@
1
+ <h2>Change your password</h2>
2
+
3
+ <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %>
4
+ <%= devise_error_messages! %>
5
+ <%= f.hidden_field :reset_password_token %>
6
+
7
+ <div><%= f.label :password, "New password" %><br />
8
+ <%= f.password_field :password, :autofocus => true %></div>
9
+
10
+ <div><%= f.label :password_confirmation, "Confirm new password" %><br />
11
+ <%= f.password_field :password_confirmation %></div>
12
+
13
+ <div><%= f.submit "Change my password" %></div>
14
+ <% end %>
15
+
16
+ <%= render "devise/shared/links" %>
@@ -0,0 +1,12 @@
1
+ <h2>Forgot your password?</h2>
2
+
3
+ <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <div><%= f.label :email %><br />
7
+ <%= f.email_field :email, :autofocus => true %></div>
8
+
9
+ <div><%= f.submit "Send me reset password instructions" %></div>
10
+ <% end %>
11
+
12
+ <%= render "devise/shared/links" %>
@@ -0,0 +1,29 @@
1
+ <h2>Edit <%= resource_name.to_s.humanize %></h2>
2
+
3
+ <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
4
+ <%= devise_error_messages! %>
5
+
6
+ <div><%= f.label :email %><br />
7
+ <%= f.email_field :email, :autofocus => true %></div>
8
+
9
+ <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
10
+ <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
11
+ <% end %>
12
+
13
+ <div><%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
14
+ <%= f.password_field :password, :autocomplete => "off" %></div>
15
+
16
+ <div><%= f.label :password_confirmation %><br />
17
+ <%= f.password_field :password_confirmation %></div>
18
+
19
+ <div><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
20
+ <%= f.password_field :current_password %></div>
21
+
22
+ <div><%= f.submit "Update" %></div>
23
+ <% end %>
24
+
25
+ <h3>Cancel my account</h3>
26
+
27
+ <p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete %></p>
28
+
29
+ <%= link_to "Back", :back %>