pages_core 3.10.2 → 3.11.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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/app/assets/builds/pages_core/admin-dist.js +8 -8
- data/app/helpers/pages_core/admin/admin_helper.rb +1 -0
- data/app/helpers/pages_core/admin/form_builder.rb +2 -1
- data/app/helpers/pages_core/admin/image_uploads_helper.rb +1 -1
- data/app/helpers/pages_core/admin/locales_helper.rb +26 -0
- data/app/helpers/pages_core/admin/localized_form_builder.rb +29 -0
- data/app/helpers/pages_core/form_builder.rb +1 -104
- data/app/helpers/pages_core/labelled_form_builder.rb +109 -0
- data/app/javascript/components/ImageEditor/Form.jsx +7 -1
- data/app/javascript/components/PageTree.jsx +4 -1
- data/app/javascript/components/PageTreeDraggable.jsx +14 -11
- data/app/javascript/components/PageTreeNode.jsx +31 -24
- data/app/javascript/components/RichTextArea.jsx +19 -2
- data/app/models/concerns/pages_core/humanizable_param.rb +14 -8
- data/app/models/concerns/pages_core/page_model/pathable.rb +12 -6
- data/app/views/admin/pages/_edit_images.html.erb +1 -1
- data/app/views/admin/pages/index.html.erb +1 -0
- metadata +24 -21
@@ -6,6 +6,7 @@ module PagesCore
|
|
6
6
|
include PagesCore::Admin::ContentTabsHelper
|
7
7
|
include PagesCore::Admin::DateRangeHelper
|
8
8
|
include PagesCore::Admin::ImageUploadsHelper
|
9
|
+
include PagesCore::Admin::LocalesHelper
|
9
10
|
include PagesCore::Admin::PageJsonHelper
|
10
11
|
include PagesCore::Admin::LabelledFieldHelper
|
11
12
|
include PagesCore::Admin::TagEditorHelper
|
@@ -4,12 +4,13 @@ module PagesCore
|
|
4
4
|
module Admin
|
5
5
|
class FormBuilder < PagesCore::FormBuilder
|
6
6
|
include DynamicImage::Helper
|
7
|
+
include PagesCore::Admin::LocalizedFormBuilder
|
7
8
|
|
8
9
|
def rich_text_area(attr, options = {})
|
9
10
|
@template.rich_text_area_tag(
|
10
11
|
"#{object_name}[#{attr}]",
|
11
12
|
object.send(attr),
|
12
|
-
options
|
13
|
+
localized_form_field_options(attr).merge(options)
|
13
14
|
)
|
14
15
|
end
|
15
16
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PagesCore
|
4
|
+
module Admin
|
5
|
+
module LocalesHelper
|
6
|
+
def locales_with_dir
|
7
|
+
locales = PagesCore.config.locales || {}
|
8
|
+
locales.each_with_object({}) do |(key, name), hash|
|
9
|
+
hash[key] = { name: name, dir: locale_direction(key) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def locale_direction(locale)
|
14
|
+
rtl_locale?(locale) ? "rtl" : "ltr"
|
15
|
+
end
|
16
|
+
|
17
|
+
def rtl_locale?(locale)
|
18
|
+
rtl_locales.include?(locale.to_s)
|
19
|
+
end
|
20
|
+
|
21
|
+
def rtl_locales
|
22
|
+
%w[ar arc dv fa ha he khw ks ku ps ur yi]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PagesCore
|
4
|
+
module Admin
|
5
|
+
module LocalizedFormBuilder
|
6
|
+
include PagesCore::Admin::LocalesHelper
|
7
|
+
|
8
|
+
def text_area(method, options = {})
|
9
|
+
super(method, localized_form_field_options(method).merge(options))
|
10
|
+
end
|
11
|
+
|
12
|
+
def text_field(method, options = {})
|
13
|
+
super(method, localized_form_field_options(method).merge(options))
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def localized_form_field_options(method)
|
19
|
+
unless object.is_a?(LocalizableModel::InstanceMethods) &&
|
20
|
+
object.class.localized_attributes.include?(method.to_sym)
|
21
|
+
return {}
|
22
|
+
end
|
23
|
+
|
24
|
+
{ dir: rtl_locale?(object.locale) ? "rtl" : "ltr",
|
25
|
+
lang: object.locale }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -3,12 +3,7 @@
|
|
3
3
|
module PagesCore
|
4
4
|
class FormBuilder < ActionView::Helpers::FormBuilder
|
5
5
|
include ActionView::Helpers::TagHelper
|
6
|
-
|
7
|
-
def field_with_label(attr, str, label = nil, class_name = nil)
|
8
|
-
classes = ["field", class_name]
|
9
|
-
classes << "field-with-errors" if object.errors[attr].any?
|
10
|
-
tag.div(label_for(attr, label) + str, class: classes.compact.join(" "))
|
11
|
-
end
|
6
|
+
include PagesCore::LabelledFormBuilder
|
12
7
|
|
13
8
|
def image_file_preview(attribute)
|
14
9
|
return "" unless object.send(attribute) &&
|
@@ -22,103 +17,5 @@ module PagesCore
|
|
22
17
|
def image_file_field(attribute, options = {})
|
23
18
|
safe_join [image_file_preview(attribute), file_field(attribute, options)]
|
24
19
|
end
|
25
|
-
|
26
|
-
def label_and_errors(attribute, label_text)
|
27
|
-
return label_text unless object.errors[attribute].any?
|
28
|
-
|
29
|
-
error = tag.span(object.errors[attribute].first, class: "error")
|
30
|
-
safe_join([label_text, error], " ")
|
31
|
-
end
|
32
|
-
|
33
|
-
def label_for(attribute, label_text = nil)
|
34
|
-
label_text ||= object.class.human_attribute_name(attribute)
|
35
|
-
tag.label(label_and_errors(attribute, label_text),
|
36
|
-
for: [object.class.to_s.underscore, attribute].join("_"))
|
37
|
-
end
|
38
|
-
|
39
|
-
def labelled_check_box(
|
40
|
-
attr, label = nil, options = {}, checked = "1", unchecked = "0"
|
41
|
-
)
|
42
|
-
labelled_field(attr, label, options) do |opts|
|
43
|
-
check_box(attr, opts, checked, unchecked)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def labelled_country_select(
|
48
|
-
attr, label = nil, priority = {}, opts = {}, html_opts = {}
|
49
|
-
)
|
50
|
-
if priority.is_a?(Hash)
|
51
|
-
return labelled_field(attr, label, priority) do |options|
|
52
|
-
country_select(attr, options, opts, html_opts)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
labelled_field(attr, label, opts) do |options|
|
56
|
-
country_select(attr, priority, options, html_opts)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def labelled_date_select(attribute, label_text = nil, options = {})
|
61
|
-
labelled_field(attribute, label_text, options) do |opts|
|
62
|
-
date_select(attribute, opts)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def labelled_datetime_select(attribute, label_text = nil, options = {})
|
67
|
-
labelled_field(attribute, label_text, options) do |opts|
|
68
|
-
datetime_select(attribute, opts)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def labelled_file_field(attribute, label_text = nil, options = {})
|
73
|
-
labelled_field(attribute, label_text, options) do |opts|
|
74
|
-
file_field(attribute, opts)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def labelled_image_file_field(attribute, label_text = nil, options = {})
|
79
|
-
labelled_field(attribute, label_text, options) do |opts|
|
80
|
-
image_file_field(attribute, opts)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def labelled_password_field(attribute, label_text = nil, options = {})
|
85
|
-
labelled_field(attribute, label_text, options, "text-field") do |opts|
|
86
|
-
password_field(attribute, opts)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def labelled_select(attribute, choices, label_text = nil, options = {})
|
91
|
-
labelled_field(attribute, label_text, options) do |opts|
|
92
|
-
select(attribute, choices, opts)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def labelled_text_area(attribute, label_text = nil, options = {})
|
97
|
-
labelled_field(attribute, label_text, options, "text-area") do |opts|
|
98
|
-
text_area(attribute, opts)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def labelled_text_field(attribute, label_text = nil, options = {})
|
103
|
-
labelled_field(attribute, label_text, options, "text-field") do |opts|
|
104
|
-
text_field(attribute, opts)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def labelled_time_select(attribute, label_text = nil, options = {})
|
109
|
-
labelled_field(attribute, label_text, options) do |opts|
|
110
|
-
time_select(attribute, opts)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
protected
|
115
|
-
|
116
|
-
def labelled_field(attr, label_text = nil, options = {}, class_name = nil)
|
117
|
-
if label_text.is_a?(Hash) && options == {}
|
118
|
-
options = label_text
|
119
|
-
label_text = nil
|
120
|
-
end
|
121
|
-
field_with_label(attr, yield(options), label_text, class_name)
|
122
|
-
end
|
123
20
|
end
|
124
21
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PagesCore
|
4
|
+
module LabelledFormBuilder
|
5
|
+
def field_with_label(attr, str, label = nil, class_name = nil)
|
6
|
+
classes = ["field", class_name]
|
7
|
+
classes << "field-with-errors" if object.errors[attr].any?
|
8
|
+
tag.div(label_for(attr, label) + str, class: classes.compact.join(" "))
|
9
|
+
end
|
10
|
+
|
11
|
+
def label_and_errors(attribute, label_text)
|
12
|
+
return label_text unless object.errors[attribute].any?
|
13
|
+
|
14
|
+
error = tag.span(object.errors[attribute].first, class: "error")
|
15
|
+
safe_join([label_text, error], " ")
|
16
|
+
end
|
17
|
+
|
18
|
+
def label_for(attribute, label_text = nil)
|
19
|
+
label_text ||= object.class.human_attribute_name(attribute)
|
20
|
+
tag.label(label_and_errors(attribute, label_text),
|
21
|
+
for: [object.class.to_s.underscore, attribute].join("_"))
|
22
|
+
end
|
23
|
+
|
24
|
+
def labelled_check_box(
|
25
|
+
attr, label = nil, options = {}, checked = "1", unchecked = "0"
|
26
|
+
)
|
27
|
+
labelled_field(attr, label, options) do |opts|
|
28
|
+
check_box(attr, opts, checked, unchecked)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def labelled_country_select(
|
33
|
+
attr, label = nil, priority = {}, opts = {}, html_opts = {}
|
34
|
+
)
|
35
|
+
if priority.is_a?(Hash)
|
36
|
+
return labelled_field(attr, label, priority) do |options|
|
37
|
+
country_select(attr, options, opts, html_opts)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
labelled_field(attr, label, opts) do |options|
|
41
|
+
country_select(attr, priority, options, html_opts)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def labelled_date_select(attribute, label_text = nil, options = {})
|
46
|
+
labelled_field(attribute, label_text, options) do |opts|
|
47
|
+
date_select(attribute, opts)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def labelled_datetime_select(attribute, label_text = nil, options = {})
|
52
|
+
labelled_field(attribute, label_text, options) do |opts|
|
53
|
+
datetime_select(attribute, opts)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def labelled_file_field(attribute, label_text = nil, options = {})
|
58
|
+
labelled_field(attribute, label_text, options) do |opts|
|
59
|
+
file_field(attribute, opts)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def labelled_image_file_field(attribute, label_text = nil, options = {})
|
64
|
+
labelled_field(attribute, label_text, options) do |opts|
|
65
|
+
image_file_field(attribute, opts)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def labelled_password_field(attribute, label_text = nil, options = {})
|
70
|
+
labelled_field(attribute, label_text, options, "text-field") do |opts|
|
71
|
+
password_field(attribute, opts)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def labelled_select(attribute, choices, label_text = nil, options = {})
|
76
|
+
labelled_field(attribute, label_text, options) do |opts|
|
77
|
+
select(attribute, choices, opts)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def labelled_text_area(attribute, label_text = nil, options = {})
|
82
|
+
labelled_field(attribute, label_text, options, "text-area") do |opts|
|
83
|
+
text_area(attribute, opts)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def labelled_text_field(attribute, label_text = nil, options = {})
|
88
|
+
labelled_field(attribute, label_text, options, "text-field") do |opts|
|
89
|
+
text_field(attribute, opts)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def labelled_time_select(attribute, label_text = nil, options = {})
|
94
|
+
labelled_field(attribute, label_text, options) do |opts|
|
95
|
+
time_select(attribute, opts)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
protected
|
100
|
+
|
101
|
+
def labelled_field(attr, label_text = nil, options = {}, class_name = nil)
|
102
|
+
if label_text.is_a?(Hash) && options == {}
|
103
|
+
options = label_text
|
104
|
+
label_text = nil
|
105
|
+
end
|
106
|
+
field_with_label(attr, yield(options), label_text, class_name)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -19,6 +19,8 @@ export default function Form(props) {
|
|
19
19
|
props.setLocale(evt.target.value);
|
20
20
|
};
|
21
21
|
|
22
|
+
const inputDir = locales[locale].dir || "ltr";
|
23
|
+
|
22
24
|
return (
|
23
25
|
<form>
|
24
26
|
<div className="field embed-code">
|
@@ -44,7 +46,7 @@ export default function Form(props) {
|
|
44
46
|
onChange={handleChangeLocale}>
|
45
47
|
{Object.keys(locales).map(key => (
|
46
48
|
<option key={`locale-${key}`} value={key}>
|
47
|
-
{locales[key]}
|
49
|
+
{locales[key].name}
|
48
50
|
</option>
|
49
51
|
))}
|
50
52
|
</select>
|
@@ -59,6 +61,8 @@ export default function Form(props) {
|
|
59
61
|
</span>
|
60
62
|
<textarea
|
61
63
|
className="alternative"
|
64
|
+
lang={locale}
|
65
|
+
dir={inputDir}
|
62
66
|
value={alternative[locale] || ""}
|
63
67
|
onChange={e => props.updateLocalization("alternative", e.target.value)} />
|
64
68
|
</div>
|
@@ -68,6 +72,8 @@ export default function Form(props) {
|
|
68
72
|
Caption
|
69
73
|
</label>
|
70
74
|
<textarea
|
75
|
+
lang={locale}
|
76
|
+
dir={inputDir}
|
71
77
|
onChange={e => props.updateLocalization("caption", e.target.value)}
|
72
78
|
value={caption[locale] || ""}
|
73
79
|
className="caption" />
|
@@ -139,7 +139,9 @@ export default class PageTree extends React.Component {
|
|
139
139
|
movedPage={this.movedPage}
|
140
140
|
toggleCollapsed={this.toggleCollapsed}
|
141
141
|
updatePage={this.updatePage}
|
142
|
-
updateTree={this.updateTree}
|
142
|
+
updateTree={this.updateTree}
|
143
|
+
locale={this.props.locale}
|
144
|
+
dir={this.props.dir} />
|
143
145
|
);
|
144
146
|
}
|
145
147
|
|
@@ -189,5 +191,6 @@ export default class PageTree extends React.Component {
|
|
189
191
|
PageTree.propTypes = {
|
190
192
|
pages: PropTypes.array,
|
191
193
|
locale: PropTypes.string,
|
194
|
+
dir: PropTypes.string,
|
192
195
|
permissions: PropTypes.array
|
193
196
|
};
|
@@ -75,7 +75,7 @@ export default class PageTreeDraggable extends React.Component {
|
|
75
75
|
}
|
76
76
|
|
77
77
|
render() {
|
78
|
-
|
78
|
+
const { tree, dir, locale } = this.props;
|
79
79
|
var dragging = this.state.dragging;
|
80
80
|
|
81
81
|
if (!tree) {
|
@@ -90,16 +90,17 @@ export default class PageTreeDraggable extends React.Component {
|
|
90
90
|
<div className="page-tree">
|
91
91
|
{this.getDraggingDom()}
|
92
92
|
<PageTreeNode
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
93
|
+
tree={tree}
|
94
|
+
index={root}
|
95
|
+
key={root.id}
|
96
|
+
paddingLeft={this.props.paddingLeft}
|
97
|
+
addChild={id => this.addChild(id)}
|
98
|
+
onDragStart={(id, dom, e) => this.dragStart(id, dom, e)}
|
99
|
+
onCollapse={nodeId => this.toggleCollapse(nodeId)}
|
100
|
+
updatePage={(idx, attrs) => this.updatePage(idx, attrs)}
|
101
|
+
dragging={dragging && dragging.id}
|
102
|
+
dir={dir}
|
103
|
+
locale={locale} />
|
103
104
|
</div>
|
104
105
|
);
|
105
106
|
}
|
@@ -303,4 +304,6 @@ PageTreeDraggable.propTypes = {
|
|
303
304
|
paddingLeft: PropTypes.number,
|
304
305
|
updatePage: PropTypes.func,
|
305
306
|
updateTree: PropTypes.func,
|
307
|
+
locale: PropTypes.string,
|
308
|
+
dir: PropTypes.string
|
306
309
|
};
|
@@ -125,9 +125,7 @@ export default class PageTreeNode extends React.Component {
|
|
125
125
|
}
|
126
126
|
|
127
127
|
childNodes() {
|
128
|
-
|
129
|
-
let tree = this.props.tree;
|
130
|
-
let dragging = this.props.dragging;
|
128
|
+
const { index, tree, dragging, dir, locale } = this.props;
|
131
129
|
|
132
130
|
if (index.children && index.children.length && !index.node.collapsed) {
|
133
131
|
var childrenStyles = {};
|
@@ -139,21 +137,22 @@ export default class PageTreeNode extends React.Component {
|
|
139
137
|
return (
|
140
138
|
<div className="children" style={childrenStyles}>
|
141
139
|
{index.children.map((child) => {
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
140
|
+
var childIndex = tree.getIndex(child);
|
141
|
+
return (
|
142
|
+
<PageTreeNode
|
143
|
+
tree={tree}
|
144
|
+
index={childIndex}
|
145
|
+
key={childIndex.id}
|
146
|
+
dragging={dragging}
|
147
|
+
paddingLeft={this.props.paddingLeft}
|
148
|
+
addChild={this.props.addChild}
|
149
|
+
onCollapse={this.props.onCollapse}
|
150
|
+
onDragStart={this.props.onDragStart}
|
151
|
+
updatePage={this.props.updatePage}
|
152
|
+
dir={dir}
|
153
|
+
locale={locale} />
|
154
|
+
);
|
155
|
+
})}
|
157
156
|
</div>
|
158
157
|
);
|
159
158
|
}
|
@@ -232,11 +231,14 @@ export default class PageTreeNode extends React.Component {
|
|
232
231
|
}
|
233
232
|
|
234
233
|
pageName() {
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
234
|
+
const name = this.node().name || <i className="untitled">Untitled</i>;
|
235
|
+
|
236
|
+
return(
|
237
|
+
<span dir={this.props.dir}
|
238
|
+
lang={this.props.locale}>
|
239
|
+
{name}
|
240
|
+
</span>
|
241
|
+
);
|
240
242
|
}
|
241
243
|
|
242
244
|
render() {
|
@@ -278,6 +280,7 @@ export default class PageTreeNode extends React.Component {
|
|
278
280
|
}
|
279
281
|
|
280
282
|
renderEditNode() {
|
283
|
+
const { dir, locale } = this.props;
|
281
284
|
let self = this;
|
282
285
|
|
283
286
|
let handleNameChange = function(event) {
|
@@ -303,6 +306,8 @@ export default class PageTreeNode extends React.Component {
|
|
303
306
|
<form onSubmit={performEdit}>
|
304
307
|
<input type="text"
|
305
308
|
value={this.state.newName}
|
309
|
+
dir={dir}
|
310
|
+
lang={locale}
|
306
311
|
autoFocus
|
307
312
|
onChange={handleNameChange} />
|
308
313
|
<button className="save" type="submit">
|
@@ -407,5 +412,7 @@ PageTreeNode.propTypes = {
|
|
407
412
|
onDragStart: PropTypes.func,
|
408
413
|
paddingLeft: PropTypes.number,
|
409
414
|
tree: PropTypes.object,
|
410
|
-
updatePage: PropTypes.func
|
415
|
+
updatePage: PropTypes.func,
|
416
|
+
locale: PropTypes.string,
|
417
|
+
dir: PropTypes.string,
|
411
418
|
};
|
@@ -129,6 +129,20 @@ export default class RichTextArea extends React.Component {
|
|
129
129
|
}
|
130
130
|
}
|
131
131
|
|
132
|
+
localeOptions() {
|
133
|
+
let opts = {};
|
134
|
+
|
135
|
+
if (this.props.lang) {
|
136
|
+
opts.lang = this.props.lang;
|
137
|
+
}
|
138
|
+
|
139
|
+
if (this.props.dir) {
|
140
|
+
opts.dir = this.props.dir;
|
141
|
+
}
|
142
|
+
|
143
|
+
return opts;
|
144
|
+
}
|
145
|
+
|
132
146
|
relativeUrl(str) {
|
133
147
|
let url = null;
|
134
148
|
|
@@ -177,7 +191,8 @@ export default class RichTextArea extends React.Component {
|
|
177
191
|
value={value}
|
178
192
|
rows={rows}
|
179
193
|
onChange={this.handleChange}
|
180
|
-
onKeyDown={this.handleKeyPress}
|
194
|
+
onKeyDown={this.handleKeyPress}
|
195
|
+
{...this.localeOptions()} />
|
181
196
|
</div>
|
182
197
|
);
|
183
198
|
}
|
@@ -209,5 +224,7 @@ RichTextArea.propTypes = {
|
|
209
224
|
name: PropTypes.string,
|
210
225
|
value: PropTypes.string,
|
211
226
|
rows: PropTypes.number,
|
212
|
-
simple: PropTypes.bool
|
227
|
+
simple: PropTypes.bool,
|
228
|
+
lang: PropTypes.string,
|
229
|
+
dir: PropTypes.string
|
213
230
|
};
|
@@ -6,15 +6,21 @@ module PagesCore
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
def humanized_param(slug)
|
9
|
-
|
9
|
+
safe_slug = safe_humanized_param(slug)
|
10
|
+
return id.to_s if safe_slug.blank?
|
10
11
|
|
11
|
-
"#{id}
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
"#{id}-#{safe_slug}"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def safe_humanized_param(str)
|
18
|
+
transliterate(str.to_s).downcase
|
19
|
+
.gsub(/[\[{]/, "(")
|
20
|
+
.gsub(/}\]/, ")")
|
21
|
+
.gsub(/[^[[:alnum:]]()-]+/, "-")
|
22
|
+
.gsub(/-{2,}/, "-")
|
23
|
+
.gsub(/(^-|-$)/, "")
|
18
24
|
end
|
19
25
|
end
|
20
26
|
end
|
@@ -24,7 +24,7 @@ module PagesCore
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def ensure_path_segment
|
27
|
-
return if deleted? || path_segment? ||
|
27
|
+
return if deleted? || path_segment? || generated_path_segment.blank?
|
28
28
|
|
29
29
|
segment = generated_path_segment
|
30
30
|
segment = "#{segment}-#{id}" if path_collision?(segment)
|
@@ -59,11 +59,8 @@ module PagesCore
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def generated_path_segment
|
62
|
-
transliterated_name.
|
63
|
-
|
64
|
-
.gsub(/(^-|-$)/, "")
|
65
|
-
.mb_chars
|
66
|
-
.downcase
|
62
|
+
safe_path_segment(transliterated_name).presence ||
|
63
|
+
safe_path_segment(unique_name)
|
67
64
|
end
|
68
65
|
|
69
66
|
def page_path_matches_routes?(page_path)
|
@@ -98,6 +95,15 @@ module PagesCore
|
|
98
95
|
false
|
99
96
|
end
|
100
97
|
|
98
|
+
def safe_path_segment(str)
|
99
|
+
str.to_s
|
100
|
+
.gsub(/[^[[:alnum:]]-_]+/, "-")
|
101
|
+
.gsub(/-{2,}/, "-")
|
102
|
+
.gsub(/(^-|-$)/, "")
|
103
|
+
.mb_chars
|
104
|
+
.downcase
|
105
|
+
end
|
106
|
+
|
101
107
|
def sibling_path_segments
|
102
108
|
siblings = if parent
|
103
109
|
parent.children
|