hera_cms 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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