panda-cms 0.7.0 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/builds/panda.cms.css +0 -50
- data/app/components/panda/cms/admin/table_component.html.erb +1 -1
- data/app/components/panda/cms/code_component.rb +2 -1
- data/app/components/panda/cms/rich_text_component.html.erb +86 -2
- data/app/components/panda/cms/rich_text_component.rb +131 -20
- data/app/controllers/panda/cms/admin/block_contents_controller.rb +18 -7
- data/app/controllers/panda/cms/admin/files_controller.rb +22 -12
- data/app/controllers/panda/cms/admin/posts_controller.rb +33 -11
- data/app/controllers/panda/cms/pages_controller.rb +29 -0
- data/app/controllers/panda/cms/posts_controller.rb +26 -4
- data/app/helpers/panda/cms/admin/posts_helper.rb +23 -32
- data/app/helpers/panda/cms/posts_helper.rb +32 -0
- data/app/javascript/panda/cms/controllers/dashboard_controller.js +0 -1
- data/app/javascript/panda/cms/controllers/editor_form_controller.js +134 -11
- data/app/javascript/panda/cms/controllers/editor_iframe_controller.js +395 -130
- data/app/javascript/panda/cms/controllers/slug_controller.js +33 -43
- data/app/javascript/panda/cms/editor/editor_js_config.js +202 -73
- data/app/javascript/panda/cms/editor/editor_js_initializer.js +243 -194
- data/app/javascript/panda/cms/editor/plain_text_editor.js +1 -1
- data/app/javascript/panda/cms/editor/resource_loader.js +89 -0
- data/app/javascript/panda/cms/editor/rich_text_editor.js +162 -0
- data/app/models/panda/cms/page.rb +18 -0
- data/app/models/panda/cms/post.rb +61 -3
- data/app/models/panda/cms/redirect.rb +2 -2
- data/app/views/panda/cms/admin/posts/_form.html.erb +15 -4
- data/app/views/panda/cms/admin/posts/index.html.erb +5 -3
- data/config/routes.rb +34 -6
- data/db/migrate/20250106223303_add_author_id_to_panda_cms_posts.rb +5 -0
- data/lib/panda/cms/editor_js_content.rb +14 -1
- data/lib/panda/cms/engine.rb +3 -6
- data/lib/panda-cms/version.rb +1 -1
- data/lib/panda-cms.rb +5 -11
- metadata +290 -35
@@ -0,0 +1,162 @@
|
|
1
|
+
import EditorJS from "@editorjs/editorjs"
|
2
|
+
import Paragraph from "@editorjs/paragraph"
|
3
|
+
import Header from "@editorjs/header"
|
4
|
+
import List from "@editorjs/list"
|
5
|
+
import Quote from "@editorjs/quote"
|
6
|
+
import Table from "@editorjs/table"
|
7
|
+
import NestedList from "@editorjs/nested-list"
|
8
|
+
|
9
|
+
export default class RichTextEditor {
|
10
|
+
constructor(element, iframe) {
|
11
|
+
this.element = element
|
12
|
+
this.iframe = iframe
|
13
|
+
this.editor = null
|
14
|
+
this.initialized = false
|
15
|
+
this.initialize()
|
16
|
+
}
|
17
|
+
|
18
|
+
async initialize() {
|
19
|
+
if (this.initialized) return
|
20
|
+
console.debug("[Panda CMS] Initializing EditorJS")
|
21
|
+
|
22
|
+
try {
|
23
|
+
let content = this.element.dataset.editableContent || ""
|
24
|
+
let previousData = this.element.dataset.editablePreviousData || ""
|
25
|
+
console.debug("[Panda CMS] Initial content:", content)
|
26
|
+
console.debug("[Panda CMS] Previous data:", previousData)
|
27
|
+
|
28
|
+
let parsedContent
|
29
|
+
if (previousData) {
|
30
|
+
try {
|
31
|
+
// Try to decode base64 first
|
32
|
+
const decodedData = atob(previousData)
|
33
|
+
console.debug("[Panda CMS] Decoded base64 data:", decodedData)
|
34
|
+
parsedContent = JSON.parse(decodedData)
|
35
|
+
console.debug("[Panda CMS] Successfully parsed base64 data:", parsedContent)
|
36
|
+
} catch (e) {
|
37
|
+
console.debug("[Panda CMS] Not base64 encoded or invalid, trying direct JSON parse:", e)
|
38
|
+
try {
|
39
|
+
parsedContent = JSON.parse(previousData)
|
40
|
+
console.debug("[Panda CMS] Successfully parsed JSON data:", parsedContent)
|
41
|
+
} catch (e2) {
|
42
|
+
console.error("[Panda CMS] Failed to parse previous data:", e2)
|
43
|
+
parsedContent = this.getDefaultContent()
|
44
|
+
}
|
45
|
+
}
|
46
|
+
} else if (content) {
|
47
|
+
try {
|
48
|
+
parsedContent = JSON.parse(content)
|
49
|
+
console.debug("[Panda CMS] Successfully parsed content:", parsedContent)
|
50
|
+
} catch (e) {
|
51
|
+
console.error("[Panda CMS] Failed to parse content:", e)
|
52
|
+
parsedContent = this.getDefaultContent()
|
53
|
+
}
|
54
|
+
} else {
|
55
|
+
parsedContent = this.getDefaultContent()
|
56
|
+
}
|
57
|
+
|
58
|
+
// Create holder element before initialization
|
59
|
+
const holderId = `editor-${Math.random().toString(36).substr(2, 9)}`
|
60
|
+
const holderElement = document.createElement("div")
|
61
|
+
holderElement.id = holderId
|
62
|
+
holderElement.className = "editor-js-holder codex-editor"
|
63
|
+
|
64
|
+
// Clear any existing content and append holder
|
65
|
+
this.element.textContent = ""
|
66
|
+
this.element.appendChild(holderElement)
|
67
|
+
|
68
|
+
// Initialize EditorJS
|
69
|
+
this.editor = new EditorJS({
|
70
|
+
holder: holderId,
|
71
|
+
data: parsedContent,
|
72
|
+
placeholder: "Click to start writing...",
|
73
|
+
tools: {
|
74
|
+
paragraph: {
|
75
|
+
class: Paragraph,
|
76
|
+
inlineToolbar: true
|
77
|
+
},
|
78
|
+
header: {
|
79
|
+
class: Header,
|
80
|
+
inlineToolbar: true
|
81
|
+
},
|
82
|
+
list: {
|
83
|
+
class: NestedList,
|
84
|
+
inlineToolbar: true,
|
85
|
+
config: {
|
86
|
+
defaultStyle: 'unordered',
|
87
|
+
enableLineBreaks: true
|
88
|
+
}
|
89
|
+
},
|
90
|
+
quote: {
|
91
|
+
class: Quote,
|
92
|
+
inlineToolbar: true
|
93
|
+
},
|
94
|
+
table: {
|
95
|
+
class: Table,
|
96
|
+
inlineToolbar: true
|
97
|
+
}
|
98
|
+
},
|
99
|
+
onChange: () => {
|
100
|
+
this.save()
|
101
|
+
}
|
102
|
+
})
|
103
|
+
|
104
|
+
await this.editor.isReady
|
105
|
+
this.initialized = true
|
106
|
+
console.debug("[Panda CMS] EditorJS initialized successfully")
|
107
|
+
} catch (error) {
|
108
|
+
console.error("[Panda CMS] Error initializing EditorJS:", error)
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
getDefaultContent() {
|
113
|
+
return {
|
114
|
+
time: Date.now(),
|
115
|
+
blocks: [
|
116
|
+
{
|
117
|
+
type: "paragraph",
|
118
|
+
data: {
|
119
|
+
text: ""
|
120
|
+
}
|
121
|
+
}
|
122
|
+
],
|
123
|
+
version: "2.28.2"
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
async save() {
|
128
|
+
if (!this.editor) return null
|
129
|
+
|
130
|
+
try {
|
131
|
+
const savedData = await this.editor.save()
|
132
|
+
const jsonString = JSON.stringify(savedData)
|
133
|
+
// Store both base64 and regular JSON
|
134
|
+
this.element.dataset.editablePreviousData = btoa(jsonString)
|
135
|
+
this.element.dataset.editableContent = jsonString
|
136
|
+
return jsonString
|
137
|
+
} catch (error) {
|
138
|
+
console.error("[Panda CMS] Error saving EditorJS content:", error)
|
139
|
+
return null
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
async clear() {
|
144
|
+
if (!this.editor) return
|
145
|
+
|
146
|
+
try {
|
147
|
+
await this.editor.clear()
|
148
|
+
this.element.dataset.editablePreviousData = ""
|
149
|
+
this.element.dataset.editableContent = ""
|
150
|
+
} catch (error) {
|
151
|
+
console.error("[Panda CMS] Error clearing EditorJS content:", error)
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
destroy() {
|
156
|
+
if (this.editor) {
|
157
|
+
this.editor.destroy()
|
158
|
+
this.editor = null
|
159
|
+
this.initialized = false
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
@@ -67,6 +67,7 @@ module Panda
|
|
67
67
|
generate_content_blocks
|
68
68
|
update_existing_menu_items
|
69
69
|
update_auto_menus
|
70
|
+
create_redirect_if_path_changed
|
70
71
|
end
|
71
72
|
|
72
73
|
def generate_content_blocks
|
@@ -91,6 +92,23 @@ module Panda
|
|
91
92
|
def update_existing_menu_items
|
92
93
|
menu_items.where.not(text: title).update_all(text: title)
|
93
94
|
end
|
95
|
+
|
96
|
+
def create_redirect_if_path_changed
|
97
|
+
return unless saved_change_to_path?
|
98
|
+
|
99
|
+
old_path = saved_changes["path"].first
|
100
|
+
new_path = saved_changes["path"].last
|
101
|
+
|
102
|
+
# Create a redirect from the old path to the new path
|
103
|
+
Panda::CMS::Redirect.create!(
|
104
|
+
origin_panda_cms_page_id: id,
|
105
|
+
destination_panda_cms_page_id: id,
|
106
|
+
status_code: 301,
|
107
|
+
visits: 0,
|
108
|
+
origin_path: old_path,
|
109
|
+
destination_path: new_path
|
110
|
+
)
|
111
|
+
end
|
94
112
|
end
|
95
113
|
end
|
96
114
|
end
|
@@ -5,6 +5,9 @@ module Panda
|
|
5
5
|
class Post < ApplicationRecord
|
6
6
|
include ::Panda::CMS::EditorJsContent
|
7
7
|
|
8
|
+
after_commit :clear_menu_cache
|
9
|
+
before_validation :format_slug
|
10
|
+
|
8
11
|
self.table_name = "panda_cms_posts"
|
9
12
|
|
10
13
|
has_paper_trail versions: {
|
@@ -12,18 +15,20 @@ module Panda
|
|
12
15
|
}
|
13
16
|
|
14
17
|
belongs_to :user, class_name: "Panda::CMS::User"
|
18
|
+
belongs_to :author, class_name: "Panda::CMS::User"
|
15
19
|
|
16
20
|
validates :title, presence: true
|
17
21
|
validates :slug,
|
18
22
|
presence: true,
|
19
23
|
uniqueness: true,
|
20
24
|
format: {
|
21
|
-
with: /\
|
22
|
-
message: "must
|
25
|
+
with: %r{\A(/\d{4}/\d{2}/[a-z0-9-]+|/[a-z0-9-]+)\z},
|
26
|
+
message: "must be in format /YYYY/MM/slug or /slug with only lowercase letters, numbers, and hyphens"
|
23
27
|
}
|
24
28
|
|
25
29
|
scope :ordered, -> { order(published_at: :desc) }
|
26
30
|
scope :with_user, -> { includes(:user) }
|
31
|
+
scope :with_author, -> { includes(:author) }
|
27
32
|
|
28
33
|
enum :status, {
|
29
34
|
active: "active",
|
@@ -33,7 +38,31 @@ module Panda
|
|
33
38
|
}
|
34
39
|
|
35
40
|
def to_param
|
36
|
-
slug
|
41
|
+
# For date-based URLs, return just the slug portion
|
42
|
+
parts = CGI.unescape(slug).delete_prefix("/").split("/")
|
43
|
+
if parts.length == 3 # year/month/slug format
|
44
|
+
parts.last
|
45
|
+
else
|
46
|
+
parts.first
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def year
|
51
|
+
return nil unless slug.match?(%r{\A/\d{4}/})
|
52
|
+
slug.split("/")[1]
|
53
|
+
end
|
54
|
+
|
55
|
+
def month
|
56
|
+
return nil unless slug.match?(%r{\A/\d{4}/\d{2}/})
|
57
|
+
slug.split("/")[2]
|
58
|
+
end
|
59
|
+
|
60
|
+
def route_params
|
61
|
+
if year && month
|
62
|
+
{year: year, month: month, slug: to_param}
|
63
|
+
else
|
64
|
+
{slug: to_param}
|
65
|
+
end
|
37
66
|
end
|
38
67
|
|
39
68
|
def admin_param
|
@@ -55,6 +84,35 @@ module Panda
|
|
55
84
|
text = text.squish if squish
|
56
85
|
text.truncate(length).html_safe
|
57
86
|
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def clear_menu_cache
|
91
|
+
Rails.cache.delete("panda_cms_news_months")
|
92
|
+
end
|
93
|
+
|
94
|
+
def format_slug
|
95
|
+
return if slug.blank?
|
96
|
+
|
97
|
+
# Remove any leading/trailing slashes and decode
|
98
|
+
self.slug = CGI.unescape(slug.strip.gsub(%r{^/+|/+$}, ""))
|
99
|
+
|
100
|
+
# Handle the case where we already have a properly formatted slug
|
101
|
+
if slug.match?(%r{\A\d{4}/\d{2}/[^/]+\z})
|
102
|
+
return self.slug = "/#{slug}"
|
103
|
+
end
|
104
|
+
|
105
|
+
# Handle the case where we have a date-prefixed slug (from JS)
|
106
|
+
if (match = slug.match(%r{\A(\d{4})-(\d{2})-(.+)\z}))
|
107
|
+
year, month, base_slug = match[1], match[2], match[3]
|
108
|
+
return self.slug = "/#{year}/#{month}/#{base_slug}"
|
109
|
+
end
|
110
|
+
|
111
|
+
# For new slugs without any date structure
|
112
|
+
base_slug = slug.downcase.gsub(/[^a-z0-9-]+/, "-").gsub(/^-+|-+$/, "")
|
113
|
+
date_prefix = published_at.present? ? published_at.strftime("%Y/%m") : Time.current.strftime("%Y/%m")
|
114
|
+
self.slug = "/#{date_prefix}/#{base_slug}"
|
115
|
+
end
|
58
116
|
end
|
59
117
|
end
|
60
118
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Panda
|
2
2
|
module CMS
|
3
3
|
class Redirect < ApplicationRecord
|
4
|
-
belongs_to :origin_page, class_name: "Panda::CMS::Page", foreign_key: :origin_panda_cms_page_id
|
5
|
-
belongs_to :destination_page, class_name: "Panda::CMS::Page", foreign_key: :destination_panda_cms_page_id
|
4
|
+
belongs_to :origin_page, class_name: "Panda::CMS::Page", foreign_key: :origin_panda_cms_page_id, optional: true
|
5
|
+
belongs_to :destination_page, class_name: "Panda::CMS::Page", foreign_key: :destination_panda_cms_page_id, optional: true
|
6
6
|
|
7
7
|
validates :status_code, presence: true
|
8
8
|
validates :visits, presence: true
|
@@ -1,7 +1,18 @@
|
|
1
1
|
<%= panda_cms_form_with model: post, url: url, method: post.persisted? ? :put : :post do |f| %>
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
<div data-controller="slug" data-slug-add-date-prefix-value="true">
|
3
|
+
<%= f.text_field :title,
|
4
|
+
class: "block w-full rounded-md border-0 p-2 text-gray-900 ring-1 ring-inset ring-mid placeholder:text-gray-300 focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer",
|
5
|
+
data: {
|
6
|
+
"slug-target": "input_text",
|
7
|
+
action: "focusout->slug#generatePath"
|
8
|
+
} %>
|
9
|
+
<%= f.text_field :slug,
|
10
|
+
class: "block w-full rounded-md border-0 p-2 text-gray-900 ring-1 ring-inset ring-mid placeholder:text-gray-300 focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer",
|
11
|
+
data: {
|
12
|
+
"slug-target": "output_text"
|
13
|
+
} %>
|
14
|
+
</div>
|
15
|
+
<%= f.select :author_id, Panda::CMS::User.admin.map { |u| [u.name, u.id] }, {}, class: "block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-mid focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
|
5
16
|
<%= f.datetime_field :published_at, class: "block w-full rounded-md border-0 p-2 text-gray-900 ring-1 ring-inset ring-mid placeholder:text-gray-300 focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
|
6
17
|
<%= f.select :status, options_for_select([["Active", "active"], ["Draft", "draft"], ["Hidden", "hidden"], ["Archived", "archived"]], selected: post.status), {}, class: "block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-mid focus:ring-1 focus:ring-inset focus:ring-dark sm:leading-6 hover:pointer" %>
|
7
18
|
|
@@ -16,7 +27,7 @@
|
|
16
27
|
} %>
|
17
28
|
<div id="<%= editor_id %>"
|
18
29
|
data-editor-form-target="editorContainer"
|
19
|
-
class="max-w-full block
|
30
|
+
class="max-w-full block bg-white pt-1 mb-4 mt-2 border border-mid rounded-md min-h-[300px]">
|
20
31
|
</div>
|
21
32
|
</div>
|
22
33
|
|
@@ -5,13 +5,15 @@
|
|
5
5
|
|
6
6
|
<%= render Panda::CMS::Admin::TableComponent.new(term: "post", rows: posts) do |table| %>
|
7
7
|
<% table.column("Title") do |post| %>
|
8
|
-
<div
|
8
|
+
<div>
|
9
9
|
<%= link_to post.title, edit_admin_post_path(post.admin_param), class: "block h-full w-full" %>
|
10
|
-
<span class="block text-xs text-black/60"
|
10
|
+
<span class="block text-xs text-black/60">
|
11
|
+
<%= CGI.unescape("#{Panda::CMS.config.posts[:prefix]}#{post.slug}") %>
|
12
|
+
</span>
|
11
13
|
</div>
|
12
14
|
<% end %>
|
13
15
|
<% table.column("Status") { |post| render Panda::CMS::Admin::TagComponent.new(status: post.status) } %>
|
14
|
-
<% table.column("Published") { |post| render Panda::CMS::Admin::UserActivityComponent.new(at: post.published_at, user: post.
|
16
|
+
<% table.column("Published") { |post| render Panda::CMS::Admin::UserActivityComponent.new(at: post.published_at, user: post.author)} %>
|
15
17
|
<% table.column("Last Updated") { |post| render Panda::CMS::Admin::UserActivityComponent.new(whodunnit_to: post)} %>
|
16
18
|
<% end %>
|
17
19
|
|
data/config/routes.rb
CHANGED
@@ -35,14 +35,42 @@ Panda::CMS::Engine.routes.draw do
|
|
35
35
|
# OmniAuth additionally adds a GET route for "#{Panda::CMS.route_namespace}/auth/:provider" but doesn't name it
|
36
36
|
delete Panda::CMS.route_namespace, to: "admin/sessions#destroy", as: :admin_logout
|
37
37
|
|
38
|
+
### APPENDED ROUTES ###
|
39
|
+
|
40
|
+
# TODO: Allow multiple types of post in future
|
38
41
|
if Panda::CMS.config.posts[:enabled]
|
39
|
-
|
40
|
-
# TODO: This now requires a page to be created, make it explicit (with a rendering posts helper?)
|
41
|
-
# get Panda::CMS.posts[:prefix], to: "posts#index", as: :posts
|
42
|
-
get "#{Panda::CMS.config.posts[:prefix]}/:slug", to: "posts#show", as: :post
|
43
|
-
end
|
42
|
+
get Panda::CMS.config.posts[:prefix], to: "posts#index", as: :posts
|
44
43
|
|
45
|
-
|
44
|
+
# Route for date-based URLs that won't encode slashes
|
45
|
+
get "#{Panda::CMS.config.posts[:prefix]}/:year/:month/:slug",
|
46
|
+
to: "posts#show",
|
47
|
+
as: :post_with_date,
|
48
|
+
constraints: {
|
49
|
+
year: /\d{4}/,
|
50
|
+
month: /\d{2}/,
|
51
|
+
slug: /[^\/]+/,
|
52
|
+
format: /html|json|xml/
|
53
|
+
}
|
54
|
+
|
55
|
+
# Route for non-date URLs
|
56
|
+
get "#{Panda::CMS.config.posts[:prefix]}/:slug",
|
57
|
+
to: "posts#show",
|
58
|
+
as: :post,
|
59
|
+
constraints: {
|
60
|
+
slug: /[^\/]+/,
|
61
|
+
format: /html|json|xml/
|
62
|
+
}
|
63
|
+
|
64
|
+
# Route for month archive
|
65
|
+
get "#{Panda::CMS.config.posts[:prefix]}/:year/:month",
|
66
|
+
to: "posts#by_month",
|
67
|
+
as: :posts_by_month,
|
68
|
+
constraints: {
|
69
|
+
year: /\d{4}/,
|
70
|
+
month: /\d{2}/,
|
71
|
+
format: /html|json|xml/
|
72
|
+
}
|
73
|
+
end
|
46
74
|
|
47
75
|
# See lib/panda/cms/engine.rb
|
48
76
|
end
|
@@ -12,10 +12,23 @@ module Panda::CMS::EditorJsContent
|
|
12
12
|
|
13
13
|
def generate_cached_content
|
14
14
|
if content.is_a?(String)
|
15
|
-
|
15
|
+
begin
|
16
|
+
parsed_content = JSON.parse(content)
|
17
|
+
self.cached_content = if parsed_content.is_a?(Hash) && parsed_content["blocks"].present?
|
18
|
+
Panda::CMS::EditorJs::Renderer.new(parsed_content).render
|
19
|
+
else
|
20
|
+
content
|
21
|
+
end
|
22
|
+
rescue JSON::ParserError
|
23
|
+
# If it's not JSON, treat it as plain text
|
24
|
+
self.cached_content = content
|
25
|
+
end
|
16
26
|
elsif content.is_a?(Hash) && content["blocks"].present?
|
17
27
|
# Process EditorJS content
|
18
28
|
self.cached_content = Panda::CMS::EditorJs::Renderer.new(content).render
|
29
|
+
else
|
30
|
+
# For any other case, store as is
|
31
|
+
self.cached_content = content.to_s
|
19
32
|
end
|
20
33
|
end
|
21
34
|
end
|
data/lib/panda/cms/engine.rb
CHANGED
@@ -1,15 +1,12 @@
|
|
1
|
-
require "
|
2
|
-
require "
|
3
|
-
require "stimulus-rails"
|
4
|
-
require "view_component"
|
5
|
-
|
1
|
+
require "rubygems"
|
2
|
+
require "panda/core"
|
6
3
|
require "panda/cms/railtie"
|
7
4
|
|
8
5
|
module Panda
|
9
6
|
module CMS
|
10
7
|
class Engine < ::Rails::Engine
|
11
8
|
isolate_namespace Panda::CMS
|
12
|
-
engine_name "
|
9
|
+
engine_name "panda-cms"
|
13
10
|
|
14
11
|
# Add services directory to autoload paths
|
15
12
|
config.autoload_paths += %W[
|
data/lib/panda-cms/version.rb
CHANGED
data/lib/panda-cms.rb
CHANGED
@@ -1,14 +1,8 @@
|
|
1
|
-
require "
|
2
|
-
require "
|
3
|
-
require "
|
4
|
-
|
5
|
-
require "
|
6
|
-
require "omniauth/strategies/microsoft_graph"
|
7
|
-
require "omniauth/strategies/google_oauth2"
|
8
|
-
require "omniauth/strategies/github"
|
9
|
-
require "paper_trail"
|
10
|
-
require "view_component"
|
11
|
-
require "zeitwerk"
|
1
|
+
require "rubygems"
|
2
|
+
require "panda/core"
|
3
|
+
require "panda/cms/railtie"
|
4
|
+
|
5
|
+
require "capybara/rspec"
|
12
6
|
|
13
7
|
module Panda
|
14
8
|
module CMS
|