hera_cms 0.1.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 (40) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +67 -0
  4. data/Rakefile +22 -0
  5. data/app/assets/config/hera_cms_manifest.js +1 -0
  6. data/app/assets/images/hera_cms/hera_white.png +0 -0
  7. data/app/assets/javascripts/hera_cms/application.js +1 -0
  8. data/app/assets/stylesheets/hera_cms/application.css +154 -0
  9. data/app/assets/stylesheets/hera_cms/links.css +4 -0
  10. data/app/controllers/hera_cms/application_controller.rb +5 -0
  11. data/app/controllers/hera_cms/base_controller.rb +9 -0
  12. data/app/controllers/hera_cms/dashboard_controller.rb +6 -0
  13. data/app/controllers/hera_cms/links_controller.rb +56 -0
  14. data/app/helpers/hera_cms/application_helper.rb +4 -0
  15. data/app/helpers/hera_cms/links_helper.rb +4 -0
  16. data/app/helpers/hera_cms/tag_helper.rb +121 -0
  17. data/app/jobs/hera_cms/application_job.rb +4 -0
  18. data/app/mailers/hera_cms/application_mailer.rb +6 -0
  19. data/app/models/hera_cms/application_record.rb +31 -0
  20. data/app/models/hera_cms/image.rb +7 -0
  21. data/app/models/hera_cms/link.rb +8 -0
  22. data/app/models/hera_cms/text.rb +7 -0
  23. data/app/views/hera_cms/dashboard/home.html.erb +6 -0
  24. data/app/views/hera_cms/layouts/hera_cms/application.html.erb +15 -0
  25. data/app/views/hera_cms/shared/_hera_edit.js +302 -0
  26. data/app/views/hera_cms/shared/_navbar.html.erb +13 -0
  27. data/app/views/layouts/hera_cms/application.html.erb +15 -0
  28. data/config/routes.rb +5 -0
  29. data/db/migrate/20201202150007_create_hera_cms_links.rb +13 -0
  30. data/db/migrate/20201202152559_create_hera_cms_texts.rb +12 -0
  31. data/db/migrate/20201203104012_create_hera_cms_images.rb +13 -0
  32. data/lib/generators/hera_cms/install_generator.rb +22 -0
  33. data/lib/generators/hera_cms/templates/config.rb.tt +2 -0
  34. data/lib/generators/hera_cms/templates/install.rb.tt +38 -0
  35. data/lib/hera_cms.rb +28 -0
  36. data/lib/hera_cms/engine.rb +34 -0
  37. data/lib/hera_cms/railtie.rb +4 -0
  38. data/lib/hera_cms/version.rb +3 -0
  39. data/lib/tasks/hera_cms_tasks.rake +32 -0
  40. metadata +201 -0
