ants 0.3.5 → 0.3.9
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/Gemfile.lock +6 -6
- data/README.md +26 -3
- data/app/assets/javascripts/ants/admin_users.coffee +102 -62
- data/app/assets/javascripts/ants/content.coffee +109 -0
- data/app/assets/javascripts/ants/menu.coffee +32 -22
- data/app/assets/javascripts/ants/meta.coffee +20 -25
- data/app/assets/javascripts/ants/slug.coffee +6 -8
- data/app/assets/javascripts/ants.coffee +4 -0
- data/app/assets/stylesheets/ants/content.scss +113 -0
- data/app/assets/stylesheets/ants/menu.scss +56 -0
- data/app/assets/stylesheets/ants.scss +2 -1
- data/app/controllers/admin/admin_users_controller.rb +26 -0
- data/app/controllers/redirects_controller.rb +0 -2
- data/app/helpers/content_helper.rb +18 -7
- data/app/helpers/menu_helper.rb +2 -2
- data/app/models/menu_link.rb +6 -1
- data/app/models/redirect.rb +1 -1
- data/lib/ants/routing.rb +27 -0
- data/lib/ants/version.rb +1 -1
- data/lib/ants.rb +4 -5
- data/lib/concerns/ants/content.rb +2 -2
- data/lib/concerns/ants/featurable.rb +4 -14
- data/lib/concerns/ants/orderable.rb +5 -16
- data/lib/concerns/ants/orderable_reverse.rb +24 -0
- data/lib/concerns/ants/scheduled.rb +17 -0
- metadata +8 -4
- data/app/assets/stylesheets/ants/redirects.scss +0 -12
- data/lib/concerns/ants/publication.rb +0 -79
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a28c13709ccfad3dd8b3f61cb3e7928d57d85af
|
4
|
+
data.tar.gz: 1753c8d2ce2aa021478cb0ecba65650f3c6bb3e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d76803eb7462cdd16f05914fd460faac70389f8077eba1aa3168d0bf378d40f78d6077f933f347e20a3454a5518361a04c5eec90257e7c6a71855aa40c8d3048
|
7
|
+
data.tar.gz: 511bc1e6f8a2dc69c193f585c26e671a6538d184bcdd6a38c376eda44c1883b1cd73fc1791eea0e4350f4b31b59937eb54c5a1c6c844d23d4d446df88fc6ffce
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ants (0.3.
|
4
|
+
ants (0.3.9)
|
5
5
|
devise
|
6
6
|
mongoid (>= 4.0)
|
7
7
|
mongoid-slug (>= 4.0.0)
|
@@ -46,7 +46,7 @@ GEM
|
|
46
46
|
tzinfo (~> 1.1)
|
47
47
|
arel (6.0.3)
|
48
48
|
bcrypt (3.1.10)
|
49
|
-
bson (
|
49
|
+
bson (4.0.0)
|
50
50
|
builder (3.2.2)
|
51
51
|
coveralls (0.8.2)
|
52
52
|
json (~> 1.8)
|
@@ -54,7 +54,7 @@ GEM
|
|
54
54
|
simplecov (~> 0.10.0)
|
55
55
|
term-ansicolor (~> 1.3)
|
56
56
|
thor (~> 0.19.1)
|
57
|
-
devise (3.5.
|
57
|
+
devise (3.5.3)
|
58
58
|
bcrypt (~> 3.0)
|
59
59
|
orm_adapter (~> 0.1)
|
60
60
|
railties (>= 3.2.6, < 5)
|
@@ -79,8 +79,8 @@ GEM
|
|
79
79
|
mime-types (2.6.1)
|
80
80
|
mini_portile (0.6.2)
|
81
81
|
minitest (5.8.0)
|
82
|
-
mongo (2.
|
83
|
-
bson (~>
|
82
|
+
mongo (2.2.0)
|
83
|
+
bson (~> 4.0)
|
84
84
|
mongoid (5.0.1)
|
85
85
|
activemodel (~> 4.0)
|
86
86
|
mongo (~> 2.1)
|
@@ -168,7 +168,7 @@ GEM
|
|
168
168
|
unf (0.1.4)
|
169
169
|
unf_ext
|
170
170
|
unf_ext (0.0.7.1)
|
171
|
-
warden (1.2.
|
171
|
+
warden (1.2.4)
|
172
172
|
rack (>= 1.0)
|
173
173
|
|
174
174
|
PLATFORMS
|
data/README.md
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
-
|
2
1
|
Ants
|
3
|
-
|
2
|
+
===============================================================================
|
4
3
|
[](http://badge.fury.io/gh/slate-studio%2Fants)
|
5
4
|
[](https://travis-ci.org/slate-studio/ants)
|
6
5
|
[](https://codeclimate.com/github/slate-studio/ants)
|
7
6
|
[](https://coveralls.io/r/slate-studio/ants)
|
8
7
|
|
9
|
-
Collection of concerns and helpers for Rails + Mongoid + Character
|
8
|
+
Collection of concerns and helpers for `Rails` + `Mongoid` + `Character` stack.
|
9
|
+
|
10
|
+
|
11
|
+
### Id
|
10
12
|
|
11
13
|
|
12
14
|
### Orderable
|
@@ -25,6 +27,8 @@ Usage:
|
|
25
27
|
ModelClass.all.set(_position: 1000)
|
26
28
|
```
|
27
29
|
|
30
|
+
### OrderableReverse
|
31
|
+
|
28
32
|
|
29
33
|
### Meta
|
30
34
|
|
@@ -42,6 +46,9 @@ Usage:
|
|
42
46
|
```
|
43
47
|
|
44
48
|
|
49
|
+
### Featurable
|
50
|
+
|
51
|
+
|
45
52
|
### Hideable
|
46
53
|
|
47
54
|
When you need to hide some documents from public or often used as draft analogue:
|
@@ -67,6 +74,8 @@ Helpers:
|
|
67
74
|
### Slug
|
68
75
|
|
69
76
|
|
77
|
+
### Scheduled
|
78
|
+
|
70
79
|
|
71
80
|
### Sorted Relations
|
72
81
|
|
@@ -110,5 +119,19 @@ Helpers:
|
|
110
119
|
- `_document_versions`
|
111
120
|
|
112
121
|
|
122
|
+
### AdminUsers
|
123
|
+
|
124
|
+
|
125
|
+
### Settings
|
126
|
+
|
127
|
+
|
128
|
+
### Redirects
|
129
|
+
|
130
|
+
|
131
|
+
### Content
|
132
|
+
|
133
|
+
|
134
|
+
### Menus
|
113
135
|
|
114
136
|
|
137
|
+
### Profile
|
@@ -1,63 +1,103 @@
|
|
1
1
|
class @AntsAdminUsers
|
2
|
-
constructor: (title='Administrators', apiPath='/admin') ->
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
2
|
+
constructor: (@title='Administrators', @apiPath='/admin') ->
|
3
|
+
@showWithParent = true
|
4
|
+
|
5
|
+
@arrayStore = new RailsArrayStore
|
6
|
+
resource: "admin_user"
|
7
|
+
path: "#{ @apiPath }/admin_users"
|
8
|
+
sortBy: "name"
|
9
|
+
searchable: true
|
10
|
+
|
11
|
+
@formSchema =
|
12
|
+
details_panel:
|
13
|
+
type: "group"
|
14
|
+
groupClass: "group-panel"
|
15
|
+
title: "Profile"
|
16
|
+
inputs:
|
17
|
+
name:
|
18
|
+
type: "string"
|
19
|
+
required: true
|
20
|
+
label: "Name"
|
21
|
+
placeholder: "Enter users full name."
|
22
|
+
onInitialize: (input) => @_disable_input_for_edit(input)
|
23
|
+
|
24
|
+
email:
|
25
|
+
type: "string"
|
26
|
+
required: true
|
27
|
+
placeholder: "Enter users email address used to sign in."
|
28
|
+
|
29
|
+
onInitialize: (input) => @_disable_input_for_edit(input)
|
30
|
+
|
31
|
+
password_panel:
|
32
|
+
type: "group"
|
33
|
+
groupClass: "group-panel"
|
34
|
+
title: "Password"
|
35
|
+
inputs:
|
36
|
+
password:
|
37
|
+
type: "password"
|
38
|
+
required: true
|
39
|
+
placeholder: "Please use a complex password."
|
40
|
+
onInitialize: (input) =>
|
41
|
+
@_transform_password_to_change_password_on_edit(input)
|
42
|
+
|
43
|
+
@onEditShow = (view) =>
|
44
|
+
@_hide_save_button(view)
|
45
|
+
|
46
|
+
# PRIVATE ===================================================================
|
47
|
+
_hide_save_button: (view) ->
|
48
|
+
view.$saveBtn.hide()
|
49
|
+
|
50
|
+
_disable_input_for_edit: (input) ->
|
51
|
+
if input.object
|
52
|
+
input.$el.removeClass "input-required"
|
53
|
+
input.config.disabled = true
|
54
|
+
input._add_disabled()
|
55
|
+
|
56
|
+
_transform_password_to_change_password_on_edit: (input) ->
|
57
|
+
if input.object
|
58
|
+
input.$el.prev().children(".group-title").html("Change Password")
|
59
|
+
input.$el.removeClass "input-required"
|
60
|
+
input.$labelTitle.html "New password"
|
61
|
+
input.config.placeholder = 'Enter your new password and hit "Update".'
|
62
|
+
input._add_placeholder()
|
63
|
+
|
64
|
+
@_add_password_update_action(input)
|
65
|
+
|
66
|
+
_add_password_update_action: (input) ->
|
67
|
+
input.$actions =$ "<span class='input-actions'></span>"
|
68
|
+
input.$updateBtn =$ "<button>Update</button>"
|
69
|
+
input.$label.append input.$actions
|
70
|
+
input.$actions.append input.$updateBtn
|
71
|
+
|
72
|
+
input.$updateBtn.on "click", (e) =>
|
73
|
+
@_update_password(input)
|
74
|
+
|
75
|
+
_update_password: (input) ->
|
76
|
+
chr.module.view.showSpinner()
|
77
|
+
input.hideErrorMessage()
|
78
|
+
|
79
|
+
resourcePath = "#{ @apiPath }/admin_users"
|
80
|
+
resourceId = input.object._id
|
81
|
+
action = "update_password"
|
82
|
+
url = "#{resourcePath}/#{resourceId}/#{action}.json"
|
83
|
+
|
84
|
+
data =
|
85
|
+
password: input.$input.val()
|
86
|
+
|
87
|
+
$.ajax
|
88
|
+
type: 'PATCH'
|
89
|
+
url: url
|
90
|
+
data: data
|
91
|
+
|
92
|
+
success: (response) ->
|
93
|
+
chr.module.view.hideSpinner()
|
94
|
+
input.$input.val("")
|
95
|
+
chr.showNotification "Password has been updated."
|
96
|
+
|
97
|
+
error: (jqXHR) ->
|
98
|
+
if jqXHR.responseJSON
|
99
|
+
firstErrorMessage = jqXHR.responseJSON[input.config.fieldName][0]
|
100
|
+
input.showErrorMessage(firstErrorMessage)
|
101
|
+
|
102
|
+
chr.showError "Your password were not updated. Please fix error and try again."
|
103
|
+
chr.module.view.hideSpinner()
|
@@ -0,0 +1,109 @@
|
|
1
|
+
class @AntsContent
|
2
|
+
constructor: (@resourceName="Document") ->
|
3
|
+
@disableDelete = true
|
4
|
+
|
5
|
+
@viewTabs =
|
6
|
+
editor: @resourceName
|
7
|
+
settings: "Options"
|
8
|
+
|
9
|
+
@formSchema =
|
10
|
+
editor:
|
11
|
+
type: "group"
|
12
|
+
groupClass: "group-content-editor"
|
13
|
+
onInitialize: (form, group) =>
|
14
|
+
@_add_actions(form, group)
|
15
|
+
inputs:
|
16
|
+
title:
|
17
|
+
type: "string"
|
18
|
+
placeholder: "#{@resourceName} Title"
|
19
|
+
slug: new AntsSlugInput("#{location.origin}/")
|
20
|
+
body_markdown:
|
21
|
+
type: "markdown"
|
22
|
+
label: "Content"
|
23
|
+
htmlFieldName: "body_html"
|
24
|
+
placeholder: "Content"
|
25
|
+
|
26
|
+
settings:
|
27
|
+
type: "group"
|
28
|
+
groupClass: "group-content-settings"
|
29
|
+
inputs:
|
30
|
+
sharing_panel:
|
31
|
+
type: "group"
|
32
|
+
groupClass: "group-panel"
|
33
|
+
title: "Sharing"
|
34
|
+
inputs:
|
35
|
+
@_meta_inputs()
|
36
|
+
|
37
|
+
general_panel:
|
38
|
+
type: "group"
|
39
|
+
groupClass: "group-panel group-content-settings"
|
40
|
+
inputs:
|
41
|
+
hidden:
|
42
|
+
type: "switch"
|
43
|
+
label: "Draft"
|
44
|
+
default: true
|
45
|
+
|
46
|
+
|
47
|
+
@onEditShow = (view) =>
|
48
|
+
@_update_slug_label(view)
|
49
|
+
|
50
|
+
@onSaveSuccess = (view) =>
|
51
|
+
@_update_slug_label(view)
|
52
|
+
@_update_preview_href(view.form)
|
53
|
+
|
54
|
+
# PRIVATE ===================================================================
|
55
|
+
|
56
|
+
_update_slug_label: (view) ->
|
57
|
+
if @_path
|
58
|
+
path = @_path(view.object)
|
59
|
+
view.form.inputs.slug.$label.text(path)
|
60
|
+
|
61
|
+
_update_preview_href: (form) ->
|
62
|
+
if @_preview_url
|
63
|
+
object = form.object
|
64
|
+
$button = form.groups[0].$previewBtn
|
65
|
+
preview_url = @_preview_url(object)
|
66
|
+
$button.attr("href", preview_url)
|
67
|
+
|
68
|
+
_add_delete_button: (form, group) ->
|
69
|
+
group.$deleteBtn =$ """
|
70
|
+
<a href='#' target='_blank' class='content-delete'>
|
71
|
+
#{Icons.delete}
|
72
|
+
</a>"""
|
73
|
+
group.$actions.prepend group.$deleteBtn
|
74
|
+
group.$deleteBtn.on 'click', (e) -> chr.module.view._delete(e)
|
75
|
+
|
76
|
+
_add_preview_button: (form, group) ->
|
77
|
+
group.$previewBtn =$ """
|
78
|
+
<a href='#' target='_blank' class='content-preview'>
|
79
|
+
#{Icons.preview}
|
80
|
+
</a>"""
|
81
|
+
group.$actions.prepend group.$previewBtn
|
82
|
+
if form.object
|
83
|
+
@_update_preview_href(form)
|
84
|
+
|
85
|
+
_add_actions: (form, group) ->
|
86
|
+
group.$actions =$ "<div class='group-content-editor-actions'/>"
|
87
|
+
group.$el.prepend group.$actions
|
88
|
+
|
89
|
+
@_add_delete_button(form, group)
|
90
|
+
@_add_preview_button(form, group)
|
91
|
+
|
92
|
+
_meta_inputs: ->
|
93
|
+
opengraph_image_url:
|
94
|
+
name: '_opengraph_image_url'
|
95
|
+
type: 'loft-image'
|
96
|
+
label: 'Image'
|
97
|
+
fullsizePreview: true
|
98
|
+
|
99
|
+
meta_title:
|
100
|
+
name: "_meta_title"
|
101
|
+
type: "string"
|
102
|
+
label: false
|
103
|
+
placeholder: "Title"
|
104
|
+
|
105
|
+
meta_description:
|
106
|
+
name: "_meta_description"
|
107
|
+
type: "text"
|
108
|
+
label: false
|
109
|
+
placeholder: "Content summary"
|
@@ -1,28 +1,38 @@
|
|
1
1
|
class @AntsMenu
|
2
|
-
constructor: (title, apiPath) ->
|
3
|
-
|
4
|
-
|
2
|
+
constructor: (@title, @apiPath) ->
|
3
|
+
@objectStore = new RailsObjectStore
|
4
|
+
resource: 'menu'
|
5
|
+
path: "#{ @apiPath }"
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
@formSchema =
|
8
|
+
links:
|
9
|
+
type: 'documents'
|
10
|
+
newButtonLabel: 'Add a Link'
|
11
|
+
sortBy: '_position'
|
12
|
+
label: false
|
13
|
+
formSchema:
|
14
|
+
title:
|
15
|
+
type: "string"
|
16
|
+
placeholder: "Title"
|
10
17
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
formSchema:
|
17
|
-
title:
|
18
|
-
type: 'string'
|
18
|
+
url:
|
19
|
+
type: "url"
|
20
|
+
placeholder: "Path or URL, e.g. /about"
|
21
|
+
onInitialize: (input) =>
|
22
|
+
@_add_preview_action(input)
|
19
23
|
|
20
|
-
|
21
|
-
|
24
|
+
@onViewShow = (view) ->
|
25
|
+
view.$el.addClass "view-menu"
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
label: 'Open in a new tab'
|
27
|
+
_add_preview_action: (input) ->
|
28
|
+
input.$actions =$ "<span class='input-actions'></span>"
|
29
|
+
input.$el.append input.$actions
|
27
30
|
|
28
|
-
|
31
|
+
input.$previewBtn =$ """<a href='#{input.value}' target='_blank'>
|
32
|
+
#{Icons.preview}
|
33
|
+
</a>"""
|
34
|
+
input.$actions.append input.$previewBtn
|
35
|
+
|
36
|
+
input.$input.on "change", (e) ->
|
37
|
+
link = input.$input.val()
|
38
|
+
input.$previewBtn.attr "href", link
|
@@ -1,29 +1,24 @@
|
|
1
1
|
class @AntsMetaGroup
|
2
|
-
constructor:
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
label: 'Meta Title'
|
9
|
-
limit: 60
|
10
|
-
placeholder: "Accurate and concise description of a page's content"
|
2
|
+
constructor: ->
|
3
|
+
@type = 'group'
|
4
|
+
@inputs =
|
5
|
+
_opengraph_image_url:
|
6
|
+
type: 'loft-image'
|
7
|
+
label: 'Image'
|
11
8
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
9
|
+
_meta_title:
|
10
|
+
type: 'string'
|
11
|
+
label: 'Title'
|
12
|
+
limit: 60
|
13
|
+
placeholder: "Accurate and concise description of a page's content"
|
17
14
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
_meta_description:
|
16
|
+
type: 'text'
|
17
|
+
label: 'Description'
|
18
|
+
limit: 200
|
19
|
+
placeholder: 'Advertise content with short summary'
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
$.extend(config, extra_config)
|
28
|
-
|
29
|
-
return config
|
21
|
+
_meta_keywords:
|
22
|
+
type: 'string'
|
23
|
+
label: 'Keywords'
|
24
|
+
placeholder: 'e.g. keyword1, keyword2, keyword3'
|
@@ -1,15 +1,13 @@
|
|
1
1
|
class @AntsSlugInput
|
2
|
-
constructor: (
|
2
|
+
constructor: (label="Slug") ->
|
3
3
|
config =
|
4
|
-
type:
|
5
|
-
name:
|
6
|
-
label:
|
7
|
-
placeholder:
|
4
|
+
type: "string"
|
5
|
+
name: "_slug"
|
6
|
+
label: label
|
7
|
+
placeholder: " "
|
8
8
|
onInitialize: (input) ->
|
9
9
|
if input.object && input.object._slugs.length
|
10
10
|
defaultSlug = input.object._slugs[0]
|
11
|
-
input.$input.attr
|
12
|
-
|
13
|
-
$.extend(config, extra_config)
|
11
|
+
input.$input.attr "placeholder", defaultSlug
|
14
12
|
|
15
13
|
return config
|
@@ -0,0 +1,113 @@
|
|
1
|
+
.group-content-settings {
|
2
|
+
.sharing_panel {
|
3
|
+
@include position(relative);
|
4
|
+
}
|
5
|
+
.input-_meta_title {
|
6
|
+
@include no-bottom-border;
|
7
|
+
padding: 0.75em 1em 0 6.25em;
|
8
|
+
.label { display: none; }
|
9
|
+
input { font-weight: $medium; }
|
10
|
+
}
|
11
|
+
.input-_meta_description {
|
12
|
+
@include no-bottom-border;
|
13
|
+
min-height: 63px;
|
14
|
+
padding: 0.25em 1em 1em 6.25em;
|
15
|
+
.label { display: none; }
|
16
|
+
textarea { font-size: 0.9em; }
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
.group-content-editor {
|
21
|
+
@include position(relative);
|
22
|
+
min-height: 16em;
|
23
|
+
background-color: $white-color;
|
24
|
+
|
25
|
+
.input-_slug {
|
26
|
+
@include no-bottom-border;
|
27
|
+
padding-bottom: 0;
|
28
|
+
}
|
29
|
+
|
30
|
+
.input-title {
|
31
|
+
@include no-bottom-border;
|
32
|
+
padding-bottom: 0;
|
33
|
+
padding-right: 4.5em;
|
34
|
+
font-weight: $medium;
|
35
|
+
|
36
|
+
&.error:after {
|
37
|
+
background-color: $assertive-color;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
.input-title,
|
42
|
+
.input-body_markdown {
|
43
|
+
padding-top: 1em;
|
44
|
+
|
45
|
+
.label {
|
46
|
+
display: none;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
.input-_slug {
|
51
|
+
padding-top: 0;
|
52
|
+
opacity: 0.5;
|
53
|
+
|
54
|
+
.label {
|
55
|
+
display: inline-block;
|
56
|
+
font-size: .8em;
|
57
|
+
cursor: pointer;
|
58
|
+
}
|
59
|
+
|
60
|
+
.error-message {
|
61
|
+
display: none;
|
62
|
+
}
|
63
|
+
|
64
|
+
input {
|
65
|
+
display: inline-block;
|
66
|
+
width: 18em;
|
67
|
+
font-size: .8em;
|
68
|
+
padding: 0;
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
.view-new .group-content-editor-actions {
|
74
|
+
display: none;
|
75
|
+
}
|
76
|
+
|
77
|
+
.content-delete {
|
78
|
+
i { @include position(relative, -1px null null null); }
|
79
|
+
&:hover { color: $assertive-color; }
|
80
|
+
}
|
81
|
+
|
82
|
+
.group-content-editor-actions {
|
83
|
+
@include position(absolute, 0.5em 0.5em null null);
|
84
|
+
z-index: 1;
|
85
|
+
|
86
|
+
a {
|
87
|
+
display: inline-block;
|
88
|
+
float: right;
|
89
|
+
width: 2em;
|
90
|
+
height: 2em;
|
91
|
+
line-height: 2;
|
92
|
+
text-align: center;
|
93
|
+
color: $base-font-color;
|
94
|
+
opacity: 0.25;
|
95
|
+
|
96
|
+
&:hover { opacity: 0.75; }
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
/* Tablet ------------------------------------------------------------------ */
|
101
|
+
@media #{$tablet} {
|
102
|
+
.group-content-settings {
|
103
|
+
.sharing_panel {
|
104
|
+
min-height: 17.8em;
|
105
|
+
}
|
106
|
+
.input-_meta_title {
|
107
|
+
padding-left: 15.25em;
|
108
|
+
}
|
109
|
+
.input-_meta_description {
|
110
|
+
padding-left: 15.25em;
|
111
|
+
}
|
112
|
+
}
|
113
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
.view-menu {
|
2
|
+
.nested-forms {
|
3
|
+
padding-top: 1.5em;
|
4
|
+
|
5
|
+
&> .label {
|
6
|
+
display: none;
|
7
|
+
}
|
8
|
+
}
|
9
|
+
|
10
|
+
.input-title {
|
11
|
+
padding: 0.75em 1em 0.75em;
|
12
|
+
|
13
|
+
.label {
|
14
|
+
display: none;
|
15
|
+
}
|
16
|
+
|
17
|
+
input {
|
18
|
+
font-weight: $medium;
|
19
|
+
padding-right: 1em;
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
.input-url {
|
24
|
+
padding: .65em 1em 0.75em;
|
25
|
+
|
26
|
+
.label-title { display: none; }
|
27
|
+
.label { height: 0; }
|
28
|
+
|
29
|
+
.input-actions {
|
30
|
+
top: 0.6em;
|
31
|
+
right: 1.1em;
|
32
|
+
|
33
|
+
a {
|
34
|
+
@include position(relative, 0.75em -2.8em null null);
|
35
|
+
color: $secondary-font-color;
|
36
|
+
|
37
|
+
&:hover { color: $base-font-color; }
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
input {
|
42
|
+
font-size: 0.9em;
|
43
|
+
padding-right: 5em;
|
44
|
+
color: $secondary-font-color;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
/* Tablet ------------------------------------------------------------------ */
|
50
|
+
@media #{$tablet} {
|
51
|
+
.view-menu {
|
52
|
+
.nested-forms {
|
53
|
+
padding-left: 0;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
@@ -1,5 +1,31 @@
|
|
1
1
|
module Admin
|
2
2
|
class AdminUsersController < Admin::BaseController
|
3
3
|
mongosteen
|
4
|
+
|
5
|
+
def update_password
|
6
|
+
user = resource
|
7
|
+
new_password = permitted_params[:password]
|
8
|
+
|
9
|
+
user.password = new_password
|
10
|
+
user.password_confirmation = new_password
|
11
|
+
|
12
|
+
if user.save
|
13
|
+
if current_admin_user == user
|
14
|
+
sign_in(user, bypass: true)
|
15
|
+
end
|
16
|
+
render nothing: true
|
17
|
+
|
18
|
+
else
|
19
|
+
render json: user.errors, status: 500
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def permitted_params
|
27
|
+
params.permit!
|
28
|
+
end
|
29
|
+
|
4
30
|
end
|
5
31
|
end
|
@@ -1,16 +1,27 @@
|
|
1
1
|
module ContentHelper
|
2
2
|
def content_meta_tags(object)
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
twitter_tags = {
|
4
|
+
url: object.canonical_url,
|
5
|
+
title: object.meta_title,
|
6
|
+
description: object.meta_description,
|
7
|
+
image: object.opengraph_image_url,
|
8
|
+
card: "summary"
|
9
|
+
}
|
10
|
+
|
11
|
+
open_graph_tags = {
|
12
|
+
title: object.meta_title,
|
13
|
+
description: object.meta_description,
|
14
|
+
url: object.canonical_url,
|
15
|
+
type: object.meta_type,
|
16
|
+
image: object.opengraph_image_url,
|
17
|
+
updated_time: object.updated_at.to_i
|
18
|
+
}
|
9
19
|
|
10
20
|
set_meta_tags title: object.meta_title,
|
11
21
|
description: object.meta_description,
|
12
22
|
keywords: object.meta_keywords,
|
13
23
|
canonical: object.canonical_url,
|
14
|
-
og: open_graph_tags
|
24
|
+
og: open_graph_tags,
|
25
|
+
twitter: twitter_tags
|
15
26
|
end
|
16
27
|
end
|
data/app/helpers/menu_helper.rb
CHANGED
data/app/models/menu_link.rb
CHANGED
@@ -7,8 +7,13 @@ class MenuLink
|
|
7
7
|
## Attributes
|
8
8
|
field :title
|
9
9
|
field :url
|
10
|
-
field :
|
10
|
+
field :force_new_tab, type: Boolean, default: false
|
11
11
|
|
12
12
|
## Relations
|
13
13
|
embedded_in :menu, class_name: 'Menu'
|
14
|
+
|
15
|
+
## Helpers
|
16
|
+
def new_tab?
|
17
|
+
(!url.start_with?("/") || force_new_tab)
|
18
|
+
end
|
14
19
|
end
|
data/app/models/redirect.rb
CHANGED
data/lib/ants/routing.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module ActionDispatch::Routing
|
2
|
+
class Mapper
|
3
|
+
def mount_ants_redirects
|
4
|
+
get "/*id" => 'redirects#show', :constraints => ::Constraints::Redirects
|
5
|
+
end
|
6
|
+
|
7
|
+
def mount_ants_admin_users_crud
|
8
|
+
resources :admin_users, controller: "admin_users" do
|
9
|
+
member do
|
10
|
+
patch :update_password
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def mount_ants_redirects_crud
|
16
|
+
resources :redirects, controller: "redirects"
|
17
|
+
end
|
18
|
+
|
19
|
+
def mount_ants_settings_crud
|
20
|
+
resources :settings_objects, controller: "settings_objects"
|
21
|
+
end
|
22
|
+
|
23
|
+
def mount_ants_menus_crud
|
24
|
+
resources :menus, controller: "menus"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/ants/version.rb
CHANGED
data/lib/ants.rb
CHANGED
@@ -4,19 +4,18 @@ require 'devise'
|
|
4
4
|
|
5
5
|
module Ants
|
6
6
|
require 'ants/engine'
|
7
|
-
|
7
|
+
require 'ants/routing'
|
8
8
|
require "mongoid/fake_criteria"
|
9
|
-
|
10
9
|
require "concerns/ants/id"
|
11
10
|
require "concerns/ants/featurable"
|
12
11
|
require "concerns/ants/hideable"
|
13
12
|
require "concerns/ants/orderable"
|
13
|
+
require "concerns/ants/orderable_reverse"
|
14
14
|
require "concerns/ants/meta"
|
15
15
|
require "concerns/ants/slug"
|
16
|
-
require "concerns/ants/publication"
|
17
16
|
require "concerns/ants/sorted_relations"
|
18
17
|
require "concerns/ants/versions"
|
18
|
+
require "concerns/ants/scheduled"
|
19
19
|
require "concerns/ants/content"
|
20
|
-
|
21
20
|
require "constraints/redirects"
|
22
|
-
end
|
21
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module Ants
|
2
2
|
module Content
|
3
3
|
extend ActiveSupport::Concern
|
4
|
-
|
5
4
|
included do
|
6
5
|
include Mongoid::Document
|
7
6
|
include Mongoid::Timestamps
|
@@ -10,6 +9,7 @@ module Ants
|
|
10
9
|
include Ants::Slug
|
11
10
|
include Ants::Hideable
|
12
11
|
include Ants::Meta
|
12
|
+
include Ants::Scheduled
|
13
13
|
|
14
14
|
## Attributes
|
15
15
|
field :title
|
@@ -49,7 +49,7 @@ module Ants
|
|
49
49
|
def opengraph_image_url
|
50
50
|
url = _opengraph_image_url.presence
|
51
51
|
if url
|
52
|
-
if !url.include?('//')
|
52
|
+
if !url.include?('//') && Rails.env.production?
|
53
53
|
"#{protocole}#{host}#{url}"
|
54
54
|
else
|
55
55
|
url
|
@@ -1,43 +1,33 @@
|
|
1
1
|
module Ants
|
2
2
|
module Featurable
|
3
3
|
extend ActiveSupport::Concern
|
4
|
-
|
5
4
|
included do
|
6
|
-
|
5
|
+
## Attributes
|
7
6
|
field :featured, type: Boolean, default: false
|
8
7
|
|
9
|
-
|
8
|
+
## Scopes
|
10
9
|
scope :featured, -> { where(featured: true) }
|
11
10
|
scope :not_featured, -> { where(featured: false) }
|
12
11
|
|
13
|
-
|
12
|
+
## Indexes
|
14
13
|
index({ featured: 1 })
|
15
14
|
|
16
|
-
|
17
|
-
# helpers
|
15
|
+
## Helpers
|
18
16
|
def featured?
|
19
17
|
self.featured
|
20
18
|
end
|
21
19
|
|
22
|
-
|
23
20
|
def set_featured!
|
24
21
|
return if self.featured?
|
25
22
|
self.featured = true
|
26
23
|
self.save!
|
27
24
|
end
|
28
25
|
|
29
|
-
|
30
26
|
def unset_featured!
|
31
27
|
return unless self.featured?
|
32
28
|
self.featured = false
|
33
29
|
self.save!
|
34
30
|
end
|
35
|
-
|
36
|
-
|
37
31
|
end
|
38
32
|
end
|
39
33
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
@@ -1,33 +1,22 @@
|
|
1
|
-
#
|
2
|
-
#
|
1
|
+
# =============================================================================
|
3
2
|
# IMPORTANT: this default value raises "stack level too deep" if concern is
|
4
3
|
# included for existing objects, to fix issue update _position
|
5
4
|
# value for all objects like this:
|
6
5
|
#
|
7
6
|
# > ModelClass.all.set(_position: 1000)
|
8
|
-
#
|
9
|
-
# ==========================================================================
|
10
|
-
|
7
|
+
# =============================================================================
|
11
8
|
module Ants
|
12
9
|
module Orderable
|
13
10
|
extend ActiveSupport::Concern
|
14
|
-
|
15
11
|
included do
|
16
|
-
|
17
|
-
# attributes
|
12
|
+
## Attributes
|
18
13
|
field :_position, type: Float, default: -> { (self.class.all.last.try(:_position) || 1000) + 10 }
|
19
14
|
|
20
|
-
|
15
|
+
## Scopes
|
21
16
|
default_scope -> { order_by(_position: :asc) }
|
22
17
|
|
23
|
-
|
18
|
+
## Indexes
|
24
19
|
index({ _position: 1 })
|
25
|
-
|
26
|
-
|
27
20
|
end
|
28
21
|
end
|
29
22
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# =============================================================================
|
2
|
+
# IMPORTANT: this default value raises "stack level too deep" if concern is
|
3
|
+
# included for existing objects, to fix issue update _position
|
4
|
+
# value for all objects like this:
|
5
|
+
#
|
6
|
+
# > ModelClass.all.set(_position: 1000)
|
7
|
+
# =============================================================================
|
8
|
+
module Ants
|
9
|
+
module OrderableReverse
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
included do
|
12
|
+
## Attributes
|
13
|
+
field :_position, type: Float, default: -> {
|
14
|
+
(self.class.all.first.try(:_position) || 1000) + 10
|
15
|
+
}
|
16
|
+
|
17
|
+
## Scopes
|
18
|
+
default_scope -> { desc(:_position) }
|
19
|
+
|
20
|
+
## Indexes
|
21
|
+
index({ _position: -1 })
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Ants
|
2
|
+
module Scheduled
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
included do
|
5
|
+
## Attributes
|
6
|
+
field :published_at, type: DateTime
|
7
|
+
|
8
|
+
## Scopes
|
9
|
+
default_scope -> { desc(:published_at) }
|
10
|
+
scope :scheduled, -> { where(:published_at.gt => Time.zone.now) }
|
11
|
+
scope :published, -> { where( :published_at.lte => Time.zone.now) }
|
12
|
+
|
13
|
+
## Indexes
|
14
|
+
index published_at: -1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ants
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Kravets
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mongoid
|
@@ -155,6 +155,7 @@ files:
|
|
155
155
|
- ants.gemspec
|
156
156
|
- app/assets/javascripts/ants.coffee
|
157
157
|
- app/assets/javascripts/ants/admin_users.coffee
|
158
|
+
- app/assets/javascripts/ants/content.coffee
|
158
159
|
- app/assets/javascripts/ants/menu.coffee
|
159
160
|
- app/assets/javascripts/ants/meta.coffee
|
160
161
|
- app/assets/javascripts/ants/profile.coffee
|
@@ -162,9 +163,10 @@ files:
|
|
162
163
|
- app/assets/javascripts/ants/settings.coffee
|
163
164
|
- app/assets/javascripts/ants/slug.coffee
|
164
165
|
- app/assets/stylesheets/ants.scss
|
166
|
+
- app/assets/stylesheets/ants/content.scss
|
165
167
|
- app/assets/stylesheets/ants/header.scss
|
168
|
+
- app/assets/stylesheets/ants/menu.scss
|
166
169
|
- app/assets/stylesheets/ants/profile.scss
|
167
|
-
- app/assets/stylesheets/ants/redirects.scss
|
168
170
|
- app/assets/stylesheets/ants/settings.scss
|
169
171
|
- app/controllers/admin/admin_users_controller.rb
|
170
172
|
- app/controllers/admin/menus_controller.rb
|
@@ -182,6 +184,7 @@ files:
|
|
182
184
|
- app/views/ants/_profile.html.erb
|
183
185
|
- lib/ants.rb
|
184
186
|
- lib/ants/engine.rb
|
187
|
+
- lib/ants/routing.rb
|
185
188
|
- lib/ants/version.rb
|
186
189
|
- lib/concerns/ants/content.rb
|
187
190
|
- lib/concerns/ants/featurable.rb
|
@@ -189,7 +192,8 @@ files:
|
|
189
192
|
- lib/concerns/ants/id.rb
|
190
193
|
- lib/concerns/ants/meta.rb
|
191
194
|
- lib/concerns/ants/orderable.rb
|
192
|
-
- lib/concerns/ants/
|
195
|
+
- lib/concerns/ants/orderable_reverse.rb
|
196
|
+
- lib/concerns/ants/scheduled.rb
|
193
197
|
- lib/concerns/ants/slug.rb
|
194
198
|
- lib/concerns/ants/sorted_relations.rb
|
195
199
|
- lib/concerns/ants/versions.rb
|
@@ -1,79 +0,0 @@
|
|
1
|
-
module Ants
|
2
|
-
module Publication
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
included do
|
6
|
-
include Mongoid::Timestamps
|
7
|
-
|
8
|
-
include Ants::Id
|
9
|
-
include Ants::Featurable
|
10
|
-
include Ants::Orderable
|
11
|
-
include Ants::Slug
|
12
|
-
include Ants::Meta
|
13
|
-
|
14
|
-
# attributes
|
15
|
-
field :title
|
16
|
-
field :draft, type: Boolean, default: false
|
17
|
-
field :published_at, type: DateTime, default: -> { DateTime.now }
|
18
|
-
|
19
|
-
# slug
|
20
|
-
slug :title
|
21
|
-
|
22
|
-
# validations
|
23
|
-
validates_presence_of :title, :published_at
|
24
|
-
|
25
|
-
# scopes
|
26
|
-
scope :drafts, -> { where(draft: true) }
|
27
|
-
scope :ready_to_publish, -> { where(draft: false) }
|
28
|
-
scope :scheduled, -> { ready_to_publish.where(:published_at.gt => Time.zone.now) }
|
29
|
-
scope :published, -> { ready_to_publish.where(:published_at.lte => Time.zone.now) }
|
30
|
-
# override featured scope set by Ants::Featured to add ordering by _position
|
31
|
-
scope :featured, -> { unscoped.published.where(featured: true).asc(:_position) }
|
32
|
-
scope :featured_first, -> { unscoped.desc(:featured).asc(:_position).desc(:published_at) }
|
33
|
-
# override default_scope set by Ants::Orderable
|
34
|
-
default_scope -> { desc(:published_at).desc(:created_at) }
|
35
|
-
|
36
|
-
# indexes @TODO: verify if these indexes are required
|
37
|
-
index({ featured: -1, _position: 1, published_at: -1 }) # featured first
|
38
|
-
index({ featured: -1, _position: 1 }) # featured
|
39
|
-
index({ published_at: -1, created_at: -1 }) # default
|
40
|
-
|
41
|
-
|
42
|
-
# helpers
|
43
|
-
def draft?
|
44
|
-
self.draft
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
def published?
|
49
|
-
! draft? && published_at <= Time.zone.now
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
def scheduled?
|
54
|
-
! draft? && published_at > Time.zone.now
|
55
|
-
end
|
56
|
-
|
57
|
-
|
58
|
-
def _list_item_title
|
59
|
-
title
|
60
|
-
end
|
61
|
-
|
62
|
-
|
63
|
-
def _list_item_subtitle
|
64
|
-
published_at.strftime("%B %d, %Y @ %l:%M %P")
|
65
|
-
end
|
66
|
-
|
67
|
-
|
68
|
-
def meta_title
|
69
|
-
_meta_title.present || title
|
70
|
-
end
|
71
|
-
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|