rails-wiki 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +55 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/javascripts/inject.js +34 -0
  6. data/app/assets/javascripts/wiki/application.js +13 -0
  7. data/app/assets/stylesheets/wiki/application.css +15 -0
  8. data/app/controllers/wiki/application_controller.rb +33 -0
  9. data/app/controllers/wiki/pages_controller.rb +80 -0
  10. data/app/helpers/wiki/application_helper.rb +4 -0
  11. data/app/helpers/wiki/wiki_helper.rb +8 -0
  12. data/app/models/wiki/attachment.rb +116 -0
  13. data/app/models/wiki/page.rb +213 -0
  14. data/app/models/wiki/wiki.rb +194 -0
  15. data/app/views/layouts/application.html.erb +14 -0
  16. data/app/views/layouts/wiki.html.erb +19 -0
  17. data/app/views/wiki/_change.erb +21 -0
  18. data/app/views/wiki/_search.erb +4 -0
  19. data/app/views/wiki/pages/edit.html.erb +69 -0
  20. data/app/views/wiki/pages/index.html.erb +16 -0
  21. data/app/views/wiki/pages/log.html.erb +29 -0
  22. data/app/views/wiki/pages/show.html.erb +50 -0
  23. data/config/initializers/wiki.rb +35 -0
  24. data/config/routes.rb +16 -0
  25. data/lib/frontmatter.rb +23 -0
  26. data/lib/markdown.rb +24 -0
  27. data/lib/rails-wiki.rb +18 -0
  28. data/lib/tasks/wiki_tasks.rake +4 -0
  29. data/lib/wiki/engine.rb +10 -0
  30. data/lib/wiki/version.rb +3 -0
  31. data/test/dummy/Rakefile +6 -0
  32. data/test/dummy/app/assets/javascripts/application.js +13 -0
  33. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  34. data/test/dummy/app/controllers/application_controller.rb +16 -0
  35. data/test/dummy/app/helpers/application_helper.rb +2 -0
  36. data/test/dummy/app/models/user.rb +11 -0
  37. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/test/dummy/bin/bundle +3 -0
  39. data/test/dummy/bin/rails +4 -0
  40. data/test/dummy/bin/rake +4 -0
  41. data/test/dummy/bin/setup +29 -0
  42. data/test/dummy/config.ru +4 -0
  43. data/test/dummy/config/application.rb +23 -0
  44. data/test/dummy/config/boot.rb +5 -0
  45. data/test/dummy/config/environment.rb +5 -0
  46. data/test/dummy/config/environments/development.rb +39 -0
  47. data/test/dummy/config/environments/production.rb +77 -0
  48. data/test/dummy/config/environments/test.rb +42 -0
  49. data/test/dummy/config/initializers/assets.rb +11 -0
  50. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  51. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  52. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  53. data/test/dummy/config/initializers/inflections.rb +16 -0
  54. data/test/dummy/config/initializers/mime_types.rb +4 -0
  55. data/test/dummy/config/initializers/session_store.rb +3 -0
  56. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  57. data/test/dummy/config/locales/en.yml +23 -0
  58. data/test/dummy/config/routes.rb +4 -0
  59. data/test/dummy/config/secrets.yml +22 -0
  60. data/test/dummy/db/wiki/test.md +1 -0
  61. data/test/dummy/db/wiki/test/brown-forward.jpg +0 -0
  62. data/test/dummy/log/development.log +1558 -0
  63. data/test/dummy/public/404.html +67 -0
  64. data/test/dummy/public/422.html +67 -0
  65. data/test/dummy/public/500.html +66 -0
  66. data/test/dummy/public/favicon.ico +0 -0
  67. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/20sgF5rnRshHxz-s4EBzqr6J28hCEioHDZq4UwSklVg.cache +0 -0
  68. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/2c3DnJIoxPn2b4vs5wqFWgOX0nJESf5SGAUi9N58_9o.cache +0 -0
  69. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/3CXqY8DyRTsJJYP9iYJaaQRmpXDbd-MFR1m4pug2GnQ.cache +1 -0
  70. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/3RqPYAE8n3lRHkprA7M3_eFnzLV5PF6FMwwk-fs_ieM.cache +1 -0
  71. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/5Lly_CA8DZvPhQV2jDQx-Y6P_y3Ygra9t5jfSlGhHDA.cache +2 -0
  72. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/BPtu96YPU5tNXsZRFA9WtMIergTFfHMuhAG3ON7DdJY.cache +0 -0
  73. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/D0X36t9hc6V_oaygIyRL41fwhkF3u9Lw12FETkPMVoc.cache +1 -0
  74. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/GtnQFUKxR1hZ540gdO9116uyLq4JfA-QB7ZsLJwvlj0.cache +1 -0
  75. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/HiLFBQupKg3TaAuqwWTIVzukgysleTkF1bjaEaIODS0.cache +1 -0
  76. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/JZuS80RuzB2Cuqgdj4m_cnDrAKe36_z_v5Ty_dPoIJ4.cache +3 -0
  77. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/OI6uxGcnsKavdWTtwDAasU3wPx8QXhzBgV0X2n1KjMQ.cache +3 -0
  78. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/X6P11z9IhRL9BK1a0t3QcW0nzXwZKzz50d35fpmMDbQ.cache +1 -0
  79. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/YzlRBEEf9MMtDMXOU1Dy6zoeRsaAZSXIUSZmXxVflT0.cache +0 -0
  80. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/h0IIenP5nZ0YCtFDIHLHyp9pvsOLSDEu6ATiy3gr5D0.cache +1 -0
  81. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/hOPmvE7Kf6eW1WQC0bUXD7G5V_zrmprQYp0Ovw03Cng.cache +1 -0
  82. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/hPk5G_BGOYjelHgOl4_2QEgvC6YnAVRQzTyN1dBvBAE.cache +0 -0
  83. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/hZi1k6tpxxCGYxRe7zY74ItcOI8gZrREOpGuA8JSpGg.cache +3 -0
  84. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/hz0b1Jdsh_JFLZIiQe7zAkicSerKslNSllnkeXXp_04.cache +2 -0
  85. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/nAoWwNAloHngS8ny7fZE5G4uUShw8tjgvVMkDMvMkpg.cache +1 -0
  86. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/pEhaat2KBd5SrT7szC_8R1_6hK17FTpvoRFkmCRSD3M.cache +2 -0
  87. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/qZPgqsYHITlAKR9bPIGwt2SSzDuKpMUUkQrEZb2MkMg.cache +0 -0
  88. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/rG7CS4nu-OV8bgDR74JP3ukGDdcMYoC_w7V0TMphk04.cache +0 -0
  89. data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/z-qEGw9bb3jqMEbMPC02VNSESIylpz0Pri2mSnoVNv8.cache +1 -0
  90. data/test/integration/navigation_test.rb +10 -0
  91. data/test/test_helper.rb +20 -0
  92. data/test/wiki_test.rb +7 -0
  93. metadata +256 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: aaa5b76e359711bd8dc4aa0e183906b335bce16d
