ecrire 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/Gemfile +4 -0
  4. data/LICENCE +9 -0
  5. data/README.md +62 -0
  6. data/Rakefile +72 -0
  7. data/bin/ecrire +42 -0
  8. data/ecrire.gemspec +26 -0
  9. data/lib/ecrire/app/assets/javascripts/admin/editor/content.coffee +319 -0
  10. data/lib/ecrire/app/assets/javascripts/admin/editor/ext.coffee +94 -0
  11. data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/clipboard.coffee +98 -0
  12. data/lib/ecrire/app/assets/javascripts/admin/editor/extensions/image.coffee +19 -0
  13. data/lib/ecrire/app/assets/javascripts/admin/editor/navigation/delete.js.coffee +11 -0
  14. data/lib/ecrire/app/assets/javascripts/admin/editor/navigation/draft.js.coffee +42 -0
  15. data/lib/ecrire/app/assets/javascripts/admin/editor/navigation/help.js.coffee +6 -0
  16. data/lib/ecrire/app/assets/javascripts/admin/editor/navigation/save.js.coffee +61 -0
  17. data/lib/ecrire/app/assets/javascripts/admin/editor/navigation/sticky.js.coffee +11 -0
  18. data/lib/ecrire/app/assets/javascripts/admin/editor/navigation/toggle.js.coffee +10 -0
  19. data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/code.coffee +39 -0
  20. data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/heading.coffee +19 -0
  21. data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/image.coffee +129 -0
  22. data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/link.coffee +44 -0
  23. data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/lists.coffee +48 -0
  24. data/lib/ecrire/app/assets/javascripts/admin/editor/parsers/word.coffee +56 -0
  25. data/lib/ecrire/app/assets/javascripts/admin/posts/header.coffee +69 -0
  26. data/lib/ecrire/app/assets/javascripts/admin/posts/new.js.coffee +7 -0
  27. data/lib/ecrire/app/assets/javascripts/admin/posts/title.coffee +15 -0
  28. data/lib/ecrire/app/assets/javascripts/admin.js +5 -0
  29. data/lib/ecrire/app/assets/javascripts/application.js +21 -0
  30. data/lib/ecrire/app/assets/javascripts/shared/overlay.js.coffee +29 -0
  31. data/lib/ecrire/app/assets/javascripts/shared/popup.js.coffee +7 -0
  32. data/lib/ecrire/app/assets/javascripts/vendor/moment.js +3043 -0
  33. data/lib/ecrire/app/assets/javascripts/vendor/prism/prism.ruby.js +25 -0
  34. data/lib/ecrire/app/assets/javascripts/vendor/prism.js +578 -0
  35. data/lib/ecrire/app/assets/stylesheets/admin/base.css.scss +98 -0
  36. data/lib/ecrire/app/assets/stylesheets/admin/documentation.css.scss +28 -0
  37. data/lib/ecrire/app/assets/stylesheets/admin/editor/base.css.scss +41 -0
  38. data/lib/ecrire/app/assets/stylesheets/admin/editor/content.css.scss +57 -0
  39. data/lib/ecrire/app/assets/stylesheets/admin/editor/header.css.scss +62 -0
  40. data/lib/ecrire/app/assets/stylesheets/admin/editor/image.css.scss +58 -0
  41. data/lib/ecrire/app/assets/stylesheets/admin/editor/publish.css.scss +40 -0
  42. data/lib/ecrire/app/assets/stylesheets/admin/editor/save.css.scss +44 -0
  43. data/lib/ecrire/app/assets/stylesheets/admin/images.css.scss +64 -0
  44. data/lib/ecrire/app/assets/stylesheets/admin/loading.css.scss +48 -0
  45. data/lib/ecrire/app/assets/stylesheets/admin/mixins/animations.css.scss +30 -0
  46. data/lib/ecrire/app/assets/stylesheets/admin/mixins/form.css.scss +22 -0
  47. data/lib/ecrire/app/assets/stylesheets/admin/navigation.css.scss +96 -0
  48. data/lib/ecrire/app/assets/stylesheets/admin/posts.css.scss +46 -0
  49. data/lib/ecrire/app/assets/stylesheets/admin/variables.css.scss +7 -0
  50. data/lib/ecrire/app/assets/stylesheets/admin.css.scss +5 -0
  51. data/lib/ecrire/app/assets/stylesheets/application.css.scss +12 -0
  52. data/lib/ecrire/app/assets/stylesheets/sessions.css.scss +97 -0
  53. data/lib/ecrire/app/assets/stylesheets/shared/main.css.scss +5 -0
  54. data/lib/ecrire/app/assets/stylesheets/shared/popup.css.scss +76 -0
  55. data/lib/ecrire/app/assets/stylesheets/vendor/prism/ruby.scss +11 -0
  56. data/lib/ecrire/app/assets/stylesheets/vendor/prism.scss +149 -0
  57. data/lib/ecrire/app/controllers/admin/application_controller.rb +16 -0
  58. data/lib/ecrire/app/controllers/admin/configurations/images_controller.rb +28 -0
  59. data/lib/ecrire/app/controllers/admin/images_controller.rb +35 -0
  60. data/lib/ecrire/app/controllers/admin/labels_controller.rb +18 -0
  61. data/lib/ecrire/app/controllers/admin/partials_controller.rb +43 -0
  62. data/lib/ecrire/app/controllers/admin/posts_controller.rb +90 -0
  63. data/lib/ecrire/app/controllers/admin/properties_controller.rb +35 -0
  64. data/lib/ecrire/app/controllers/application_controller.rb +29 -0
  65. data/lib/ecrire/app/controllers/concerns/.keep +0 -0
  66. data/lib/ecrire/app/controllers/ecrire/posts_controller.rb +42 -0
  67. data/lib/ecrire/app/controllers/images_controller.rb +2 -0
  68. data/lib/ecrire/app/controllers/onboarding/databases_controller.rb +67 -0
  69. data/lib/ecrire/app/controllers/onboarding/users_controller.rb +32 -0
  70. data/lib/ecrire/app/controllers/onboarding_controller.rb +20 -0
  71. data/lib/ecrire/app/controllers/partials_controller.rb +8 -0
  72. data/lib/ecrire/app/controllers/sessions_controller.rb +23 -0
  73. data/lib/ecrire/app/controllers/static_controller.rb +5 -0
  74. data/lib/ecrire/app/forms/admin/image_builder.rb +24 -0
  75. data/lib/ecrire/app/forms/admin/partial_builder.rb +105 -0
  76. data/lib/ecrire/app/forms/admin/post_builder.rb +47 -0
  77. data/lib/ecrire/app/helpers/admin/images_helper.rb +60 -0
  78. data/lib/ecrire/app/helpers/admin/labels_helper.rb +26 -0
  79. data/lib/ecrire/app/helpers/admin/posts_helper.rb +26 -0
  80. data/lib/ecrire/app/helpers/application_helper.rb +64 -0
  81. data/lib/ecrire/app/helpers/content_tag_helper.rb +91 -0
  82. data/lib/ecrire/app/helpers/open_graph_helper.rb +45 -0
  83. data/lib/ecrire/app/helpers/posts_helper.rb +19 -0
  84. data/lib/ecrire/app/models/admin/image.rb +108 -0
  85. data/lib/ecrire/app/models/admin/partial.rb +4 -0
  86. data/lib/ecrire/app/models/admin/post.rb +56 -0
  87. data/lib/ecrire/app/models/concerns/.keep +0 -0
  88. data/lib/ecrire/app/models/image.rb +5 -0
  89. data/lib/ecrire/app/models/label.rb +3 -0
  90. data/lib/ecrire/app/models/partial.rb +3 -0
  91. data/lib/ecrire/app/models/post.rb +90 -0
  92. data/lib/ecrire/app/models/property/image.rb +30 -0
  93. data/lib/ecrire/app/models/property/label.rb +28 -0
  94. data/lib/ecrire/app/models/user.rb +31 -0
  95. data/lib/ecrire/app/strategies/password_strategy.rb +17 -0
  96. data/lib/ecrire/app/views/admin/configurations/images/complete.html.erb +6 -0
  97. data/lib/ecrire/app/views/admin/configurations/images/show.html.erb +14 -0
  98. data/lib/ecrire/app/views/admin/images/_errors.html.erb +14 -0
  99. data/lib/ecrire/app/views/admin/images/_form.html.erb +15 -0
  100. data/lib/ecrire/app/views/admin/images/destroy.js.erb +0 -0
  101. data/lib/ecrire/app/views/admin/images/errors.js.erb +1 -0
  102. data/lib/ecrire/app/views/admin/images/update.js.erb +1 -0
  103. data/lib/ecrire/app/views/admin/posts/_documentation.html.erb +61 -0
  104. data/lib/ecrire/app/views/admin/posts/_list.html.erb +18 -0
  105. data/lib/ecrire/app/views/admin/posts/_post.html.erb +3 -0
  106. data/lib/ecrire/app/views/admin/posts/edit.html.erb +73 -0
  107. data/lib/ecrire/app/views/admin/posts/help.js.erb +1 -0
  108. data/lib/ecrire/app/views/admin/posts/index.js.erb +1 -0
  109. data/lib/ecrire/app/views/admin/posts/title.html.erb +10 -0
  110. data/lib/ecrire/app/views/admin/posts/update/content.js.erb +1 -0
  111. data/lib/ecrire/app/views/admin/posts/update/title.js.erb +1 -0
  112. data/lib/ecrire/app/views/admin/properties/image/create.js.erb +14 -0
  113. data/lib/ecrire/app/views/admin/properties/image/destroy.js.erb +6 -0
  114. data/lib/ecrire/app/views/admin/properties/label/create.js.erb +8 -0
  115. data/lib/ecrire/app/views/admin/properties/label/destroy.js.erb +2 -0
  116. data/lib/ecrire/app/views/layouts/admin/application.html.erb +47 -0
  117. data/lib/ecrire/app/views/partials/show.html.erb +14 -0
  118. data/lib/ecrire/app/views/sessions/_form.html.erb +18 -0
  119. data/lib/ecrire/app/views/sessions/_navigation.html.erb +24 -0
  120. data/lib/ecrire/app/views/sessions/new.html.erb +1 -0
  121. data/lib/ecrire/application.rb +18 -0
  122. data/lib/ecrire/commands/base.rb +9 -0
  123. data/lib/ecrire/commands/console.rb +21 -0
  124. data/lib/ecrire/commands/new.rb +47 -0
  125. data/lib/ecrire/commands/server.rb +27 -0
  126. data/lib/ecrire/commands.rb +19 -0
  127. data/lib/ecrire/config/environment/development.rb +12 -0
  128. data/lib/ecrire/config/environment/production.rb +12 -0
  129. data/lib/ecrire/config/environment/test.rb +3 -0
  130. data/lib/ecrire/config/environment.rb +46 -0
  131. data/lib/ecrire/config/onboarding_routes.rb +11 -0
  132. data/lib/ecrire/config/routes.rb +32 -0
  133. data/lib/ecrire/db/migrate/20131111222709_create_posts.rb +16 -0
  134. data/lib/ecrire/db/migrate/20131113200949_create_users.rb +12 -0
  135. data/lib/ecrire/db/migrate/20131114205041_add_excerpt_to_post.rb +5 -0
  136. data/lib/ecrire/db/migrate/20131221181640_add_javascript_to_posts.rb +5 -0
  137. data/lib/ecrire/db/migrate/20131221195338_create_partials.rb +12 -0
  138. data/lib/ecrire/db/migrate/20140115215122_create_images.rb +12 -0
  139. data/lib/ecrire/db/migrate/20140209144412_add_favorite_flag_to_image.rb +5 -0
  140. data/lib/ecrire/db/migrate/20140617224837_create_labels.rb +12 -0
  141. data/lib/ecrire/db/migrate/20140618321984_add_properties_to_posts.rb +6 -0
  142. data/lib/ecrire/db/migrate/20150113839713_convert_markdown_in_post_content.rb +6 -0
  143. data/lib/ecrire/db/migrate/20150120093481_add_preview_to_posts.rb +8 -0
  144. data/lib/ecrire/db/schema.rb +76 -0
  145. data/lib/ecrire/locales/admin/configurations/images/en.yml +3 -0
  146. data/lib/ecrire/locales/admin/en.yml +26 -0
  147. data/lib/ecrire/locales/admin/images/en.yml +5 -0
  148. data/lib/ecrire/locales/admin/partials/en.yml +18 -0
  149. data/lib/ecrire/locales/admin/posts/en.yml +14 -0
  150. data/lib/ecrire/locales/en.yml +9 -0
  151. data/lib/ecrire/onboarding/assets/images/.keep +0 -0
  152. data/lib/ecrire/onboarding/assets/images/background.png +0 -0
  153. data/lib/ecrire/onboarding/assets/images/github-logo.png +0 -0
  154. data/lib/ecrire/onboarding/assets/images/profile.png +0 -0
  155. data/lib/ecrire/onboarding/assets/images/twitter-logo.png +0 -0
  156. data/lib/ecrire/onboarding/assets/javascripts/base.js +2 -0
  157. data/lib/ecrire/onboarding/assets/javascripts/databases/information.js.coffee +17 -0
  158. data/lib/ecrire/onboarding/assets/javascripts/message.js.coffee +5 -0
  159. data/lib/ecrire/onboarding/assets/stylesheets/base.css.scss +80 -0
  160. data/lib/ecrire/onboarding/assets/stylesheets/complete.css.scss +3 -0
  161. data/lib/ecrire/onboarding/assets/stylesheets/fonts.css.scss +17 -0
  162. data/lib/ecrire/onboarding/assets/stylesheets/form.css.scss +25 -0
  163. data/lib/ecrire/onboarding/assets/stylesheets/message.css.scss +11 -0
  164. data/lib/ecrire/onboarding/assets/stylesheets/terminal.css.scss +16 -0
  165. data/lib/ecrire/onboarding/assets/stylesheets/variables.css.scss +3 -0
  166. data/lib/ecrire/onboarding/assets/stylesheets/welcome.css.scss +62 -0
  167. data/lib/ecrire/onboarding/views/layouts/application.html.erb +16 -0
  168. data/lib/ecrire/onboarding/views/onboarding/complete.html.erb +6 -0
  169. data/lib/ecrire/onboarding/views/onboarding/databases/index.html.erb +34 -0
  170. data/lib/ecrire/onboarding/views/onboarding/users/index.html.erb +16 -0
  171. data/lib/ecrire/onboarding/views/onboarding/welcome.html.erb +12 -0
  172. data/lib/ecrire/railtie/onboarding.rb +63 -0
  173. data/lib/ecrire/railtie/theme.rb +69 -0
  174. data/lib/ecrire/railtie.rb +30 -0
  175. data/lib/ecrire/tasks/assets.rake +15 -0
  176. data/lib/ecrire/tasks/routes.rake +6 -0
  177. data/lib/ecrire/template/Gemfile +31 -0
  178. data/lib/ecrire/template/Rakefile +4 -0
  179. data/lib/ecrire/template/assets/images/.keep +0 -0
  180. data/lib/ecrire/template/assets/javascripts/base.js.coffee +0 -0
  181. data/lib/ecrire/template/assets/javascripts/post.js.coffee +0 -0
  182. data/lib/ecrire/template/assets/stylesheets/base.css.scss +86 -0
  183. data/lib/ecrire/template/assets/stylesheets/code.css.scss +24 -0
  184. data/lib/ecrire/template/assets/stylesheets/footer.css.scss +5 -0
  185. data/lib/ecrire/template/assets/stylesheets/header.css.scss +18 -0
  186. data/lib/ecrire/template/assets/stylesheets/home.css.scss +35 -0
  187. data/lib/ecrire/template/config.ru +4 -0
  188. data/lib/ecrire/template/controllers/posts_controller.rb +2 -0
  189. data/lib/ecrire/template/helpers/blog_helper.rb +2 -0
  190. data/lib/ecrire/template/views/layouts/application.html.erb +35 -0
  191. data/lib/ecrire/template/views/posts/_post.html.erb +9 -0
  192. data/lib/ecrire/template/views/posts/index.html.erb +13 -0
  193. data/lib/ecrire/template/views/posts/show.html.erb +7 -0
  194. data/lib/ecrire/template/views/static/about.html +0 -0
  195. data/lib/ecrire/template/views/static/customize/_helper.html +7 -0
  196. data/lib/ecrire/template/views/static/customize/_helper_used.html +6 -0
  197. data/lib/ecrire/template/views/static/customize/_image.html +1 -0
  198. data/lib/ecrire/template/views/static/customize/_index.html +1 -0
  199. data/lib/ecrire/template/views/static/customize/_javascript.html +1 -0
  200. data/lib/ecrire/template/views/static/customize/_layout.html.erb +15 -0
  201. data/lib/ecrire/template/views/static/customize/_post.html +3 -0
  202. data/lib/ecrire/template/views/static/customize.html +95 -0
  203. data/lib/ecrire/version.rb +3 -0
  204. data/lib/ecrire.rb +24 -0
  205. data/test/editor/controllers/admin/images_controller_test.rb +6 -0
  206. data/test/editor/controllers/admin/posts_controller_test.rb +90 -0
  207. data/test/editor/controllers/base_controller_test.rb +26 -0
  208. data/test/editor/controllers/images_controller_test.rb +2 -0
  209. data/test/editor/controllers/partials_controller_test.rb +2 -0
  210. data/test/editor/controllers/posts_controller_test.rb +21 -0
  211. data/test/editor/controllers/sessions_controller_test.rb +21 -0
  212. data/test/editor/helpers/base_helper_test.rb +19 -0
  213. data/test/editor/helpers/posts_helper_test.rb +3 -0
  214. data/test/editor/models/image_test.rb +33 -0
  215. data/test/editor/models/partial_test.rb +4 -0
  216. data/test/editor/models/post_test.rb +109 -0
  217. data/test/editor/models/user_test.rb +4 -0
  218. data/test/editor/test_helper.rb +28 -0
  219. data/test/fixtures/images.yml +11 -0
  220. data/test/fixtures/partials.yml +9 -0
  221. data/test/fixtures/posts.yml +16 -0
  222. data/test/fixtures/users.yml +9 -0
  223. data/test/initializations/railtie_test.rb +49 -0
  224. data/test/isolation.rb +16 -0
  225. data/test/themes/template/Gemfile +28 -0
  226. data/test/themes/template/assets/javascripts/blog.js.coffee +0 -0
  227. data/test/themes/template/assets/javascripts/post.js.coffee +0 -0
  228. data/test/themes/template/assets/stylesheets/blog.css.scss +0 -0
  229. data/test/themes/template/assets/stylesheets/post.css.scss +0 -0
  230. data/test/themes/template/config.ru +0 -0
  231. data/test/themes/template/views/layouts/application.html.erb +1 -0
  232. data/test/themes/template/views/posts/index.html.erb +0 -0
  233. metadata +275 -0
