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,67 @@
1
+ require_relative '../onboarding_controller'
2
+
3
+ module Onboarding
4
+ class DatabasesController < OnboardingController
5
+
6
+ helper_method :user, :password, :database
7
+
8
+ def index; end;
9
+
10
+ def create
11
+ info ||= {
12
+ 'adapter' => 'postgresql',
13
+ 'database' => database,
14
+ 'user' => user,
15
+ 'password' => password,
16
+ 'encoding' => 'utf8'
17
+ }
18
+ begin
19
+ ActiveRecord::Base.configurations = {
20
+ 'development' => info,
21
+ 'production' => info
22
+ }
23
+ ActiveRecord::Base.establish_connection
24
+ ActiveRecord::Base.connection
25
+ migrate!
26
+ rescue Exception => e
27
+ @exception = e
28
+ ActiveRecord::Base.clear_all_connections!
29
+ render 'index' and return
30
+ end
31
+
32
+ redirect_to :onboarding_users
33
+ end
34
+
35
+ protected
36
+
37
+ private
38
+
39
+ def migrate!
40
+ ActiveRecord::Tasks::DatabaseTasks.database_configuration = ActiveRecord::Base.configurations
41
+ ActiveRecord::Migrator.migrations_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
42
+ ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
43
+ ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
44
+ ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
45
+ end
46
+ end
47
+
48
+ def db_params
49
+ params.fetch(:database, {})
50
+ end
51
+
52
+ def user
53
+ @user ||= "ecrire#{SecureRandom.hex(2)}"
54
+ db_params.fetch(:user, @user)
55
+ end
56
+
57
+ def password
58
+ @password ||= SecureRandom.hex(16)
59
+ db_params.fetch(:password, @password)
60
+ end
61
+
62
+ def database
63
+ @database ||= "ecrire"
64
+ db_params.fetch(:name, @database)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,32 @@
1
+ module Onboarding
2
+ class UsersController < OnboardingController
3
+
4
+ helper_method :user
5
+
6
+ def index;end;
7
+
8
+ def create
9
+ @user = User.find_or_initialize_by(email: user_params[:email])
10
+ @user.update(user_params)
11
+
12
+ if user.errors.blank?
13
+ save_configurations!
14
+ redirect_to :root
15
+ else
16
+ render 'index'
17
+ end
18
+ end
19
+
20
+ protected
21
+
22
+
23
+ def user_params
24
+ params.require(:user).permit(:email, :password, :password_confirmation)
25
+ end
26
+
27
+ def user
28
+ @user ||= User.new
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ class OnboardingController < ApplicationController
2
+
3
+ def index
4
+ if File.exist?(Rails.application.paths['config/secrets'].expanded.last)
5
+ render 'complete' and return
6
+ end
7
+ render 'welcome'
8
+ end
9
+
10
+ protected
11
+
12
+ def save_configurations!
13
+ File.open(Rails.application.paths['config/secrets'].expanded.last, 'w') do |file|
14
+ config = ActiveRecord::Base.configurations
15
+ config['development']['secret_key_base'] = config['production']['secret_key'] = Rails.application.secrets.secret_key_base
16
+ file.write(config.to_yaml)
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,8 @@
1
+ class PartialsController < ApplicationController
2
+ def show
3
+ @partial = Partial.find(params[:id].to_i)
4
+ respond_to do |format|
5
+ format.html { render layout: false }
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,23 @@
1
+ class SessionsController < ApplicationController
2
+ before_action :authenticate!, except: [:failed, :new]
3
+
4
+ def create
5
+ redirect_to :root
6
+ end
7
+
8
+ def destroy
9
+ warden.logout
10
+ redirect_to :back
11
+ end
12
+
13
+ def failed
14
+ respond_to do |format|
15
+ format.html do
16
+ render 'new'
17
+ end
18
+ format.js do
19
+ render 'show' and return
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ class StaticController < ApplicationController
2
+ def show
3
+ render params[:view]
4
+ end
5
+ end
@@ -0,0 +1,24 @@
1
+ module Admin
2
+ class ImageBuilder < ActionView::Helpers::FormBuilder
3
+
4
+ def message
5
+ if object.header?
6
+ content_tag :p, "You can replace your header by either <strong>clicking</strong> or <strong>dropping</strong> a picture here.".html_safe
7
+ else
8
+ content_tag :p, "Upload a picture for your post' header by either <strong>clicking</strong> or <strong>dropping</strong> a picture here".html_safe
9
+ end
10
+ end
11
+
12
+ protected
13
+
14
+ def t(*args)
15
+ args.push({}) unless args.last.is_a?(Hash)
16
+ args.last[:scope] = %w(admin form image)
17
+ I18n.t *args
18
+ end
19
+
20
+ def method_missing(method, *args, &block)
21
+ @template.send(method, *args, &block)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,105 @@
1
+ module Admin
2
+ class PartialBuilder < ActionView::Helpers::FormBuilder
3
+
4
+ def errors
5
+ return unless object.errors.any?
6
+ content_tag :div, class: %w(container errors) do
7
+ [
8
+ content_tag(:span, h(object.errors.full_messages.to_sentence)),
9
+ link_to("x", "javascript:void(0)", class: %w(dismiss button))
10
+ ].join.html_safe
11
+ end
12
+ end
13
+
14
+ def title
15
+ content_tag :div, id: "partial_title_wrapper", class: %w(title wrapper) do
16
+ [
17
+ text_field(:title, placeholder: t('.title'), class: %w(input)),
18
+ possible_actions
19
+ ].join.html_safe
20
+ end
21
+ end
22
+
23
+ def editor
24
+ [
25
+ editor_options,
26
+ content_tag(:div, class: %w(main editor)) do
27
+ [
28
+ editor_content,
29
+ content_tag(:div, preview, id: "editorSideContent")
30
+ ].join.html_safe
31
+ end
32
+ ].join.html_safe
33
+ end
34
+
35
+ protected
36
+
37
+ def preview
38
+ content_tag(:div, id: 'contentPreviewContainer') do
39
+ [
40
+ content_tag(:style),
41
+ content_tag(:script, nil, class: %w(preview), type: 'text/javascript'),
42
+ content_tag(:article, nil, class: %w(content preview)),
43
+ ].join.html_safe
44
+ end
45
+ end
46
+
47
+ def editor_options
48
+ content_tag :div, class: %w(editor options wrapper) do
49
+ Options.new(@template).render
50
+ end
51
+ end
52
+
53
+ def editor_content
54
+ content_tag :div, class: %w(content wrapper) do
55
+ [
56
+ text_area(:content, placeholder: t('.content'), class: %w(content editor)),
57
+ text_area(:stylesheet, placeholder: t('.stylesheet'), class: %w(stylesheet hidden editor)),
58
+ text_area(:javascript, placeholder: t('.javascript'), class: %w(javascript hidden editor))
59
+ ].join.html_safe
60
+ end
61
+ end
62
+
63
+ def possible_actions
64
+ content_tag :div, class: %w(possible save actions) do
65
+ content_tag(:div, save_button, class: %w(wrapper standalone))
66
+ end
67
+ end
68
+
69
+ def save_button
70
+ button("Save", name: "post[status]", value: "draft", class: %w(button))
71
+ end
72
+
73
+ def method_missing(method, *args, &block)
74
+ @template.send(method, *args, &block)
75
+ end
76
+
77
+ class Options
78
+
79
+ def initialize(template)
80
+ @template = template
81
+ end
82
+
83
+ def render
84
+ content_tag(:div, editor_options, class: %w(editor options))
85
+ end
86
+
87
+ def editor_options
88
+ [
89
+ content_tag(:a, t('fields.text'), binding: ".editor.content", class: %w(content active)),
90
+ content_tag(:a, t('fields.CSS'), binding: ".editor.stylesheet", class: %w(content)),
91
+ content_tag(:a, t('fields.JS'), binding: ".editor.javascript", class: %w(content))
92
+ ].join.html_safe
93
+ end
94
+
95
+ def t(*args)
96
+ I18n.t args[0], scope: %w(admin partials form options)
97
+ end
98
+
99
+ def method_missing(method, *args, &block)
100
+ @template.send(method, *args, &block)
101
+ end
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,47 @@
1
+ module Admin
2
+ class PostBuilder < ActionView::Helpers::FormBuilder
3
+
4
+ def initialize(object_name, object, template, options)
5
+ if object.draft?
6
+ options[:html][:class] << 'autosave'
7
+ end
8
+ super
9
+ end
10
+
11
+ def errors
12
+ return unless object.errors.any?
13
+ content_tag :div, class: %w(container errors) do
14
+ [
15
+ content_tag(:span, h(object.errors.full_messages.to_sentence)),
16
+ link_to("x", "javascript:void(0)", class: %w(dismiss button))
17
+ ].join.html_safe
18
+ end
19
+ end
20
+
21
+ def editor
22
+ content_tag :section, class: %w(textareas) do
23
+ [
24
+ text_area(:content, placeholder: t('.content'), class: %w(content active), target: 'content'),
25
+ text_area(:stylesheet, placeholder: t('.stylesheet'), class: %w(stylesheet), target: 'stylesheet'),
26
+ text_area(:javascript, placeholder: t('.javascript'), class: %w(javascript), target: 'javascript')
27
+ ].join.html_safe
28
+ end
29
+ end
30
+
31
+ def action
32
+ if object.published?
33
+ button 'Save', value: 'save', form: "postEditor"
34
+ else
35
+ button 'Publish', value: 'publish', form: "postEditor"
36
+ end
37
+ end
38
+
39
+ def t(*args)
40
+ I18n.t args[0], scope: %w(admin form post)
41
+ end
42
+
43
+ def method_missing(method, *args, &block)
44
+ @template.send(method, *args, &block)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,60 @@
1
+ module Admin
2
+ module ImagesHelper
3
+
4
+ def image_form_signature(policy)
5
+ @signature ||= begin
6
+ key = Rails.application.secrets.s3['secret_key']
7
+ sha = OpenSSL::Digest.new('sha1')
8
+ digest = OpenSSL::HMAC.digest sha, key, policy
9
+ Base64.encode64(digest).gsub("\n", "")
10
+ end
11
+ end
12
+
13
+ def image_form_policy(post)
14
+ @policy ||= begin
15
+ namespace = [post.id]
16
+ if Rails.application.secrets.s3.has_key?('namespace')
17
+ namespace.insert 0, Rails.application.secrets.s3['namespace']
18
+ end
19
+
20
+ policy = {
21
+ "expiration" => (Time.now + 10.years).utc.to_s(:iso8601),
22
+ "conditions" => [
23
+ {"bucket" => Rails.application.secrets.s3['bucket']},
24
+ ["starts-with", "$key", "#{namespace.join('/')}/"],
25
+ ["starts-with", "$Content-Type", ""],
26
+ {"acl" => "private"},
27
+ {'success_action_status' => '201'}
28
+ ]
29
+ }
30
+ Base64.encode64(policy.to_json).gsub("\n","")
31
+ end
32
+ end
33
+
34
+ def editor_image_tag(post)
35
+ return unless post.header?
36
+ content_tag :div, class: %w(image), style: "background-image: url('#{post.header.url}')" do
37
+ button_to "Remove this image",
38
+ admin_post_properties_path(post.id),
39
+ method: :delete,
40
+ remote: true,
41
+ params: {
42
+ property: 'image'
43
+ },
44
+ data: {confirm: 'Are you sure you want to destroy this image?'}
45
+ end
46
+ end
47
+
48
+ def images_config_title
49
+ if @s3 && @s3.errors.any?
50
+ content_tag :h1, class: %w(error) do
51
+ @s3.errors.messages.values.flatten.to_sentence
52
+ end
53
+ else
54
+ content_tag :h1 do
55
+ t('admin.images.helper.title')
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,26 @@
1
+ module Admin::LabelsHelper
2
+ def create_label_button(label, post)
3
+ button_to label.name.capitalize,
4
+ admin_post_properties_path(post.id),
5
+ remote: true,
6
+ form: {id: "label-#{label.id}"},
7
+ form_class: %w(create label),
8
+ params: {
9
+ property: :label,
10
+ value: label.name
11
+ }
12
+ end
13
+
14
+ def destroy_label_button(label, post)
15
+ button_to label.name.capitalize,
16
+ admin_post_properties_path(post.id),
17
+ form: {id: "label-#{label.id}"},
18
+ form_class: %w(destroy label),
19
+ method: :delete,
20
+ remote: true,
21
+ params: {
22
+ property: :label,
23
+ value: label.name
24
+ }
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Admin
2
+ module PostsHelper
3
+ include ::PostsHelper
4
+
5
+ def post_edit_content(post)
6
+ content_tag :div, id: 'PostBody', postid: @post.id,
7
+ class: %w(content),
8
+ as: 'Editor.Content',
9
+ contenteditable: true,
10
+ href: admin_post_path(@post.id) do |div|
11
+
12
+ if Rails.application.secrets.has_key?(:s3)
13
+ div[:bucket] = Rails.application.secrets.s3['bucket'],
14
+ div[:access_key] = Rails.application.secrets.s3['access_key'],
15
+ div[:signature] = image_form_signature(image_form_policy(@post)),
16
+ div[:policy] = image_form_policy(@post)
17
+ if Rails.application.secrets.s3.has_key?('namespace')
18
+ div['namespace'] = Rails.application.secrets.s3['namespace']
19
+ end
20
+ end
21
+
22
+ post.content
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,64 @@
1
+ module ApplicationHelper
2
+ def admin_navigation
3
+ return unless signed_in?
4
+ render 'sessions/navigation'
5
+ end
6
+
7
+ def title_tag(title = 'Ecrire')
8
+ content_tag :title do
9
+ if block_given?
10
+ yield
11
+ elsif !@post.nil?
12
+ @post.title
13
+ else
14
+ title
15
+ end
16
+ end
17
+ end
18
+
19
+ def assets_tags
20
+ [
21
+ stylesheet_link_tag("application", "base", media: "all", "data-turbolinks-track" => true),
22
+ javascript_include_tag("application", "base", "data-turbolinks-track" => true)
23
+ ].join.html_safe
24
+ end
25
+
26
+ def meta_informations_tags
27
+ [
28
+ content_tag(:link, nil, rel: 'alternate', type: 'application/rss+xml', title: 'RSS', href: '/feed'),
29
+ content_tag(:link, nil, rel: %w(shortcut icon), href: asset_url('favicon.ico')),
30
+ csrf_meta_tags
31
+ ].join.html_safe
32
+ end
33
+
34
+ def description_meta_tag
35
+ if Rails.application.secrets.fetch(:meta, {}).has_key?(:description)
36
+ content_tag :meta, nil, name: 'description', content: Rails.application.secrets[:meta][:description]
37
+ end
38
+ end
39
+
40
+ def open_graph_type
41
+ if @post.nil?
42
+ 'website'
43
+ else
44
+ 'article'
45
+ end
46
+ end
47
+
48
+ def body_tag(html_options = {}, &block)
49
+ html_options[:id] ||= [controller_name, action_name].map(&:capitalize).join
50
+ content_tag :body, html_options, &block
51
+ end
52
+
53
+
54
+ def flash_messages
55
+ return if flash.empty?
56
+
57
+ flash.map do |name, msg|
58
+ content_tag :div, class: %W(flash #{name}) do
59
+ content_tag(:span, h(msg), class: %w(message))
60
+ end
61
+ end.join.html_safe
62
+ end
63
+
64
+ end
@@ -0,0 +1,91 @@
1
+ module ContentTagHelper
2
+ def content_tag(*args, &block)
3
+ if block_given?
4
+ tag = Tag.new(*args)
5
+ old_buf = @output_buffer
6
+ @output_buffer = ActionView::OutputBuffer.new
7
+ value = yield(tag)
8
+ content = tag.render(@output_buffer.presence || value)
9
+ @output_buffer = old_buf
10
+ content
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ class Tag
19
+ include ActionView::Helpers::CaptureHelper
20
+ attr_accessor :id
21
+ attr_reader :name, :css
22
+
23
+ def initialize(name, *args)
24
+ @name = name
25
+ @attributes = tag_options(*args)
26
+ @attributes[:class] = Tag::CSS.new(@attributes.fetch(:class, {}))
27
+ end
28
+
29
+ def tag_options(*args)
30
+ options = nil
31
+ args.each do |a|
32
+ if a.is_a?(Hash)
33
+ options = a
34
+ break
35
+ end
36
+ end
37
+ (options || {}).with_indifferent_access
38
+ end
39
+
40
+ def css
41
+ @attributes[:class]
42
+ end
43
+
44
+ def []=(k,v)
45
+ @attributes[k] = v
46
+ end
47
+
48
+ def render(content)
49
+ "<#{name}#{render_attributes}>#{content.to_s.strip}</#{name}>".html_safe
50
+ end
51
+
52
+ def render_attributes
53
+ attrs = @attributes.dup
54
+ if css.empty?
55
+ attrs.delete :class
56
+ else
57
+ attrs[:class] = css.to_s
58
+ end
59
+
60
+ attrs.keys.map do |k|
61
+ "#{k}='#{attrs[k]}'"
62
+ end.join(' ').prepend(' ').html_safe
63
+ end
64
+
65
+
66
+ class CSS
67
+
68
+ def initialize(css)
69
+ if css.is_a? String
70
+ @internals = css.split(' ')
71
+ else
72
+ @internals = css.to_a
73
+ end
74
+ end
75
+
76
+ def to_s
77
+ @internals.uniq.join(' ').html_safe
78
+ end
79
+
80
+ def empty?
81
+ @internals.empty?
82
+ end
83
+
84
+ def <<(name)
85
+ @internals << name
86
+ nil
87
+ end
88
+ end
89
+ end
90
+ end
91
+
@@ -0,0 +1,45 @@
1
+ module OpenGraphHelper
2
+ # OpenGraph is in meta tags. First I thought it would bring problem if meta tags wouldn't change between page load.
3
+ # Thinking it through, it doesn't matter if open graph tags aren't changed between pages as it's needed by crawler that does full page load
4
+ # So the tags will always match the content.
5
+ # I doubt this will change in the future as turbolinks checks for crawlers and disable itself when it meets one.
6
+ #
7
+
8
+ def open_graph_tags
9
+ if @post.nil?
10
+ og_website
11
+ else
12
+ og_article(@post)
13
+ end
14
+ end
15
+
16
+ protected
17
+
18
+ def og_website
19
+ [
20
+ og_title,
21
+ og_type('website')
22
+ ].join.html_safe
23
+ end
24
+
25
+ def og_article(post)
26
+ raise OGNoArticleError if post.nil?
27
+ [
28
+ og_title,
29
+ og_type('article'),
30
+ content_tag(:meta, nil, property: 'og:article:published_time', content: post.published_at.iso8601)
31
+ ].join.html_safe
32
+
33
+ end
34
+
35
+ def og_type(value)
36
+ content_tag :meta, nil, property: 'og:type', content: value
37
+ end
38
+
39
+ def og_title
40
+ content_tag :meta, nil, property: 'og:title', content: title
41
+ end
42
+
43
+ class OGNoArticleError < StandardError; end
44
+
45
+ end
@@ -0,0 +1,19 @@
1
+ module PostsHelper
2
+ def post_path(post, options = {})
3
+ if post.published?
4
+ options[:year] = post.published_at.year
5
+ options[:month] = post.published_at.month
6
+ else
7
+ options[:year] = post.created_at.year
8
+ options[:month] = post.created_at.month
9
+ end
10
+
11
+ super(post.becomes(::Post), options)
12
+ end
13
+
14
+ def edit_post_link(options = {})
15
+ return unless signed_in?
16
+
17
+ link_to t('posts.edit'), edit_admin_post_path(post.id), options
18
+ end
19
+ end