tomify 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/tomify/dynamic/classes/form.coffee +1 -0
- data/app/assets/javascripts/tomify/dynamic/classes/model.coffee +1 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/admin/feedback.coffee +20 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/admin/pages.coffee.erb +3 -4
- data/app/assets/javascripts/tomify/dynamic/react/components/admin/settings.coffee +2 -2
- data/app/assets/javascripts/tomify/dynamic/react/components/admin/sidebars.coffee.erb +2 -2
- data/app/assets/javascripts/tomify/dynamic/react/components/admin/uploads.coffee +2 -2
- data/app/assets/javascripts/tomify/dynamic/react/components/admin/users.coffee +2 -2
- data/app/assets/javascripts/tomify/dynamic/react/components/index.coffee +39 -30
- data/app/assets/javascripts/tomify/dynamic/react/components/layout/public_navbar.coffee +2 -2
- data/app/assets/javascripts/tomify/dynamic/react/components/public/feedback.coffee +39 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/public/subscription.coffee +1 -1
- data/app/assets/javascripts/tomify/dynamic/react/components/show.coffee +60 -0
- data/app/controllers/tomify/admin/feedback_controller.rb +2 -0
- data/app/controllers/tomify/api/admin/feedback_controller.rb +16 -0
- data/app/controllers/tomify/api/admin/pages_controller.rb +1 -0
- data/app/controllers/tomify/api/admin/settings_controller.rb +1 -0
- data/app/controllers/tomify/api/admin/sidebars_controller.rb +1 -0
- data/app/controllers/tomify/api/admin/uploads_controller.rb +1 -0
- data/app/controllers/tomify/api/public/feedback_controller.rb +15 -0
- data/app/controllers/tomify/concerns/default/navbar_helper.rb +1 -0
- data/app/controllers/tomify/public/feedback_controller.rb +5 -0
- data/app/mailers/tomify/feedback_mailer.rb +13 -0
- data/app/models/tomify/concerns/feedback.rb +23 -0
- data/app/models/tomify/concerns/page.rb +1 -1
- data/app/models/tomify/feedback.rb +3 -0
- data/app/models/tomify/setting.rb +1 -1
- data/app/views/templates/feedback.haml +1 -0
- data/app/views/tomify/mailers/feedback_mailer/admin.haml +17 -0
- data/app/views/tomify/mailers/feedback_mailer/user.haml +8 -0
- data/app/views/tomify/mailers/partials/_unsubscribe.haml +3 -2
- data/app/views/tomify/mailers/user_mailer/invite.haml +2 -2
- data/app/views/tomify/mailers/user_mailer/reset_password.haml +5 -5
- data/app/views/tomify/mailers/user_mailer/welcome.haml +2 -2
- data/config/initializers/inflectors.rb +3 -0
- data/config/routes.rb +3 -0
- data/db/migrate/19900000000012_create_feedback.rb +15 -0
- data/db/seeds.rb +1 -1
- data/lib/previews/tomify/feedback_preview.rb +14 -0
- data/lib/tomify/version.rb +1 -1
- data/lib/tomify.rb +3 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57007dacbc53ef0792f799a4dd59ddc0f1fbd1aa
|
4
|
+
data.tar.gz: c599f317a3bd1b022c2b6d3e1a5b108e5abb14a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 219e26d3ce04cf98995c401ac4264f90932d316080ea2a4cec2427bbfb793302478087037f0c6731129e3cd2cb117ebf0eeb0003156d55803336ec3d7a258c25
|
7
|
+
data.tar.gz: 931fcab4e391dcf69120fa1cbc71d2e460687388cc6be7f13e930f923f9ed6a4b1a29d24f8263b23437394db93081fd7f802dc786d869a95f7855f3108a82e30
|
@@ -30,6 +30,7 @@ class @Form
|
|
30
30
|
@models.push options.model if options.model
|
31
31
|
setComponent: (component, store) ->
|
32
32
|
store ?= component.store
|
33
|
+
store.findOrCreate "Form", @
|
33
34
|
@component = component
|
34
35
|
@record = store.findOrCreate "Record", {}
|
35
36
|
@changes = store.findOrCreate "Changes", {}
|
@@ -33,6 +33,7 @@ class @Model extends Observer
|
|
33
33
|
Request[type](route, params)
|
34
34
|
setDefaultActions: ->
|
35
35
|
@setAction "find", @request "get"
|
36
|
+
@setAction "show", @request "get"
|
36
37
|
@setAction "edit", @request "get"
|
37
38
|
@setAction "update", @request "put", true
|
38
39
|
@setAction "new", Request.none
|
@@ -0,0 +1,20 @@
|
|
1
|
+
model = Model.findOrCreate "Admin.Feedback", path: "feedback"
|
2
|
+
resolve = (r) ->
|
3
|
+
click = (e) ->
|
4
|
+
e.preventDefault()
|
5
|
+
model.update(r.id, { resolved: !r.resolved }).then (response) ->
|
6
|
+
message type: response.type, text: response.message
|
7
|
+
<a key="resolve" className="btn btn-primary btn-xs" href="#" onClick={click}>
|
8
|
+
{if r.resolved then "Unresolve" else "Resolve"}
|
9
|
+
</a>
|
10
|
+
model.actions = { show: true, resolve: resolve, destroy: true }
|
11
|
+
model.columns = [
|
12
|
+
{ name: "name" },
|
13
|
+
{ name: "email" },
|
14
|
+
{ name: "subject" },
|
15
|
+
{ name: "resolved", value: (r) -> if r.resolved then "Yes" else "No" },
|
16
|
+
{ name: "created_at", value: (r) -> r.created_at.date() },
|
17
|
+
{ name: "updated_at", value: (r) -> r.updated_at.date() }
|
18
|
+
]
|
19
|
+
|
20
|
+
Component.create "Admin.Feedback.Index.Container", render: -> <Index.Container name="Admin.Feedback" />
|
@@ -1,4 +1,6 @@
|
|
1
1
|
model = Model.findOrCreate "Admin.Page"
|
2
|
+
view = (r) -> <a key="visit" className="btn btn-primary btn-xs" href="/#{r.path}" target="_blank">Visit</a>
|
3
|
+
model.actions = { new: true, view: view, edit: true, destroy: true }
|
2
4
|
model.columns = [
|
3
5
|
{ name: "active", value: (r) -> if r.active then "Yes" else "No" },
|
4
6
|
{ name: "rank" },
|
@@ -14,10 +16,7 @@ model.columns = [
|
|
14
16
|
return "None" unless r.share_image.url
|
15
17
|
<a href={r.share_image.url} target="_blank">View</a>
|
16
18
|
},
|
17
|
-
{ name: "updated_at", value: (r) -> r.updated_at.date() }
|
18
|
-
{ name: "actions", edit: true, destroy: true, view: (r) ->
|
19
|
-
<a key="visit" href="/#{r.path}" target="_blank">Visit</a>
|
20
|
-
}
|
19
|
+
{ name: "updated_at", value: (r) -> r.updated_at.date() }
|
21
20
|
]
|
22
21
|
|
23
22
|
form = new Form "horizontal"
|
@@ -1,4 +1,5 @@
|
|
1
1
|
model = Model.findOrCreate "Admin.Setting"
|
2
|
+
model.actions = { new: true, edit: true, destroy: true }
|
2
3
|
model.columns = [
|
3
4
|
{ name: "name" },
|
4
5
|
{ name: "public", value: (r) -> if r.public then "Yes" else "No" },
|
@@ -12,8 +13,7 @@ model.columns = [
|
|
12
13
|
when "Json" then "JSON"
|
13
14
|
else r.value
|
14
15
|
},
|
15
|
-
{ name: "updated_at", value: (r) -> r.updated_at.date() }
|
16
|
-
{ name: "actions", edit: true, destroy: true }
|
16
|
+
{ name: "updated_at", value: (r) -> r.updated_at.date() }
|
17
17
|
]
|
18
18
|
|
19
19
|
options = ["Boolean", "Json", "Text", "Uploader"]
|
@@ -1,10 +1,10 @@
|
|
1
1
|
model = Model.findOrCreate "Admin.Sidebar"
|
2
|
+
model.actions = { new: true, edit: true, destroy: true }
|
2
3
|
model.columns = [
|
3
4
|
{ name: "active", value: (r) -> if r.active then "Yes" else "No" },
|
4
5
|
{ name: "name" },
|
5
6
|
{ name: "heading" },
|
6
|
-
{ name: "updated_at", value: (r) -> r.updated_at.date() }
|
7
|
-
{ name: "actions", edit: true, destroy: true }
|
7
|
+
{ name: "updated_at", value: (r) -> r.updated_at.date() }
|
8
8
|
]
|
9
9
|
|
10
10
|
form = new Form "horizontal"
|
@@ -1,9 +1,9 @@
|
|
1
1
|
model = Model.findOrCreate "Admin.Upload"
|
2
|
+
model.actions = { new: true, edit: true, destroy: true }
|
2
3
|
model.columns = [
|
3
4
|
{ name: "name" },
|
4
5
|
{ name: "file", value: (r) -> <a href={r.file.url} target="_blank">View</a> },
|
5
|
-
{ name: "updated_at", value: (r) -> r.updated_at.date() }
|
6
|
-
{ name: "actions", edit: true, destroy: true }
|
6
|
+
{ name: "updated_at", value: (r) -> r.updated_at.date() }
|
7
7
|
]
|
8
8
|
|
9
9
|
form = new Form "horizontal"
|
@@ -1,4 +1,5 @@
|
|
1
1
|
model = Model.findOrCreate "Admin.User"
|
2
|
+
model.actions = { new: true, edit: true, destroy: true }
|
2
3
|
model.columns = [
|
3
4
|
{ name: "admin", value: (r) -> if r.admin then "Yes" else "No" },
|
4
5
|
{ name: "email" },
|
@@ -7,8 +8,7 @@ model.columns = [
|
|
7
8
|
{ name: "invited", value: (r) -> if r.invited then "Yes" else "No" },
|
8
9
|
{ name: "verified", value: (r) -> if r.verified then "Yes" else "No" },
|
9
10
|
{ name: "created_at", value: (r) -> r.created_at.date() },
|
10
|
-
{ name: "updated_at", value: (r) -> r.updated_at.date() }
|
11
|
-
{ name: "actions", edit: true, destroy: true }
|
11
|
+
{ name: "updated_at", value: (r) -> r.updated_at.date() }
|
12
12
|
]
|
13
13
|
|
14
14
|
form = new Form "horizontal"
|
@@ -8,8 +8,6 @@ Component.create "Index.Container",
|
|
8
8
|
@follow @model.on "create", @modelCreate
|
9
9
|
@follow @model.on "update", @modelUpdate
|
10
10
|
@follow @model.on "destroy", @modelDestroy
|
11
|
-
@newForm = @props.newForm || @props.form.copy()
|
12
|
-
@editForm = @props.editForm || @props.form.copy()
|
13
11
|
componentDidMount: ->
|
14
12
|
@model.all()
|
15
13
|
modelAll: (response) ->
|
@@ -23,20 +21,34 @@ Component.create "Index.Container",
|
|
23
21
|
@model.all() if response.type == "danger"
|
24
22
|
setPage: (page) ->
|
25
23
|
@setState(page: 1, currentRecords: @state.records[(page*10 - 10)..page*10])
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
24
|
+
actions: (record) ->
|
25
|
+
actions = []
|
26
|
+
|
27
|
+
for action, value of @model.actions when action != "new" && value
|
28
|
+
switch
|
29
|
+
when typeof value is "function"
|
30
|
+
actions.push value(record)
|
31
|
+
when action in ["show", "edit"]
|
32
|
+
modelAction = @model[action]
|
33
|
+
click = (e) ->
|
34
|
+
e.preventDefault()
|
35
|
+
modelAction(record.id)
|
36
|
+
actions.push <a key={action} className="btn btn-primary btn-xs" href="#" onClick={click}>{action.titleize}</a>
|
37
|
+
when action == "destroy"
|
38
|
+
destroy = @model.destroy
|
39
|
+
click = (e) ->
|
40
|
+
e.preventDefault()
|
41
|
+
destroy(record.id).then (response) -> message type: response.type, text: response.message
|
42
|
+
confirm = "Are you sure you want to delete #{record.name}?"
|
43
|
+
actions.push <a key="destroy" className="btn btn-primary btn-xs" href="#" onClick={click} data-confirm={confirm}>Delete</a>
|
44
|
+
|
45
|
+
<td>
|
46
|
+
<div className="btn-group">{actions}</div>
|
47
|
+
</td>
|
39
48
|
render: ->
|
49
|
+
actions = @model.actions
|
50
|
+
actions = !!Object.keys(actions).find (action) -> action != "new" && actions[action]
|
51
|
+
|
40
52
|
<div>
|
41
53
|
<div className="row">
|
42
54
|
<div className="col-xs-12">
|
@@ -44,7 +56,12 @@ Component.create "Index.Container",
|
|
44
56
|
<div className="panel-heading">
|
45
57
|
<h4>
|
46
58
|
{@model.name.pluralize.titleize}
|
47
|
-
|
59
|
+
{if @model.actions.new
|
60
|
+
click = (e) =>
|
61
|
+
e.preventDefault()
|
62
|
+
@model.new()
|
63
|
+
<a className="btn btn-primary btn-xs" href="#" onClick={click}>New</a>
|
64
|
+
}
|
48
65
|
</h4>
|
49
66
|
<Pagination page="1" total={@state.records.length} setPage={@setPage} />
|
50
67
|
</div>
|
@@ -55,25 +72,16 @@ Component.create "Index.Container",
|
|
55
72
|
{for field, i in @model.columns
|
56
73
|
<th key={i}>{field.name.titleize}</th>
|
57
74
|
}
|
75
|
+
{<th>Actions</th> if actions}
|
58
76
|
</tr>
|
59
77
|
</thead>
|
60
78
|
<tbody>
|
61
79
|
{for record, i in @state.records
|
62
80
|
<tr key={i}>
|
63
81
|
{for field, j in @model.columns
|
64
|
-
|
65
|
-
<td key={j}>
|
66
|
-
{[
|
67
|
-
field.view && field.view(record)
|
68
|
-
field.view && field.edit && " | "
|
69
|
-
field.edit && <a key="edit" onClick={@edit.bind(null, record.id)} href="#">Edit</a>
|
70
|
-
field.destroy && " | "
|
71
|
-
field.destroy && <a key="destroy" onClick={@destroy.bind(null, record.id)} href="#" data-confirm="Are you sure you want to delete #{record.name}?">Delete</a>
|
72
|
-
]}
|
73
|
-
</td>
|
74
|
-
else
|
75
|
-
<td key={j}>{field.value?(record) ? record[field.name]}</td>
|
82
|
+
<td key={j}>{field.value?(record) ? record[field.name]}</td>
|
76
83
|
}
|
84
|
+
{@actions(record) if actions}
|
77
85
|
</tr>
|
78
86
|
}
|
79
87
|
</tbody>
|
@@ -82,6 +90,7 @@ Component.create "Index.Container",
|
|
82
90
|
</div>
|
83
91
|
</div>
|
84
92
|
</div>
|
85
|
-
<New.Container name={@props.name} form={@newForm} />
|
86
|
-
<
|
93
|
+
{<New.Container name={@props.name} form={@props.newForm || @props.form.copy()} /> if @model.actions.new}
|
94
|
+
{<Show.Container name={@props.name} /> if @model.actions.show}
|
95
|
+
{<Edit.Container name={@props.name} form={@props.editForm || @props.form.copy()} /> if @model.actions.edit}
|
87
96
|
</div>
|
@@ -29,9 +29,9 @@ Component.create "Layout.PublicNavbar",
|
|
29
29
|
<ul className="nav navbar-nav">
|
30
30
|
{if setting "allow_signup"
|
31
31
|
if @state.user.id
|
32
|
-
@link(name: "Login", url: "/session")
|
33
|
-
else
|
34
32
|
@link(name: "Profile", url: "/profile")
|
33
|
+
else
|
34
|
+
@link(name: "Login", url: "/session")
|
35
35
|
}
|
36
36
|
{for page in @state.pages
|
37
37
|
if page.children[0]
|
@@ -0,0 +1,39 @@
|
|
1
|
+
Model.create "Public.Feedback", path: "feedback"
|
2
|
+
|
3
|
+
form = new Form
|
4
|
+
form.add "name", "text"
|
5
|
+
form.add "email", "email"
|
6
|
+
form.add "subject", "text"
|
7
|
+
form.add "message", "textarea"
|
8
|
+
|
9
|
+
Component.create "Public.Feedback",
|
10
|
+
componentWillInitialize: ->
|
11
|
+
@model = Model.find "Public.Feedback"
|
12
|
+
@store = Store.findOrCreate "Public.Feedback"
|
13
|
+
@form = form.setComponent @
|
14
|
+
@follow @model.on "create", @modelCreate
|
15
|
+
@model.new()
|
16
|
+
modelCreate: (response) ->
|
17
|
+
message type: response.type, text: response.message
|
18
|
+
@form.changes.set {} if response.type == "success"
|
19
|
+
submit: (e) ->
|
20
|
+
e.preventDefault()
|
21
|
+
if @form.changes.empty()
|
22
|
+
message type: "warning", text: "You shall not pass!"
|
23
|
+
else
|
24
|
+
@model.create @form.changes.get()
|
25
|
+
false
|
26
|
+
render: ->
|
27
|
+
<div className="row text-center">
|
28
|
+
<div className="dynamic-sm">
|
29
|
+
<div>
|
30
|
+
<h3>Feedback</h3>
|
31
|
+
<form onSubmit={@submit}>
|
32
|
+
{@form.render()}
|
33
|
+
<div className="form-group">
|
34
|
+
<input type="submit" name="commit" value="Submit" className="btn btn-primary" />
|
35
|
+
</div>
|
36
|
+
</form>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
</div>
|
@@ -7,7 +7,7 @@ Component.create "Public.Subscription",
|
|
7
7
|
@store = Store.findOrCreate "Public.Subscription"
|
8
8
|
@follow @model.on "destroy", @modelDestroy
|
9
9
|
modelDestroy: (response) ->
|
10
|
-
message type: type, text: response.message unless response.type == "success"
|
10
|
+
message type: response.type, text: response.message unless response.type == "success"
|
11
11
|
@setState unsubscribed: true, message: response.message
|
12
12
|
destroy: (e) ->
|
13
13
|
e.preventDefault()
|
@@ -0,0 +1,60 @@
|
|
1
|
+
Component.create "Show.Container",
|
2
|
+
componentWillInitialize: ->
|
3
|
+
@model = Model.findOrCreate @props.name
|
4
|
+
@store = Store.findOrCreate "#{@props.name}.Show"
|
5
|
+
@follow @model.on "new", @hide
|
6
|
+
@follow @model.on "create", @hide
|
7
|
+
@follow @model.on "show", @show
|
8
|
+
@follow @model.on "edit", @hide
|
9
|
+
@follow @model.on "update", @hide
|
10
|
+
show: (response) ->
|
11
|
+
@store.merge(show: true, record: response.data)
|
12
|
+
hide: ->
|
13
|
+
@store.merge(show: false)
|
14
|
+
cancel: (e) ->
|
15
|
+
e.preventDefault()
|
16
|
+
@store.merge(show: false)
|
17
|
+
false
|
18
|
+
skipField: (field, value) ->
|
19
|
+
return true unless value?
|
20
|
+
return true if field == "id" || field.indexOf("_id") >= 0
|
21
|
+
return true if field.indexOf("password") >= 0
|
22
|
+
false
|
23
|
+
renderFields: (object) ->
|
24
|
+
switch
|
25
|
+
when object instanceof Array
|
26
|
+
for field in object
|
27
|
+
<div key={field} className="row">
|
28
|
+
<div className="col-xs-12">
|
29
|
+
{@renderFields field}
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
when object instanceof Object
|
33
|
+
for field, value of object when !@skipField(field, value)
|
34
|
+
<div key={field} className="row">
|
35
|
+
<label className="col-xs-3 text-right">
|
36
|
+
{field.titleize}
|
37
|
+
</label>
|
38
|
+
<div className="col-xs-9">
|
39
|
+
{@renderFields value}
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
when Date.parse(object)
|
43
|
+
object.date()
|
44
|
+
else
|
45
|
+
"#{object}"
|
46
|
+
render: ->
|
47
|
+
return <div /> unless @state.store.show
|
48
|
+
<div className="row">
|
49
|
+
<div className="col-xs-12">
|
50
|
+
<div className="panel panel-default">
|
51
|
+
<div className="panel-heading">
|
52
|
+
<h4>{@model.name.titleize}</h4>
|
53
|
+
<a className="btn btn-danger pull-right" href="#" onClick={@cancel}><i className="fa fa-close" /></a>
|
54
|
+
</div>
|
55
|
+
<div className="panel-body">
|
56
|
+
{@renderFields @state.store.record}
|
57
|
+
</div>
|
58
|
+
</div>
|
59
|
+
</div>
|
60
|
+
</div>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Tomify::Api::Admin::FeedbackController < Tomify.controllers.admin_api
|
2
|
+
def update
|
3
|
+
find_record
|
4
|
+
update_record
|
5
|
+
render json: { type: :success, data: data, message: "#{model_name} #{record.resolved ? "Resolved" : "Unresolved"}" }
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
def permitted_attributes
|
10
|
+
[:resolved]
|
11
|
+
end
|
12
|
+
|
13
|
+
def serializable_options
|
14
|
+
{ include: [user: { only: [:first_name, :last_name, :email] }] }
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Tomify::Api::Public::FeedbackController < Tomify.controllers.public_api
|
2
|
+
def create
|
3
|
+
create_record
|
4
|
+
render json: { type: :success, data: data, message: "Thank you for your feedback!" }
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
def permitted_attributes
|
9
|
+
[:name, :email, :subject, :message]
|
10
|
+
end
|
11
|
+
|
12
|
+
def record_params
|
13
|
+
super.merge(user_id: current_user.try(:id))
|
14
|
+
end
|
15
|
+
end
|
@@ -8,6 +8,7 @@ module Tomify::Concerns::Default::NavbarHelper
|
|
8
8
|
add_to_navbar :public, Proc.new { Tomify.models.page.for_navbar }
|
9
9
|
add_to_navbar :admin, [
|
10
10
|
{ name: "App", path: "admin/settings" },
|
11
|
+
{ name: "Feedback", path: "admin/feedback" },
|
11
12
|
{ name: "Pages", path: "admin/pages" },
|
12
13
|
{ name: "Sidebars", path: "admin/sidebars" },
|
13
14
|
{ name: "Uploads", path: "admin/uploads" },
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Tomify::FeedbackMailer < Tomify.mailers.base
|
2
|
+
def admin(feedback)
|
3
|
+
@feedback = feedback
|
4
|
+
|
5
|
+
mail to: Tomify.models.user.admin.pluck(:email), subject: "Feedback: #{@feedback.subject}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def user(feedback)
|
9
|
+
@feedback = feedback
|
10
|
+
|
11
|
+
mail to: @feedback.email, subject: "Thank You!"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Tomify::Concerns::Feedback
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
belongs_to :user, class_name: Tomify.models.user.to_s, optional: true
|
6
|
+
|
7
|
+
after_create :notify_admin
|
8
|
+
after_create :notify_user
|
9
|
+
|
10
|
+
validates_presence_of :name, :email, :subject, :message
|
11
|
+
|
12
|
+
default_scope { order(:resolved, created_at: :desc) }
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def notify_admin
|
17
|
+
Tomify.mailers.feedback.admin(self).deliver_now
|
18
|
+
end
|
19
|
+
|
20
|
+
def notify_user
|
21
|
+
Tomify.mailers.feedback.user(self).deliver_now
|
22
|
+
end
|
23
|
+
end
|
@@ -12,7 +12,7 @@ module Tomify::Concerns::Page
|
|
12
12
|
before_validation :format_path
|
13
13
|
before_validation :require_root, if: :root_changed?
|
14
14
|
before_save :set_root, if: :root_changed?
|
15
|
-
before_destroy { |page| page.root.blank? }
|
15
|
+
before_destroy { |page| throw :abort unless page.root.blank? }
|
16
16
|
|
17
17
|
validates_presence_of :rank, :path, :name, :template
|
18
18
|
validates_uniqueness_of :path
|
@@ -7,7 +7,7 @@ class Tomify::Setting < Tomify.models.base
|
|
7
7
|
validate :public_valid?, on: :update
|
8
8
|
|
9
9
|
after_commit :update_config
|
10
|
-
before_destroy { |record|
|
10
|
+
before_destroy { |record| throw :abort if record.name.in? self.class.required_settings }
|
11
11
|
|
12
12
|
def self.required_settings
|
13
13
|
["allow_signup", "aws", "name", "email", "timezone"]
|
@@ -0,0 +1 @@
|
|
1
|
+
= react_component "Public.Feedback"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
%div(style="text-align: center;")
|
2
|
+
%p
|
3
|
+
#{setting :name} has recieved new feedback!
|
4
|
+
#{link_to "Login", root_url} to resolve it.
|
5
|
+
%div
|
6
|
+
%p
|
7
|
+
%b Name:
|
8
|
+
= @feedback.name
|
9
|
+
%p
|
10
|
+
%b Email:
|
11
|
+
= @feedback.email
|
12
|
+
%p
|
13
|
+
%b Subject:
|
14
|
+
= @feedback.subject
|
15
|
+
%p
|
16
|
+
%b Message
|
17
|
+
%p(style="white-space: pre-line;")= @feedback.message
|
@@ -1,3 +1,4 @@
|
|
1
1
|
- if @_message.to.length == 1
|
2
|
-
|
3
|
-
|
2
|
+
%div(style="text-align: center;")
|
3
|
+
To unsubscribe from all #{setting(:name)} emails
|
4
|
+
= link_to "click here", subscription_url(email: @_message.to.first)
|
@@ -1,9 +1,9 @@
|
|
1
1
|
%div(style="text-align: center;")
|
2
2
|
%p Dear #{@user.first_name},
|
3
3
|
%p You've been invited to join #{setting(:name)}!
|
4
|
-
= image_tag "gifs/woods.gif"
|
4
|
+
= image_tag "tomify/gifs/woods.gif"
|
5
5
|
%p
|
6
6
|
Click #{link_to "here", profile_url(token: @user.token(:email), from: :email)}
|
7
7
|
to join!
|
8
|
-
= image_tag "gifs/wind.gif"
|
8
|
+
= image_tag "tomify/gifs/wind.gif"
|
9
9
|
= render "partials/signature"
|
@@ -1,13 +1,13 @@
|
|
1
1
|
%div(style="text-align: center;")
|
2
2
|
%p Dear #{@user.first_name},
|
3
3
|
%p Looks like you forgot your password...
|
4
|
-
= image_tag "gifs/laughing.gif"
|
5
|
-
= image_tag "gifs/sneer.gif"
|
4
|
+
= image_tag "tomify/gifs/laughing.gif"
|
5
|
+
= image_tag "tomify/gifs/sneer.gif"
|
6
6
|
%p Sounds like a rough day
|
7
|
-
= image_tag "gifs/woods.gif"
|
8
|
-
= image_tag "gifs/basketball.gif"
|
7
|
+
= image_tag "tomify/gifs/woods.gif"
|
8
|
+
= image_tag "tomify/gifs/basketball.gif"
|
9
9
|
%p You'll do better next time
|
10
|
-
= image_tag "gifs/wind.gif"
|
10
|
+
= image_tag "tomify/gifs/wind.gif"
|
11
11
|
%p
|
12
12
|
Guess you can update it
|
13
13
|
= link_to "here", profile_url(token: @user.token(:email), from: :email)
|
@@ -1,9 +1,9 @@
|
|
1
1
|
%div(style="text-align: center;")
|
2
2
|
%p Dear #{@user.first_name},
|
3
3
|
%p Welcome to #{setting(:name)}!
|
4
|
-
= image_tag "gifs/woods.gif"
|
4
|
+
= image_tag "tomify/gifs/woods.gif"
|
5
5
|
%p
|
6
6
|
Click #{link_to "here", root_url(token: @user.token(:email), from: :email)}
|
7
7
|
to come back home!
|
8
|
-
= image_tag "gifs/wind.gif"
|
8
|
+
= image_tag "tomify/gifs/wind.gif"
|
9
9
|
= render "partials/signature"
|
data/config/routes.rb
CHANGED
@@ -11,6 +11,7 @@ Rails.application.routes.draw do
|
|
11
11
|
namespace :admin do
|
12
12
|
root "settings#index"
|
13
13
|
|
14
|
+
resources :feedback, only: :index
|
14
15
|
resources :pages, only: :index
|
15
16
|
resources :settings, only: :index
|
16
17
|
resources :sidebars, only: :index
|
@@ -20,6 +21,7 @@ Rails.application.routes.draw do
|
|
20
21
|
|
21
22
|
namespace :api do
|
22
23
|
namespace :admin do
|
24
|
+
resources :feedback, only: [:index, :show, :update, :destroy]
|
23
25
|
resources :pages, only: [:index, :create, :show, :update, :destroy]
|
24
26
|
resources :settings, only: [:index, :create, :show, :update, :destroy]
|
25
27
|
resources :sidebars, only: [:index, :create, :show, :update, :destroy]
|
@@ -28,6 +30,7 @@ Rails.application.routes.draw do
|
|
28
30
|
end
|
29
31
|
|
30
32
|
namespace :public do
|
33
|
+
resource :feedback, only: :create
|
31
34
|
resource :user, only: [:create, :show, :update, :destroy]
|
32
35
|
resource :session, only: [:create, :destroy]
|
33
36
|
resource :password, only: :create
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateFeedback < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :feedback do |t|
|
4
|
+
t.string :user_id
|
5
|
+
t.string :name, null: false
|
6
|
+
t.string :email, null: false
|
7
|
+
t.string :subject, null: false
|
8
|
+
t.string :message, null: false
|
9
|
+
t.boolean :resolved, default: false, null: false, index: true
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
t.index [:resolved, :created_at]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/db/seeds.rb
CHANGED
data/lib/tomify/version.rb
CHANGED
data/lib/tomify.rb
CHANGED
@@ -15,6 +15,7 @@ module Tomify
|
|
15
15
|
mattr_accessor :mailers
|
16
16
|
self.mailers = Constantly.new(
|
17
17
|
base: "TomifyMailer",
|
18
|
+
feedback: "Tomify::FeedbackMailer",
|
18
19
|
user: "Tomify::UserMailer"
|
19
20
|
)
|
20
21
|
|
@@ -22,6 +23,7 @@ module Tomify
|
|
22
23
|
self.models = Constantly.new(
|
23
24
|
activity: "Tomify::Activity",
|
24
25
|
base: "TomifyRecord",
|
26
|
+
feedback: "Tomify::Feedback",
|
25
27
|
page: "Tomify::Page",
|
26
28
|
setting: "Tomify::Setting",
|
27
29
|
sidebar: "Tomify::Sidebar",
|
@@ -33,6 +35,7 @@ module Tomify
|
|
33
35
|
mattr_accessor :previews
|
34
36
|
self.previews = Constantly.new(
|
35
37
|
base: "Tomify::Preview",
|
38
|
+
feedback: "Tomify::FeedbackPreview",
|
36
39
|
user: "Tomify::UserPreview"
|
37
40
|
)
|
38
41
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tomify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Prats
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-06-
|
11
|
+
date: 2017-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -265,6 +265,7 @@ files:
|
|
265
265
|
- app/assets/javascripts/tomify/dynamic/classes/request.coffee
|
266
266
|
- app/assets/javascripts/tomify/dynamic/classes/store.coffee
|
267
267
|
- app/assets/javascripts/tomify/dynamic/global.coffee
|
268
|
+
- app/assets/javascripts/tomify/dynamic/react/components/admin/feedback.coffee
|
268
269
|
- app/assets/javascripts/tomify/dynamic/react/components/admin/pages.coffee.erb
|
269
270
|
- app/assets/javascripts/tomify/dynamic/react/components/admin/settings.coffee
|
270
271
|
- app/assets/javascripts/tomify/dynamic/react/components/admin/sidebars.coffee.erb
|
@@ -288,6 +289,7 @@ files:
|
|
288
289
|
- app/assets/javascripts/tomify/dynamic/react/components/layout/public_navbar.coffee
|
289
290
|
- app/assets/javascripts/tomify/dynamic/react/components/new.coffee
|
290
291
|
- app/assets/javascripts/tomify/dynamic/react/components/pagination.coffee
|
292
|
+
- app/assets/javascripts/tomify/dynamic/react/components/public/feedback.coffee
|
291
293
|
- app/assets/javascripts/tomify/dynamic/react/components/public/passwords/new.coffee
|
292
294
|
- app/assets/javascripts/tomify/dynamic/react/components/public/profile.coffee
|
293
295
|
- app/assets/javascripts/tomify/dynamic/react/components/public/sessions/new.coffee
|
@@ -296,6 +298,7 @@ files:
|
|
296
298
|
- app/assets/javascripts/tomify/dynamic/react/components/public/users/edit.coffee
|
297
299
|
- app/assets/javascripts/tomify/dynamic/react/components/public/users/new.coffee
|
298
300
|
- app/assets/javascripts/tomify/dynamic/react/components/public/users/show.coffee
|
301
|
+
- app/assets/javascripts/tomify/dynamic/react/components/show.coffee
|
299
302
|
- app/assets/javascripts/tomify/dynamic/react/mixins/follow.coffee
|
300
303
|
- app/assets/javascripts/tomify/dynamic/react/mixins/will-initialize.coffee
|
301
304
|
- app/assets/javascripts/tomify/dynamic/react/mount.coffee
|
@@ -310,18 +313,21 @@ files:
|
|
310
313
|
- app/assets/stylesheets/tomify/_pagination.scss
|
311
314
|
- app/assets/stylesheets/tomify/_variables.scss
|
312
315
|
- app/controllers/tomify/admin/controller.rb
|
316
|
+
- app/controllers/tomify/admin/feedback_controller.rb
|
313
317
|
- app/controllers/tomify/admin/pages_controller.rb
|
314
318
|
- app/controllers/tomify/admin/settings_controller.rb
|
315
319
|
- app/controllers/tomify/admin/sidebars_controller.rb
|
316
320
|
- app/controllers/tomify/admin/uploads_controller.rb
|
317
321
|
- app/controllers/tomify/admin/users_controller.rb
|
318
322
|
- app/controllers/tomify/api/admin/controller.rb
|
323
|
+
- app/controllers/tomify/api/admin/feedback_controller.rb
|
319
324
|
- app/controllers/tomify/api/admin/pages_controller.rb
|
320
325
|
- app/controllers/tomify/api/admin/settings_controller.rb
|
321
326
|
- app/controllers/tomify/api/admin/sidebars_controller.rb
|
322
327
|
- app/controllers/tomify/api/admin/uploads_controller.rb
|
323
328
|
- app/controllers/tomify/api/admin/users_controller.rb
|
324
329
|
- app/controllers/tomify/api/public/controller.rb
|
330
|
+
- app/controllers/tomify/api/public/feedback_controller.rb
|
325
331
|
- app/controllers/tomify/api/public/passwords_controller.rb
|
326
332
|
- app/controllers/tomify/api/public/sessions_controller.rb
|
327
333
|
- app/controllers/tomify/api/public/subscriptions_controller.rb
|
@@ -337,6 +343,7 @@ files:
|
|
337
343
|
- app/controllers/tomify/concerns/default/navbar_helper.rb
|
338
344
|
- app/controllers/tomify/concerns/default/react_helper.rb
|
339
345
|
- app/controllers/tomify/public/controller.rb
|
346
|
+
- app/controllers/tomify/public/feedback_controller.rb
|
340
347
|
- app/controllers/tomify/public/pages_controller.rb
|
341
348
|
- app/controllers/tomify/public/profiles_controller.rb
|
342
349
|
- app/controllers/tomify/public/sessions_controller.rb
|
@@ -346,16 +353,19 @@ files:
|
|
346
353
|
- app/helpers/tomify/email_helper.rb
|
347
354
|
- app/helpers/tomify/render_helper.rb
|
348
355
|
- app/helpers/tomify/timezone_helper.rb
|
356
|
+
- app/mailers/tomify/feedback_mailer.rb
|
349
357
|
- app/mailers/tomify/user_mailer.rb
|
350
358
|
- app/mailers/tomify_mailer.rb
|
351
359
|
- app/models/tomify/activity.rb
|
352
360
|
- app/models/tomify/concerns/activity.rb
|
361
|
+
- app/models/tomify/concerns/feedback.rb
|
353
362
|
- app/models/tomify/concerns/page.rb
|
354
363
|
- app/models/tomify/concerns/sidebar.rb
|
355
364
|
- app/models/tomify/concerns/subscription.rb
|
356
365
|
- app/models/tomify/concerns/token.rb
|
357
366
|
- app/models/tomify/concerns/upload.rb
|
358
367
|
- app/models/tomify/concerns/user.rb
|
368
|
+
- app/models/tomify/feedback.rb
|
359
369
|
- app/models/tomify/page.rb
|
360
370
|
- app/models/tomify/setting.rb
|
361
371
|
- app/models/tomify/setting/boolean.rb
|
@@ -373,6 +383,7 @@ files:
|
|
373
383
|
- app/uploaders/tomify_uploader.rb
|
374
384
|
- app/views/templates/contact.haml
|
375
385
|
- app/views/templates/default.haml
|
386
|
+
- app/views/templates/feedback.haml
|
376
387
|
- app/views/templates/sidebar.haml
|
377
388
|
- app/views/tomify/defaults/_body.haml
|
378
389
|
- app/views/tomify/defaults/_container.haml
|
@@ -382,6 +393,8 @@ files:
|
|
382
393
|
- app/views/tomify/defaults/_sharing.haml
|
383
394
|
- app/views/tomify/layouts/application.haml
|
384
395
|
- app/views/tomify/layouts/mailer.haml
|
396
|
+
- app/views/tomify/mailers/feedback_mailer/admin.haml
|
397
|
+
- app/views/tomify/mailers/feedback_mailer/user.haml
|
385
398
|
- app/views/tomify/mailers/partials/_signature.haml
|
386
399
|
- app/views/tomify/mailers/partials/_unsubscribe.haml
|
387
400
|
- app/views/tomify/mailers/user_mailer/invite.haml
|
@@ -390,6 +403,7 @@ files:
|
|
390
403
|
- config/initializers/assets.rb
|
391
404
|
- config/initializers/database.rb
|
392
405
|
- config/initializers/flash_patch.rb
|
406
|
+
- config/initializers/inflectors.rb
|
393
407
|
- config/initializers/mailer.rb
|
394
408
|
- config/initializers/renderers.rb
|
395
409
|
- config/initializers/settings.rb
|
@@ -406,10 +420,12 @@ files:
|
|
406
420
|
- db/migrate/19900000000009_add_invited_to_users.rb
|
407
421
|
- db/migrate/19900000000010_add_template_to_sidebars.rb
|
408
422
|
- db/migrate/19900000000011_create_activities.rb
|
423
|
+
- db/migrate/19900000000012_create_feedback.rb
|
409
424
|
- db/seeds.rb
|
410
425
|
- lib/generators/tomify/bundle/bundle_generator.rb
|
411
426
|
- lib/generators/tomify/bundle/templates/default.js
|
412
427
|
- lib/generators/tomify/bundle/templates/react.js
|
428
|
+
- lib/previews/tomify/feedback_preview.rb
|
413
429
|
- lib/previews/tomify/preview.rb
|
414
430
|
- lib/previews/tomify/user_preview.rb
|
415
431
|
- lib/tasks/package.rake
|