@@ -0,0 +1,4 @@
1
+ module HeraCms
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module HeraCms
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,31 @@
1
+ module HeraCms
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ def self.identify(identifier)
5
+ self.find_by(identifier: identifier)
6
+ end
7
+
8
+ def editable?
9
+ true
10
+ end
11
+
12
+ def update_seed
13
+ model = self.class.model_name.plural
14
+ file_name = "hera_database_seed.yml"
15
+ directory_path = File.join(Rails.root, 'db', 'hera_cms')
16
+ @file_path = File.join(directory_path, file_name)
17
+
18
+ Dir.mkdir(directory_path) unless File.exists?(directory_path)
19
+
20
+ elements = File.exists?(@file_path) ? YAML.load(File.read(@file_path)) : {}
21
+
22
+ elements[model] = {} unless elements.key?(model)
23
+
24
+ elements[model][self.identifier] = self.as_json.without("id", "created_at", "updated_at")
25
+
26
+
27
+ File.open(@file_path, "w") { |file| file.write(elements.to_yaml) }
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ module HeraCms
2
+ class Image < ApplicationRecord
3
+ validates :identifier, presence: true, uniqueness: true
4
+
5
+ before_save :update_seed
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module HeraCms
2
+ class Link < ApplicationRecord
3
+ validates :identifier, presence: true, uniqueness: true
4
+ validates :path, presence: true
5
+
6
+ before_save :update_seed
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module HeraCms
2
+ class Text < ApplicationRecord
3
+ validates :identifier, presence: true, uniqueness: true
4
+
5
+ before_save :update_seed
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ <%#= stylesheet_link_tag 'hera_cms/application', media: 'all' %>
2
+
3
+
4
+ <h1>Hello Hera CMS Engine</h1>
5
+
6
+ <%= hera_link "my-link" %>
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Hera cms</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "hera_cms/application", media: "all" %>
9
+ </head>
10
+ <body>
11
+
12
+ <%= yield %>
13
+
14
+ </body>
15
+ </html>
@@ -0,0 +1,302 @@
1
+ console.log('Hera CMS Javascript ON :)');
2
+
3
+ let editables = document.querySelectorAll('.hera-editable')
4
+
5
+ const AUTH_TOKEN = document.querySelector('meta[name=csrf-token]').attributes['content'].value;
6
+
7
+ const allowEdit = () => {
8
+ // Add listeners for all editables [edit mode]
9
+ editables.forEach((editable) => {
10
+ editable.addEventListener('mouseenter', displayEditor);
11
+ editable.addEventListener('mouseleave', hideEditor);
12
+ });
13
+ };
14
+
15
+ const lockEdit = () => {
16
+ // Remove listeners for all editables [view mode]
17
+ editables.forEach((editable) => {
18
+ editable.removeEventListener('mouseenter', displayEditor);
19
+ editable.removeEventListener('mouseleave', hideEditor);
20
+ });
21
+ };
22
+
23
+ const toggleOff = () => {
24
+ // Remove open forms and remove highlights that could be still active [switching from edit to view mode]
25
+ let formBox = document.querySelector(".hera-form-box");
26
+ if(formBox){
27
+ formBox.parentNode.removeChild(formBox);
28
+ };
29
+
30
+ editables.forEach((editable) => {
31
+ if (editable.classList.contains('hera-highlight-layer')){
32
+ editable.classList.remove('hera-highlight-layer');
33
+ }
34
+ });
35
+ // Swap wrapper with editable one (?)
36
+ let wrapper = document.querySelector('.hera-editable-wrapper');
37
+ if(wrapper){
38
+ wrapper.parentNode.replaceChild(wrapper.firstChild, wrapper);
39
+ };
40
+ };
41
+
42
+
43
+ const toggleEditLoader = (e) => {
44
+ // Select all editable elements on the page
45
+ editables = document.querySelectorAll('.hera-editable')
46
+
47
+ // Switches between Edit Mode and View mode
48
+ // In edit mode, all editable links, texts and images need the following:
49
+ // 1. One edit button that appears on hover
50
+ // 2. When you click it, it opens a form that enables you to change the value of the content
51
+ // This events can only exist on edit mode. On view mode nothing happens.
52
+
53
+ const button = e.target;
54
+ if (button.dataset["mode"] === "edit") {
55
+ console.log('Switching to view mode');
56
+ button.innerText = "Off"
57
+ button.dataset["mode"] = "view"
58
+ button.classList.toggle("hera-edit-button-on");
59
+ toggleOff();
60
+
61
+ const layer = document.querySelector(".hera-background-layer");
62
+ document.body.removeChild(layer);
63
+ // const slickerLayer = document.querySelector(".slicker-layer");
64
+ // const slider = document.querySelector('.slick-track');
65
+ // slider.removeChild(slickerLayer);
66
+ lockEdit();
67
+ }
68
+ else if (button.dataset["mode"] === "view") {
69
+ console.log('Switching to edit mode');
70
+ button.innerText = "On"
71
+ button.classList.toggle("hera-edit-button-on");
72
+ button.dataset["mode"] = "edit"
73
+ const layer = document.createElement("div");
74
+ layer.classList.add('hera-background-layer');
75
+ // const slickerLayer = document.createElement("div");
76
+ // slickerLayer.classList.add('hera-slicker-layer');
77
+ // const slider = document.querySelector('.slick-track');
78
+ // slider.appendChild(slickerLayer);
79
+ document.body.appendChild(layer);
80
+ allowEdit();
81
+ }
82
+ }
83
+
84
+ const displayEditor = (e) => {
85
+ // Shows the edit button for an element
86
+ // highlith the field to be edited
87
+ e.target.classList.add('hera-highlight-layer');
88
+ e.target.addEventListener('click', createForm);
89
+ }
90
+
91
+ const hideEditor = (e) => {
92
+ // Removes the edit button for an element
93
+ // remove highlighted field
94
+ e.target.classList.remove('hera-highlight-layer');
95
+ e.target.removeEventListener('click', createForm);
96
+ }
97
+
98
+ const sendRequest = (e) => {
99
+ e.preventDefault();
100
+ form = e.target;
101
+
102
+ let url = form.action + "?&authenticity_token=" + encodeURIComponent(AUTH_TOKEN);
103
+
104
+ options = {
105
+ method: "PUT",
106
+ body: new FormData(form)
107
+ };
108
+
109
+ console.log("fetching");
110
+ fetch(url, options)
111
+ .then(response => response.json())
112
+ .then((data) => {
113
+ console.log(data);
114
+ });
115
+
116
+ console.log('end');
117
+ }
118
+
119
+ const createForm = (e) => {
120
+ // Create a form to update the content of element in the database, using its dataset
121
+
122
+ // Prevent the link click when clicks on the form, and the default form behavior
123
+ e.stopImmediatePropagation();
124
+ e.preventDefault();
125
+ e.target.removeEventListener('click', createForm);
126
+ lockEdit();
127
+
128
+ // Selects the element to be edited
129
+ const editable = e.currentTarget
130
+
131
+ // Creates the base form using rails autenthicity token
132
+ let form = document.createElement("form");
133
+
134
+ // Updates the form action to the proper element update route, using the datase
135
+ form.action = `/hera_cms/${editable.dataset['editableType']}/${editable.dataset['editableId']}`;
136
+
137
+ form.enctype = 'multipart/form-data';
138
+
139
+ // Add a hidden input field to properly utilize the PATCH method
140
+
141
+ let i = document.createElement("input");
142
+ i.setAttribute('type', "hidden");
143
+ i.setAttribute('name', "_method");
144
+ i.setAttribute('value', 'patch');
145
+
146
+ form.appendChild(i);
147
+
148
+ // Add the proper inputs for each type of editable
149
+
150
+ switch (editable.dataset['editableType']) {
151
+ case 'links':
152
+ form = linkForm(form, editable);
153
+ break;
154
+ case 'media_index':
155
+ form = mediaForm(form, editable);
156
+ break;
157
+ case 'texts':
158
+ form = textForm(form, editable);
159
+ break;
160
+ case 'forms':
161
+ form = formForm(form, editable);
162
+ break;
163
+ default:
164
+ console.log(`Tipo ${editable.dataset['editableType']} não identificado.`)
165
+ }
166
+
167
+ // Add form submit button
168
+ let s = document.createElement("input");
169
+ s.setAttribute('type', "submit");
170
+ s.setAttribute('value', "Atualizar");
171
+
172
+ form.appendChild(s);
173
+
174
+ // Add editable wrapper with form inside
175
+ let wrapper = document.createElement("div");
176
+ wrapper.className = "hera-editable-wrapper " + editable.className;
177
+ editable.parentNode.insertBefore(wrapper ,editable);
178
+
179
+ wrappedEditable = wrapper.appendChild(editable);
180
+
181
+ // Creates and initializes the modal
182
+ let formBox = document.createElement('div');
183
+ formBox.classList.add("hera-form-box");
184
+ formBox.appendChild(form);
185
+ wrapper.appendChild(formBox);
186
+ let boxPositionY = wrapper.getBoundingClientRect().top + 150;
187
+ let boxPositionX = wrapper.getBoundingClientRect().left + 150;
188
+ let screenHeight = window.screen.height;
189
+ let screenWidth = window.screen.width;
190
+ let relativeY = (boxPositionY / screenHeight) * screenHeight;
191
+ let relativeX = boxPositionX / screenWidth * screenWidth;
192
+ console.log(relativeX);
193
+ console.log(screenWidth/2);
194
+ if(relativeX > screenWidth / 2){
195
+ formBox.style.left = "-308px";
196
+ }else{
197
+ formBox.style.right = "-308px";
198
+ };
199
+ if(relativeY > screenHeight / 2){
200
+ formBox.style.top = "-150px";
201
+ }else{
202
+ formBox.style.bottom = "-150px";
203
+ };
204
+ wrappedEditable.removeEventListener('mouseenter', displayEditor);
205
+ wrappedEditable.removeEventListener('mouseleave', hideEditor);
206
+
207
+ // Adds button to destroy wrapper and the form
208
+ let destroyButton = document.createElement("p");
209
+ destroyButton.className = "form-destroy";
210
+ destroyButton.innerHTML = "x";
211
+ destroyButton.addEventListener('click', () => {
212
+ if (wrapper.parentNode) {
213
+ newEditable = wrapper.parentNode.replaceChild(editable, wrapper);
214
+ wrapper.removeChild(formBox);
215
+ editable.classList.remove('hera-highlight-layer');
216
+ bodyLayer.classList.remove('hera-clickable');
217
+ allowEdit();
218
+ }
219
+ });
220
+
221
+
222
+ let bodyLayer = document.querySelector('.hera-background-layer');
223
+ bodyLayer.classList.add('hera-clickable');
224
+ bodyLayer.addEventListener('click', () => {
225
+ destroyButton.click();
226
+ });
227
+
228
+ form.appendChild(destroyButton);
229
+ form.addEventListener('submit', sendRequest);
230
+ }
231
+
232
+ const linkForm = (form, editable) => {
233
+
234
+ // Creates text input for the content of the element and appends it to the form
235
+ i = document.createElement("input");
236
+ i.setAttribute('type', "text");
237
+ i.setAttribute('name', "link[path]");
238
+ i.setAttribute('value', editable);
239
+
240
+ form.appendChild(i);
241
+
242
+ return form;
243
+ }
244
+
245
+ const mediaForm = (form, editable) => {
246
+
247
+ // Creates text input for the content of the element and appends it to the form
248
+ i = document.createElement("input");
249
+ i.setAttribute('type', "file");
250
+ i.setAttribute('name', "media[upload]");
251
+
252
+ form.appendChild(i);
253
+
254
+ i = document.createElement("input");
255
+ i.setAttribute('type', "hidden");
256
+ i.setAttribute('name', "media[upload_cache]");
257
+
258
+ form.appendChild(i);
259
+
260
+ return form;
261
+ }
262
+
263
+ const textForm = (form, editable) => {
264
+
265
+ // Creates text input for the content of the element and appends it to the form
266
+ i = document.createElement("textarea");
267
+ i.setAttribute('type', "text");
268
+ i.setAttribute('name', "text[inner_text]");
269
+ i.innerHTML = editable.innerText;
270
+ i.setAttribute('value', editable.innerText);
271
+
272
+ form.appendChild(i);
273
+
274
+ return form;
275
+ }
276
+
277
+ const formForm = (form, editable) => {
278
+ console.log(editable)
279
+ console.log(form)
280
+ i = document.createElement("input");
281
+ i.setAttribute('type', "text");
282
+ i.setAttribute('name', "form[send_to]");
283
+ i.setAttribute('value', editable.getAttribute("data-mail"))
284
+ form.appendChild(i)
285
+ return form
286
+ }
287
+
288
+
289
+ const createElementFromHTML = (htmlString) => {
290
+ // Creates a node element from a HTML string
291
+ const div = document.createElement('div');
292
+ div.innerHTML = htmlString.trim();
293
+
294
+ return div.firstChild;
295
+ }
296
+
297
+
298
+ // Selects the view's edit button and adds listener to toggle between edit and view mode
299
+ const editButton = document.getElementById('hera-edit-button');
300
+ if (editButton) {
301
+ editButton.addEventListener('click', toggleEditLoader)
302
+ }
@@ -0,0 +1,13 @@
1
+ <%= stylesheet_link_tag 'hera_cms/application', media: 'all' %>
2
+
3
+ <div class="hera-navbar">
4
+ <%= image_tag("hera_cms/hera_white.png") %>
5
+ <div class="margin-auto-center">
6
+ Modo Edição:
7
+ <button id="hera-edit-button" data-mode="view">Off</button>
8
+ </div>
9
+ </div>
10
+
11
+ <%= javascript_tag do %>
12
+ <%= render(partial: 'hera_cms/shared/hera_edit', handlers: [], formats: [:js]) %>
13
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Hera cms</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "hera_cms/application", media: "all" %>
9
+ </head>
10
+ <body>
11
+
12
+ <%= yield %>
13
+
14
+ </body>
15
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ HeraCms::Engine.routes.draw do
2
+ root to: "dashboard#home"
3
+
4
+ resources :links, only: :update
5
+ end
@@ -0,0 +1,13 @@
1
+ class CreateHeraCmsLinks < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table :hera_cms_links do |t|
4
+ t.string :identifier
5
+ t.string :path
6
+ t.string :inner_text, default: ""
7
+ t.string :classes, default: ""
8
+ t.string :style, default: ""
9
+
10
+ t.timestamps
11
+ end
12
+ end
13
+ end