4
+ data.tar.gz: f3c24e3ec96ae567912750f402331420e12ecb7a
5
+ SHA512:
6
+ metadata.gz: 82b49289f36a9d89d5df6ca1f4e740b9761c9c2fe32a0a745124e657a3738b68f9075f6f9c53ec966e34784773700486c527c9a5528dc59133ba6e00b31ff590
7
+ data.tar.gz: 7d859845c6c6f10347cdef24afca2658208e6a46e1b85a3b41effbd6b698f95f2e48ec807a695bb7ec06cbc903a01cad2d4704841ad5c351299d6080d803fa7f
@@ -0,0 +1,20 @@
1
+ Copyright 2015 Hans Gerwitz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,55 @@
1
+ # Rails Wiki
2
+
3
+ A small Rails engine that provides a git-based Markdown wiki.
4
+
5
+ WARNING: this has been abstracted from a proprietary tool, and so is poorly documented and lacks a standalone test suite. It would be premature to include it as a blind dependency.
6
+
7
+ ## How it Works
8
+
9
+ Rails Wiki is based on [gollum-lib](https://github.com/gollum/gollum-lib). None of [Gollum](https://github.com/gollum/gollum/wiki)'s layout (e.g. sidebars, headers, footers) are implemented, and a many of the text filters (e.g. macros, code, diagrams) have been disabled. Also unlike Gollum, the hierarchy of pages is considered meaningful.
10
+
11
+ The storage structure will be familiar to users of Middleman or Jekyll. Each page is an `.md` file with YAML frontmatter for metadata. Other files are treated as attachments to the page associated with their containing directory.
12
+
13
+ All storage is via a local Git repository. Optionally, a remote can be specified and changes will be pushed to it.
14
+
15
+ _TODO: review and document remote behavior_
16
+
17
+ ## Installation
18
+
19
+ Add Rails Wiki to your Gemfile
20
+
21
+ gem 'rails-wiki'
22
+
23
+ Your application controller needs to provide two methods:
24
+
25
+ - `auth_required` is called on every request
26
+ - `current_user` returns a _user object_
27
+
28
+ The user object must response to `.name` and `.email` with string values.
29
+
30
+ ## Configuration
31
+
32
+ In your `routes.rb`, specify a mount point:
33
+
34
+ mount Wiki::Engine, at: '/wiki'
35
+
36
+ In your `config/environment/*` files, configure the wiki:
37
+
38
+ require 'rails-wiki'
39
+ Wiki.local_directory = 'db/wiki'
40
+
41
+ _TODO: move this configuration to an initializer?_
42
+
43
+ ## Usage
44
+
45
+ Pathnames that begin with underscore (`_`) are reserved.
46
+
47
+ ## Feature Roadmap
48
+
49
+ 1. Markdown link rooting
50
+ 1. Recursive page deletion
51
+ 1. Individual attachment deletion
52
+ 1. Path redirection rules
53
+ 1. Configurable help paths
54
+ 1. Nicer attachment upload
55
+ 1. Nicer page editing
@@ -0,0 +1,37 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Rails Wiki'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task default: :test
@@ -0,0 +1,34 @@
1
+ // inserts text at cursor, or before and after selection
2
+ // based on http://stackoverflow.com/a/12815163/5610
3
+ jQuery.fn.extend({
4
+ inject: function(insertTextPre, insertTextPost){
5
+ return this.each(function(i) {
6
+ if (document.selection) {
7
+ // IE
8
+ this.focus();
9
+ sel = document.selection.createRange();
10
+ sel.text = insertTextPre + insertTextPost;
11
+ this.focus();
12
+ }
13
+ else if (this.selectionStart || this.selectionStart == '0') {
14
+ // modern browser
15
+ var startPos = this.selectionStart;
16
+ var endPos = this.selectionEnd;
17
+ var scrollTop = this.scrollTop;
18
+ this.value = this.value.substring(0, startPos)+insertTextPre+this.value.substring(startPos,endPos)+insertTextPost+this.value.substring(endPos,this.value.length);
19
+ this.focus();
20
+ this.selectionStart = startPos + insertTextPre.length;
21
+ this.selectionEnd = ((startPos + insertTextPre.length) + this.value.substring(startPos,endPos).length);
22
+ this.scrollTop = scrollTop;
23
+ } else {
24
+ // no selection, only use prefix
25
+ if (document.queryCommandSupported('insertText')) {
26
+ document.execCommand('insertText', false, text);
27
+ } else {
28
+ this.value += insertTextPre;
29
+ this.focus();
30
+ }
31
+ }
32
+ })
33
+ }
34
+ });
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,33 @@
1
+ module Wiki
2
+ # inherit from host app for current_user and auth_required
3
+ class ApplicationController < ::ApplicationController
4
+
5
+ before_filter :auth_required
6
+
7
+ layout 'layouts/wiki'
8
+
9
+ def display
10
+ @home = Rails.configuration.wiki.home_page
11
+
12
+ ext = File.extname(request.path_info).downcase
13
+ if ext.blank? || ext == ".html"
14
+ @page = Rails.configuration.wiki.find_page(params[:path])
15
+ if @page.nil?
16
+ raise Exception.new("Attempt to create orphan page")
17
+ elsif @page.new_page?
18
+ redirect_to controller: :pages, action: :new
19
+ else
20
+ render 'wiki/pages/show'
21
+ end
22
+ else
23
+ @attachment = Rails.configuration.wiki.find_attachment(params[:path], ext)
24
+ if @attachment.blank?
25
+ raise ActionController::RoutingError.new('File Not Found')
26
+ else
27
+ send_file @attachment.filesystem_path, type: @attachment.mime_type, disposition: 'inline'
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,80 @@
1
+ class Wiki::PagesController < Wiki::ApplicationController
2
+
3
+ layout 'layouts/wiki'
4
+
5
+ def index
6
+ select = params[:select]
7
+ query = params[:query]
8
+ if query
9
+ @query = query
10
+ @pages = Rails.configuration.wiki.search(query)
11
+ else
12
+ @pages = Rails.configuration.wiki.all_pages
13
+ end
14
+ end
15
+
16
+ def log
17
+ @changes_by_path = Rails.configuration.wiki.changelog.group_by{|c| c[:path]}
18
+ end
19
+
20
+ def new
21
+ @page = Rails.configuration.wiki.find_page(params[:path])
22
+ render :edit
23
+ end
24
+
25
+ def create
26
+ @page = Rails.configuration.wiki.create_page(params[:path])
27
+ @page.update(page_params)
28
+ if @page.save(current_user)
29
+ redirect_to @page
30
+ else
31
+ render :edit
32
+ end
33
+ end
34
+
35
+ def edit
36
+ Rails.configuration.wiki.pull_repo
37
+ @page = Rails.configuration.wiki.find_page(params[:path])
38
+ end
39
+
40
+ def update
41
+ @page = Rails.configuration.wiki.find_page(params[:path])
42
+ @page.update(page_params)
43
+ if @page.save(current_user)
44
+ redirect_to @page
45
+ else
46
+ render :edit
47
+ end
48
+ end
49
+
50
+ def destroy
51
+ return_path = root_path
52
+ # ext = File.extname(request.path_info).downcase
53
+ # if ext.blank? || ext == "html"
54
+ page = Rails.configuration.wiki.find_page(params[:path])
55
+ parent_path = page.parent_path
56
+ if parent_path.empty?
57
+ return_path = root_path
58
+ else
59
+ return_path = page_path(parent_path)
60
+ end
61
+ page.destroy!(current_user)
62
+ # TODO: recurse pages and attachments
63
+
64
+ # else
65
+ # file = Rails.configuration.wiki.find_file(params[:path] + '.' + params[:format])
66
+ # return_path = page_path(file.parent_path)
67
+ # file.destroy!(current_user)
68
+ # end
69
+
70
+ # TODO: look for first *valid* parent
71
+ redirect_to return_path
72
+ end
73
+
74
+ private
75
+
76
+ def page_params
77
+ params.require(:page).permit(:name, :content, uploaded_files: [])
78
+ end
79
+
80
+ end
@@ -0,0 +1,4 @@
1
+ module Wiki
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,8 @@
1
+ module Wiki
2
+ module WikiHelper
3
+ def wiki_changes(max=10)
4
+ changes = Rails.configuration.wiki.changelog.first(max)
5
+ "<ol>#{render partial: "wiki/change", collection: changes}</ol>".html_safe
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,116 @@
1
+ require 'active_model'
2
+
3
+ module Wiki
4
+ class Attachment
5
+
6
+ include ActiveModel::AttributeMethods
7
+ include ActiveModel::Conversion
8
+ include ActiveModel::Dirty
9
+ extend ActiveModel::Naming
10
+
11
+ define_attribute_methods :name, :content
12
+
13
+ attr_accessor :wiki, :name, :content, :path, :format
14
+ alias :id :path
15
+
16
+ def name=(value)
17
+ name_will_change!
18
+ @name=value unless value.nil?
19
+ end
20
+
21
+ def content=(value)
22
+ content_will_change!
23
+ @content=value unless value.nil?
24
+ end
25
+
26
+ def to_s
27
+ self.path
28
+ end
29
+
30
+ def path
31
+ @path
32
+ end
33
+
34
+ def update(hash)
35
+ self.name = hash[:name]
36
+ self.content = hash[:content]
37
+
38
+ return true
39
+ end
40
+
41
+ def initialize(params={})
42
+ wiki = params[:wiki]
43
+ gollum_file = params[:gollum_file]
44
+ path = params[:path]
45
+
46
+ if wiki
47
+ @wiki = wiki
48
+ end
49
+ if gollum_file
50
+ self.gollum_file = gollum_file
51
+ elsif path
52
+ safepath = path.downcase
53
+ @path = safepath
54
+ new_file = @wiki.find_gollum_file(safepath)
55
+ if new_file
56
+ self.gollum_file = new_file
57
+ end
58
+ end
59
+ end
60
+
61
+ def gollum_file=(file)
62
+ @gollum_file = file
63
+ @name = file.name
64
+ @format = File.extname(file.name).split('.').last.to_sym
65
+ # filename = ::Attachment.basename(fullname, ext)
66
+ @path = file.url_path + file.name
67
+ end
68
+
69
+ def gollum_file
70
+ @gollum_file
71
+ end
72
+
73
+ def filesystem_path
74
+ return File.join(@wiki.base_path, @path)
75
+ end
76
+
77
+ def mime_type
78
+ @gollum_file.mime_type
79
+ end
80
+
81
+ def to_param
82
+ @path
83
+ end
84
+
85
+ def parent_path
86
+ parent_path = File.dirname(@path)
87
+ (parent_path == '.') ? '' : parent_path + '/'
88
+ end
89
+
90
+ def destroy!(user)
91
+ commit_options = {name: user.name, email: user.email, message: "removed #{@path}"}
92
+ Rails.logger.debug("removing #{@path}")
93
+
94
+ committer = Gollum::Committer.new(@wiki.gollum_wiki, commit_options)
95
+ committer.delete(@path)
96
+
97
+ committer.after_commit do |index, _sha|
98
+ dir = File.dirname(@path)
99
+ dir = '' if dir == '.'
100
+
101
+ @wiki.gollum_wiki.clear_cache
102
+ committer.update_working_dir(dir, parent_path, @format)
103
+ end
104
+
105
+ @wiki.pull_repo
106
+ committer.commit
107
+ @wiki.push_repo
108
+ end
109
+
110
+ def persisted?
111
+ # TODO: make this a real dirty flag
112
+ true
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,213 @@
1
+ require 'active_model'
2
+
3
+ module Wiki
4
+ class Page
5
+ # a gollum page facade
6
+
7
+ @@format = :markdown
8
+
9
+ # include ActiveModel::Model
10
+ include ActiveModel::AttributeMethods
11
+ include ActiveModel::Conversion
12
+ include ActiveModel::Dirty
13
+ extend ActiveModel::Naming
14
+
15
+ define_attribute_methods :name, :content
16
+
17
+ attr_accessor :wiki, :name, :content, :path, :uploaded_files
18
+ alias :id :path
19
+
20
+ def name=(value)
21
+ name_will_change!
22
+ @name=value unless value.nil?
23
+ end
24
+
25
+ def content=(value)
26
+ content_will_change!
27
+ @content=value unless value.nil?
28
+ end
29
+
30
+ def to_param
31
+ @path
32
+ end
33
+
34
+ def path
35
+ @path
36
+ end
37
+
38
+ def update(hash)
39
+ self.name = hash[:name]
40
+ self.content = hash[:content]
41
+ @uploaded_files = hash[:uploaded_files]
42
+
43
+ return true
44
+ end
45
+
46
+ def initialize(params={})
47
+ wiki = params[:wiki]
48
+ gollum_page = params[:gollum_page]
49
+ path = params[:path]
50
+
51
+ if wiki
52
+ @wiki = wiki
53
+ end
54
+ if gollum_page
55
+ self.gollum_page = gollum_page
56
+ elsif path
57
+ safepath = path.downcase
58
+ @path = safepath
59
+ new_page = @wiki.find_gollum_page(safepath)
60
+ if new_page
61
+ self.gollum_page = new_page
62
+ else
63
+ @name = safepath.split('/').last
64
+ @blank = true
65
+ end
66
+ end
67
+ end
68
+
69
+ def gollum_page=(new_gp)
70
+ @gollum_page = new_gp
71
+ @name = new_gp.name
72
+ @path = new_gp.url_path
73
+ @content = new_gp.raw_data
74
+ end
75
+
76
+ def gollum_page
77
+ @gollum_page
78
+ end
79
+
80
+ def new_page?
81
+ @blank && true
82
+ end
83
+
84
+ # may be out of sync with @content!
85
+ def html
86
+ @gollum_page.formatted_data unless @blank
87
+ end
88
+
89
+ def metadata
90
+ @gollum_page.metadata
91
+ end
92
+
93
+ # def to_param
94
+ # @gollum_page.url_path unless @blank
95
+ # end
96
+
97
+ def author
98
+ @gollum_page.versions.first.author.name unless @blank
99
+ end
100
+ def authored_date
101
+ @gollum_page.versions.first.authored_date unless @blank
102
+ end
103
+
104
+ def history_url
105
+ # @@format is :markdown, so let's presume
106
+ @wiki.history_url + @path + '.md'
107
+ end
108
+
109
+ def parents
110
+ parents = []
111
+ ancestors = @path.split('/')
112
+ for i in 1..(ancestors.length - 1) do
113
+ parent_name = ancestors.take(i).join('/')
114
+ parents << @wiki.find_page(parent_name)
115
+ end
116
+ return parents
117
+ end
118
+
119
+ def parent_path
120
+ parent_path = File.dirname(@path)
121
+ (parent_path == '.') ? '' : parent_path # + '/'
122
+ end
123
+
124
+ def children
125
+ @wiki.pages_under_path(@path)
126
+ end
127
+
128
+ def attachments
129
+ return @wiki.files_under_path(@path)
130
+ end
131
+
132
+ def save(user)
133
+ if @blank
134
+ commit = {name: user.name, email: user.email, message: "created #{@path}"}
135
+ Rails.logger.debug("creating #{@path}")
136
+
137
+ @wiki.pull_repo
138
+ @wiki.gollum_wiki.write_page(@name, @@format, @content, commit, self.parent_path)
139
+
140
+ @gollum_page = @wiki.find_gollum_page(@path)
141
+ @blank = false
142
+ else
143
+ commit = {name: user.name, email: user.email, message: "updated #{@path}"}
144
+ Rails.logger.debug("saving #{@path}")
145
+
146
+ @wiki.pull_repo
147
+ @wiki.gollum_wiki.update_page(@gollum_page, @name, @@format, @content, commit)
148
+ end
149
+
150
+ if @uploaded_files.present?
151
+ Rails.logger.debug("found #{@uploaded_files.length} new files for #{@path}")
152
+ head = @wiki.gollum_wiki.repo.head
153
+
154
+ # NOTE: may raise Gollum::DuplicatePageError
155
+ commit_options = {
156
+ message: "added #{@uploaded_files.length} attachments to #{@path}",
157
+ name: user.name,
158
+ email: user.email
159
+ }
160
+ committer = Gollum::Committer.new(@wiki.gollum_wiki, commit_options)
161
+
162
+ self.uploaded_files.each do |uploaded|
163
+ # TODO: maybe this should be creating then saving new files
164
+ fullname = uploaded.original_filename
165
+ ext = File.extname(fullname)
166
+ format = ext.split('.').last
167
+ filename = File.basename(fullname, ext)
168
+ contents = uploaded.read
169
+ fsdir = File.join(@wiki.gollum_wiki.path, @path)
170
+ pathname = File.join(@path, fullname)
171
+
172
+ if !FileTest::directory?(fsdir)
173
+ Dir::mkdir(fsdir)
174
+ end
175
+ fspath = File.join(fsdir, fullname)
176
+ File.open(fspath, 'wb') do |file|
177
+ file.write(contents)
178
+ Rails.logger.debug("uploaded #{format} to #{fspath}")
179
+ end
180
+
181
+ committer.index.add(pathname, contents)
182
+ # committer.add_to_index(@path, filename, format, contents)
183
+ # # committer.add_to_index(@path, filename, format, "file")
184
+ committer.after_commit do |committer, sha|
185
+ @wiki.gollum_wiki.clear_cache
186
+ committer.update_working_dir(@path, filename, format)
187
+ end
188
+ end
189
+ committer.commit
190
+ end
191
+
192
+ @wiki.push_repo
193
+ changes_applied
194
+ end
195
+
196
+ def destroy!(user)
197
+ commit = {name: user.name, email: user.email, message: "removed #{@path}"}
198
+ Rails.logger.debug("removing #{@path}")
199
+ @wiki.gollum_wiki.delete_page(@gollum_page, commit)
200
+ @wiki.push_repo
201
+ end
202
+
203
+ def inspect
204
+ "Wiki::Page '#{@path}' (#{@gollum_page})"
205
+ end
206
+
207
+ def persisted?
208
+ # TODO: make this a real dirty flag
209
+ ! self.new_page?
210
+ end
211
+
212
+ end
213
+ end