@@ -0,0 +1,108 @@
1
+ require 's3'
2
+
3
+ module Admin
4
+ class Image < ::Image
5
+ belongs_to :post, class_name: Admin::Post
6
+ before_save :update_file
7
+
8
+ attr_reader :s3
9
+
10
+ def s3
11
+ @s3 ||= S3.new(Rails.application.secrets.s3 || {})
12
+ end
13
+
14
+ def file=(file)
15
+ @file = s3.bucket.objects.build path(file)
16
+ @file.content = file
17
+ end
18
+
19
+ def clear!
20
+ return if self.url.nil?
21
+
22
+ s3.bucket.objects.find(key).destroy
23
+ self.url = nil
24
+ self.key = nil
25
+ save!
26
+ end
27
+
28
+ protected
29
+
30
+ def update_file
31
+ unless @file.nil?
32
+ begin
33
+ @file.save
34
+ self.url = @file.url
35
+ self.key = @file.key
36
+ rescue StandardError => e
37
+ errors.add :remote, e
38
+ return false
39
+ end
40
+ return true
41
+ end
42
+ end
43
+
44
+ def remove_file
45
+ end
46
+
47
+ def path(file)
48
+ items = [post.id, file.original_filename]
49
+
50
+ items.prepend(s3.path) unless s3.path.blank?
51
+
52
+ items.join("/")
53
+ end
54
+
55
+ class S3
56
+ include ::S3
57
+
58
+ attr_reader :bucket, :access_key, :secret_key, :path, :errors
59
+
60
+ def initialize(options={})
61
+ @errors = ActiveModel::Errors.new(self)
62
+ @access_key = options.fetch('access_key', "")
63
+ @secret_key = options.fetch('secret_key', "")
64
+ @path = options.fetch('path', "")
65
+
66
+ @bucket = service.bucket(options.fetch('bucket', 'ecrire'))
67
+ end
68
+
69
+ def configuration_hash
70
+ config = {
71
+ 'access_key' => access_key,
72
+ 'secret_key' => secret_key,
73
+ 'bucket' => bucket.name
74
+ }
75
+
76
+ unless path.blank?
77
+ config['path'] = path
78
+ end
79
+
80
+ config
81
+ end
82
+
83
+ def connect
84
+ begin
85
+ @bucket.retrieve
86
+ @connected = true
87
+ rescue Error::ResponseError, ArgumentError, SocketError => e
88
+ errors.add :remote, e
89
+ end
90
+ end
91
+
92
+ def service
93
+ @service ||= Service.new(access_key_id: access_key,
94
+ secret_access_key: secret_key,
95
+ use_ssl: true
96
+ )
97
+ end
98
+
99
+ def connected?
100
+ if @connected.nil?
101
+ connect
102
+ end
103
+ !!@connected
104
+ end
105
+
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,4 @@
1
+ module Admin
2
+ class Partial < ::Partial
3
+ end
4
+ end
@@ -0,0 +1,56 @@
1
+ require 'kramdown'
2
+
3
+ module Admin
4
+ class Post < ::Post
5
+
6
+ class Callbacks
7
+ def after_create(record)
8
+ Admin::Image.create(post: record)
9
+ end
10
+ end
11
+
12
+ has_one :header, class_name: Admin::Image
13
+ before_save :compile!, prepend: true
14
+ before_save :excerptize!
15
+ after_create Callbacks.new
16
+
17
+ def publish!(params = {})
18
+ self.assign_attributes(params)
19
+ self.published_at = DateTime.now
20
+ self.save!
21
+ end
22
+
23
+ def unpublish!(params = {})
24
+ self.assign_attributes(params)
25
+ self.published_at = nil
26
+ self.save!
27
+ end
28
+
29
+ def stylesheet
30
+ super || ""
31
+ end
32
+
33
+ def javascript
34
+ super || ""
35
+ end
36
+
37
+ def content
38
+ read_attribute(:content) || ""
39
+ end
40
+
41
+ private
42
+
43
+ def compile!
44
+ self.compiled_content = Kramdown::Document.new(self.content).to_html
45
+ end
46
+
47
+ def excerptize!
48
+ html = Nokogiri::HTML(self.compiled_content)
49
+ html.xpath("//img").each do |img|
50
+ img.remove
51
+ end
52
+ self.compiled_excerpt = html.xpath('//body').children[0..20].to_s
53
+ end
54
+
55
+ end
56
+ end
File without changes
@@ -0,0 +1,5 @@
1
+ class Image < ActiveRecord::Base
2
+ belongs_to :post
3
+
4
+ scope :favorites, lambda { where('images.favorite is true') }
5
+ end
@@ -0,0 +1,3 @@
1
+ class Label < ActiveRecord::Base
2
+ validates_presence_of :name, unique: true
3
+ end
@@ -0,0 +1,3 @@
1
+ class Partial < ActiveRecord::Base
2
+ validates_presence_of :title
3
+ end
@@ -0,0 +1,90 @@
1
+ require 'nokogiri'
2
+
3
+ class Post < ActiveRecord::Base
4
+ has_one :header, class_name: Image
5
+
6
+ store_accessor :properties, :labels
7
+
8
+ scope :status, lambda {|status|
9
+ if status.eql?("published")
10
+ where "posts.published_at IS NOT NULL"
11
+ else
12
+ where "posts.published_at IS NULL"
13
+ end
14
+ }
15
+
16
+ scope :published, lambda { status("published") }
17
+ scope :drafted, lambda { status("drafted") }
18
+ scope :slug, lambda { |slug| where("posts.slug is ?", slug) }
19
+ scope :without, lambda { |post| where("posts.id != ?", post.id) }
20
+
21
+ validates :title, presence: true, uniqueness: true
22
+ validates :slug, presence: true, uniqueness: true
23
+
24
+ before_validation :create_slug_if_nil
25
+
26
+ def status=(new_status)
27
+ if new_status.eql? "publish"
28
+ publish!
29
+ end
30
+ end
31
+
32
+ def publish!
33
+ return unless published_at.nil?
34
+ self.published_at = DateTime.now
35
+ save!
36
+ end
37
+
38
+ def published?
39
+ !draft?
40
+ end
41
+
42
+ def draft?
43
+ published_at.nil?
44
+ end
45
+
46
+ def to_param
47
+ if self.instance_of?(Admin::Post)
48
+ id.to_s
49
+ else
50
+ slug
51
+ end
52
+ end
53
+
54
+ def content
55
+ (self.compiled_content || super || '').html_safe
56
+ end
57
+
58
+ def excerpt
59
+ (self.compiled_excerpt || "").html_safe
60
+ end
61
+
62
+
63
+ def title=(new_title)
64
+ super
65
+ if self.draft?
66
+ self.slug = nil
67
+ end
68
+ end
69
+
70
+ def header?
71
+ !self.header.nil? && !self.header.url.blank?
72
+ end
73
+
74
+ def labels
75
+ ids = super || ''
76
+ Label.where(id: ids.split(',')).to_a
77
+ end
78
+
79
+ def labels=(labels)
80
+ super(labels.map(&:id).join(','))
81
+ end
82
+
83
+ protected
84
+
85
+ def create_slug_if_nil
86
+ return unless self.slug.blank?
87
+ self.slug = self.title.parameterize
88
+ end
89
+
90
+ end
@@ -0,0 +1,30 @@
1
+ module Property
2
+ class Image
3
+ attr_accessor :post
4
+
5
+ def name
6
+ "image"
7
+ end
8
+
9
+ def create(params)
10
+ unless post.header.nil?
11
+ post.header.destroy
12
+ end
13
+ img = post.images.build
14
+ img.file = params[:admin_image][:file]
15
+ img.save
16
+ post.header = img
17
+ post.save
18
+ end
19
+
20
+ # TODO:
21
+ # 3. Remove id in post properties
22
+ def destroy(value)
23
+ return if post.header.nil?
24
+ img = post.header
25
+ img.destroy
26
+ end
27
+
28
+ end
29
+ end
30
+
@@ -0,0 +1,28 @@
1
+ module Property
2
+ class Label
3
+ attr_accessor :post
4
+
5
+ def name
6
+ "label"
7
+ end
8
+
9
+ def create(params)
10
+ new_label = ::Label.find_or_create_by!(name: params[:value])
11
+ labels = post.labels
12
+ labels << new_label
13
+ post.labels = labels
14
+ post.save!
15
+ new_label
16
+ end
17
+
18
+ def destroy(params)
19
+ label = ::Label.find_by!(name: params[:value])
20
+ labels = post.labels
21
+ labels.delete(label)
22
+ post.labels = labels
23
+ post.save!
24
+ label
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ require 'bcrypt'
2
+
3
+ class User < ActiveRecord::Base
4
+ include BCrypt
5
+
6
+ attr_accessor :password_confirmation
7
+
8
+ validates_presence_of :email
9
+
10
+ validate do |record|
11
+ record.errors.add(:password, :blank) unless record.encrypted_password.present?
12
+ end
13
+
14
+ validates_confirmation_of :password, if: ->{ password.present? }
15
+
16
+ def password
17
+ return if self.encrypted_password.blank?
18
+ @password ||= Password.new(self.encrypted_password)
19
+ end
20
+
21
+ def password=(new_password)
22
+ if new_password.nil?
23
+ self.encrypted_password = nil
24
+ elsif new_password.present?
25
+ @password = Password.create(new_password)
26
+ self.encrypted_password = @password
27
+ end
28
+ end
29
+
30
+
31
+ end
@@ -0,0 +1,17 @@
1
+ class PasswordStrategy < Warden::Strategies::Base
2
+ def valid?
3
+ return false if request.get?
4
+ user_data = params.fetch("session", {})
5
+ user_data.has_key?("email") && user_data.has_key?("password")
6
+ end
7
+
8
+ def authenticate!
9
+ user = User.find_by_email(params["session"].fetch("email"))
10
+ if !user.nil? && user.password == params["session"].fetch("password")
11
+ success! user
12
+ else
13
+ env['warden'].errors.add :general, "Sorry, couldn't log you in."
14
+ fail!
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,6 @@
1
+ <section class="complete">
2
+ <h1>Your S3 bucket is configure and ready to go!</h1>
3
+
4
+ <p>You can now use images in your posts.</p>
5
+
6
+ </section>
@@ -0,0 +1,14 @@
1
+ <section class="configure">
2
+ <%= images_config_title %>
3
+
4
+ <%= form_for :s3 do |f| %>
5
+
6
+ <%= f.text_field :access_key, value: @s3.access_key, placeholder: "Access key" %>
7
+ <%= f.text_field :secret_key, value: @s3.secret_key, placeholder: "Secret key" %>
8
+ <%= f.text_field :bucket, value: @s3.bucket.name, placeholder: "Bucket name" %>
9
+ <%= f.text_field :path, value: @s3.path, placeholder: "Root path (Optional)" %>
10
+
11
+ <%= f.button "Connect & Save" %>
12
+
13
+ <% end %>
14
+ </section>
@@ -0,0 +1,14 @@
1
+ <div class='overlay' as='Overlay'>
2
+ <div class='popup errors' id='Failure'>
3
+ <header>
4
+ <h5>Oops, something wrong just happened</h5>
5
+ </header>
6
+
7
+ <ol class='errors'>
8
+ <% errors.each do |attr, message| %>
9
+ <%= content_tag :li, message.to_s.capitalize %>
10
+ <% end %>
11
+ </ol>
12
+
13
+ </div>
14
+ </div>
@@ -0,0 +1,15 @@
1
+ <%= form_for post.header, url: admin_post_image_path(post), as: :image, html: {class: %w(update)} do |f| %>
2
+
3
+ <div class='drop zone'>
4
+ </div>
5
+
6
+ <p>You can drop an image here to set a header image for this post.</p>
7
+
8
+ <%= f.file_field :file %>
9
+
10
+ <% end %>
11
+
12
+ <%= form_for post.header, url: admin_post_image_path(post), remote: true, method: :delete, html: {class: %w(clear)} do |f| %>
13
+ Do you want to remove this header? <%= f.button 'Remove' %>
14
+ <% end %>
15
+
File without changes
@@ -0,0 +1 @@
1
+ e.ErrorHTML = "<%= j render('errors', errors: @image.errors ) %>".toHTML()
@@ -0,0 +1 @@
1
+ e.headerURL = "<%= j @image.url %>"
@@ -0,0 +1,61 @@
1
+ <div class='overlay' as='Overlay'>
2
+ <div class='popup' id="Documentation">
3
+ <header>
4
+ <h5>Available syntax when writing your post</h5>
5
+ </header>
6
+
7
+ <section class='words'>
8
+ <h3>Headers</h3>
9
+ <div>
10
+ <h1># Header 1</h1>
11
+ <h2># Header 2</h2>
12
+ <h3># Header 3</h3>
13
+ <h4># Header 4</h4>
14
+ <h5># Header 5</h5>
15
+ </div>
16
+ </section>
17
+
18
+ <section class='words'>
19
+ <h3>Italic & bold</h3>
20
+ <p>You <em>*can*</em> use asterisks(*) to style <strong>**words**</strong>.</p>
21
+
22
+ </section>
23
+
24
+ <section class='code'>
25
+ <h3>Code block</h3>
26
+ <pre><code>~~~ language
27
+ [1,2,3].each do |i|
28
+ puts i
29
+ end
30
+ ~~~</code></pre>
31
+ </section>
32
+
33
+ <section class='image'>
34
+ <h3>Images</h3>
35
+ <p>Images are block level in Ecrire. You can only add an image if it's at the start of a line.</p>
36
+ <p><strong>You can only use images if you have configured Ecrire to upload to your S3 bucket.</strong></p>
37
+ <picture>
38
+ <div style="background-image: url(https://www.google.com/images/srpr/logo11w.png);"></div>
39
+ <em name="An image">![An image](https://www.google.com/images/srpr/logo11w.png)</em>
40
+ </picture>
41
+ </section>
42
+
43
+ <section class='list'>
44
+ <h3>Ordered list</h3>
45
+ <ol>
46
+ <li>1. First</li>
47
+ <li>1. Second</li>
48
+ <li>1. Third</li>
49
+ </ol>
50
+
51
+ <h3>Unordered list</h3>
52
+ <ul>
53
+ <li>- First</li>
54
+ <li>- Second</li>
55
+ <li>- Third</li>
56
+ </ul>
57
+ </section>
58
+ </div>
59
+ </div>
60
+
61
+
@@ -0,0 +1,18 @@
1
+ <div class='overlay' as='Overlay'>
2
+ <div class='popup' id='PostsListPopup'>
3
+ <header>
4
+ <%= content_tag :h5, params[:type].capitalize %>
5
+ </header>
6
+ <% if posts.any? %>
7
+ <ol>
8
+ <%= render posts %>
9
+ </ol>
10
+ <% else %>
11
+ <div class='empty'>
12
+ <p>You don't have any <%= params[:type] %> post.</p>
13
+
14
+ <p><%= link_to 'Do you want to create a new post?', new_title_admin_posts_path, class: %w(new button) %></p>
15
+ </div>
16
+ <% end %>
17
+ </div>
18
+ </div>
@@ -0,0 +1,3 @@
1
+ <li class="post item">
2
+ <%= link_to post.title, edit_admin_post_path(post.id) %>
3
+ </li>
@@ -0,0 +1,73 @@
1
+ <header>
2
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" class='toggle', as='Navigation.Toggle' x="0px" y="0px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
3
+ <path d="M4.516,7.548c0.436-0.446,1.043-0.481,1.576,0L10,11.295l3.908-3.747c0.533-0.481,1.141-0.446,1.574,0 c0.436,0.445,0.408,1.197,0,1.615c-0.406,0.418-4.695,4.502-4.695,4.502C10.57,13.888,10.285,14,10,14s-0.57-0.112-0.789-0.335 c0,0-4.287-4.084-4.695-4.502C4.107,8.745,4.08,7.993,4.516,7.548z"/>
4
+ </svg>
5
+
6
+ <%= form_for @post,
7
+ as: 'post',
8
+ remote: true,
9
+ html: {as: 'Posts.Title', class: %w(post title settings autosave)} do |f| %>
10
+ <%= hidden_field_tag :context, :title %>
11
+
12
+ <%= f.text_field :title, class: %w(title), autocomplete: :off %>
13
+ <% end %>
14
+ </header>
15
+
16
+ <section as='Post.Documentation'>
17
+
18
+ <%= content_tag :div, class: %w(publish state), href: admin_post_path(@post.id), as: 'Post.Sticky' do %>
19
+
20
+ <% if @post.published? %>
21
+
22
+ <%= form_for @post, method: :put, html: { class: %w(hide button destroy) } do |f| %>
23
+ <%= f.button 'Retract', value: 'unpublish'%>
24
+ <% end %>
25
+
26
+ <% else %>
27
+
28
+ <%= form_for @post, method: :put, html: { id: 'PublishPost', class: %w(button publish) } do |f| %>
29
+ <%= f.button 'Publish', value: 'publish'%>
30
+ <% end %>
31
+
32
+ <%= form_for @post, method: :delete, html: { id: 'DeletePost', class: %w(destroy button), as: 'Post.Delete' } do |f| %>
33
+ <%= f.button disabled: true do %>
34
+ <%= check_box_tag 'delete' %>
35
+ Delete this post
36
+ <% end %>
37
+ <% end %>
38
+
39
+ <% end %>
40
+
41
+ <% end %>
42
+
43
+ <article>
44
+ <%= content_tag :header, as: "Post.Header" do |header| %>
45
+ <% if @post.header? %>
46
+ <% header[:style] = "background-image: url(#{@post.header.url});" %>
47
+ <% end %>
48
+
49
+ <%= render 'admin/images/form', post: @post %>
50
+ <% end %>
51
+
52
+ <%= post_edit_content(@post) %>
53
+ </article>
54
+
55
+ <div class='save' as='Post.Sticky'>
56
+ <%= form_for @post, method: :put, remote: true, html: {as: 'Editor.Save', class: %w(save button)} do |f| %>
57
+ <%= f.button '', dirty: 'Update', persisted: 'Up to date', disabled: true %>
58
+ <%= content_tag :div, class: %w(update) do %>
59
+ Last updated
60
+ <%= content_tag :p, nil, time: @post.updated_at.iso8601 %>
61
+ <% end %>
62
+ <% end %>
63
+
64
+ <%= link_to 'Syntax', help_admin_posts_path, remote: true, class: %w(help) %>
65
+
66
+ <% unless Rails.application.secrets.has_key?(:s3) %>
67
+ <div class='s3 configuration missing'>
68
+ <%= link_to 'Configure S3 to upload images.', admin_configurations_images_path %>
69
+ </div>
70
+ <% end %>
71
+ </div>
72
+
73
+ </section>
@@ -0,0 +1 @@
1
+ e.HTML = "<%= j render('documentation') %>".toHTML()
@@ -0,0 +1 @@
1
+ e.HTML = "<%= j render('list', posts: @posts) %>".toHTML()
@@ -0,0 +1,10 @@
1
+ <%= form_for Admin::Post.new, as: 'post', html: { class: %w(post new title), as: 'Posts.New' } do |f| %>
2
+
3
+ <fieldset class="transparent">
4
+ <%= f.text_field :title, placeholder: t('admin.posts.new.title') %>
5
+ </fieldset>
6
+ <%= content_tag :p, t('admin.posts.new.editable'), class: %w(transparent) %>
7
+
8
+ <%= f.button :submit, class: %w(hidden) %>
9
+
10
+ <% end %>
@@ -0,0 +1 @@
1
+ e.UpdatedAtTime = "<%= j @post.updated_at.iso8601 %>"
@@ -0,0 +1 @@
1
+ e.HTML = "<%= j @post.title%>"
@@ -0,0 +1,14 @@
1
+ $header = $('aside.preview header')
2
+ $old_form = $header.find('form.image.header')
3
+ $form = $('<%= j render('admin/images/header', post: post) %>')
4
+ $old_form.replaceWith($form)
5
+ $image = $('aside.preview > div.image')
6
+
7
+ if ($image.length) {
8
+ $image.attr('style', "background-image: url('<%= post.header.url %>')")
9
+ }
10
+ else {
11
+ $('aside.preview').prepend("<%= j editor_image_tag(post) %>")
12
+ }
13
+
14
+ $header.find('p.handler').click()
@@ -0,0 +1,6 @@
1
+ $header = $('aside.preview header')
2
+ $old_form = $header.find('form.image.header')
3
+ $form = $('<%= j render('admin/images/header', post: post) %>')
4
+ $old_form.replaceWith($form)
5
+
6
+ $('aside.preview').children('div.image').remove()
@@ -0,0 +1,8 @@
1
+ $placeholder = $('#label-<%= @property.id %>')
2
+ $button = $('<%= j destroy_label_button(@property, post) %>')
3
+ if ($placeholder.length > 0) {
4
+ $placeholder.replaceWith($button)
5
+ }
6
+ else {
7
+ $('form.labels.new').siblings('.existing').append($button)
8
+ }
@@ -0,0 +1,2 @@
1
+ $('#label-<%= @property.id %>').replaceWith('<%= j create_label_button(@property, post) %>')
2
+