tomify 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/tomify/dynamic/classes/form.coffee +1 -0
  3. data/app/assets/javascripts/tomify/dynamic/classes/model.coffee +1 -0
  4. data/app/assets/javascripts/tomify/dynamic/react/components/admin/feedback.coffee +20 -0
  5. data/app/assets/javascripts/tomify/dynamic/react/components/admin/pages.coffee.erb +3 -4
  6. data/app/assets/javascripts/tomify/dynamic/react/components/admin/settings.coffee +2 -2
  7. data/app/assets/javascripts/tomify/dynamic/react/components/admin/sidebars.coffee.erb +2 -2
  8. data/app/assets/javascripts/tomify/dynamic/react/components/admin/uploads.coffee +2 -2
  9. data/app/assets/javascripts/tomify/dynamic/react/components/admin/users.coffee +2 -2
  10. data/app/assets/javascripts/tomify/dynamic/react/components/index.coffee +39 -30
  11. data/app/assets/javascripts/tomify/dynamic/react/components/layout/public_navbar.coffee +2 -2
  12. data/app/assets/javascripts/tomify/dynamic/react/components/public/feedback.coffee +39 -0
  13. data/app/assets/javascripts/tomify/dynamic/react/components/public/subscription.coffee +1 -1
  14. data/app/assets/javascripts/tomify/dynamic/react/components/show.coffee +60 -0
  15. data/app/controllers/tomify/admin/feedback_controller.rb +2 -0
  16. data/app/controllers/tomify/api/admin/feedback_controller.rb +16 -0
  17. data/app/controllers/tomify/api/admin/pages_controller.rb +1 -0
  18. data/app/controllers/tomify/api/admin/settings_controller.rb +1 -0
  19. data/app/controllers/tomify/api/admin/sidebars_controller.rb +1 -0
  20. data/app/controllers/tomify/api/admin/uploads_controller.rb +1 -0
  21. data/app/controllers/tomify/api/public/feedback_controller.rb +15 -0
  22. data/app/controllers/tomify/concerns/default/navbar_helper.rb +1 -0
  23. data/app/controllers/tomify/public/feedback_controller.rb +5 -0
  24. data/app/mailers/tomify/feedback_mailer.rb +13 -0
  25. data/app/models/tomify/concerns/feedback.rb +23 -0
  26. data/app/models/tomify/concerns/page.rb +1 -1
  27. data/app/models/tomify/feedback.rb +3 -0
  28. data/app/models/tomify/setting.rb +1 -1
  29. data/app/views/templates/feedback.haml +1 -0
  30. data/app/views/tomify/mailers/feedback_mailer/admin.haml +17 -0
  31. data/app/views/tomify/mailers/feedback_mailer/user.haml +8 -0
  32. data/app/views/tomify/mailers/partials/_unsubscribe.haml +3 -2
  33. data/app/views/tomify/mailers/user_mailer/invite.haml +2 -2
  34. data/app/views/tomify/mailers/user_mailer/reset_password.haml +5 -5
  35. data/app/views/tomify/mailers/user_mailer/welcome.haml +2 -2
  36. data/config/initializers/inflectors.rb +3 -0
  37. data/config/routes.rb +3 -0
  38. data/db/migrate/19900000000012_create_feedback.rb +15 -0
  39. data/db/seeds.rb +1 -1
  40. data/lib/previews/tomify/feedback_preview.rb +14 -0
  41. data/lib/tomify/version.rb +1 -1
  42. data/lib/tomify.rb +3 -0
  43. metadata +18 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d7adedc7c0800596d7cb43f4fa62d73f062c60de
4
- data.tar.gz: 22f4e06750d3eff48a100d576de05fc0c6f36368
3
+ metadata.gz: 57007dacbc53ef0792f799a4dd59ddc0f1fbd1aa
4
+ data.tar.gz: c599f317a3bd1b022c2b6d3e1a5b108e5abb14a1
5
5
  SHA512:
6
- metadata.gz: 4f317a362b20c86647022e24a3bec525f70ff0b724c84df451d1d723d1de61a80e3e92fcb349fa2c2731bfbb8812f203865cd2a25e2fabc3188b531ba2d47651
7
- data.tar.gz: ac43203ca348e0da245ed209dba4c2520adbe84c29f988dc5e207cccb4f94406cd43e9a2996c758946d34292c328b0b8ef2df4a5bf46788ad5924d6a10628bc3
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
- new: (e) ->
27
- e.preventDefault()
28
- @model.new()
29
- false
30
- edit: (id, e) ->
31
- e.preventDefault()
32
- @model.edit(id)
33
- false
34
- destroy: (id, e) ->
35
- e.preventDefault()
36
- @model.destroy(id).then (response) ->
37
- message type: response.type, text: response.message
38
- false
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
- <a className="btn btn-primary btn-xs" href="#" onClick={@new}>New</a>
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
- if field.name == "actions"
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
- <Edit.Container name={@props.name} form={@editForm} />
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,2 @@
1
+ class Tomify::Admin::FeedbackController < Tomify.controllers.admin
2
+ end
@@ -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
@@ -1,4 +1,5 @@
1
1
  class Tomify::Api::Admin::PagesController < Tomify.controllers.admin_api
2
+ private
2
3
  def permitted_attributes
3
4
  [
4
5
  :parent_id, :sidebar_id,
@@ -1,4 +1,5 @@
1
1
  class Tomify::Api::Admin::SettingsController < Tomify.controllers.admin_api
2
+ private
2
3
  def permitted_attributes
3
4
  [:type, :name, :public, :value, json: {}]
4
5
  end
@@ -1,4 +1,5 @@
1
1
  class Tomify::Api::Admin::SidebarsController < Tomify.controllers.admin_api
2
+ private
2
3
  def permitted_attributes
3
4
  [:active, :name, :heading, :template, :text]
4
5
  end
@@ -1,4 +1,5 @@
1
1
  class Tomify::Api::Admin::UploadsController < Tomify.controllers.admin_api
2
+ private
2
3
  def permitted_attributes
3
4
  [:uuid, :name, :file, :size, :content_type]
4
5
  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,5 @@
1
+ class Tomify::Public::FeedbackController < Tomify.controllers.public
2
+ def new
3
+ render component: "Public.Feedback"
4
+ end
5
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ class Tomify::Feedback < Tomify.models.base
2
+ include Tomify::Concerns::Feedback
3
+ end
@@ -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| !record.name.in? self.class.required_settings }
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
@@ -0,0 +1,8 @@
1
+ %div(style="text-align: center;")
2
+ %p Dear #{@feedback.name},
3
+ %p Thank you for your feedback! You're the best!
4
+ = image_tag "tomify/gifs/woods.gif"
5
+ %p
6
+ Click #{link_to "here", root_url}
7
+ to come back home!
8
+ = render "partials/signature"
@@ -1,3 +1,4 @@
1
1
  - if @_message.to.length == 1
2
- To unsubscribe from all #{setting(:name)} emails
3
- = link_to "click here", subscription_url(email: @_message.to.first)
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"
@@ -0,0 +1,3 @@
1
+ ActiveSupport::Inflector.inflections do |inflect|
2
+ inflect.irregular "feedback", "feedback"
3
+ end
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
@@ -10,7 +10,7 @@ if Tomify.models.setting.count.zero?
10
10
  debug Tomify::Setting::Json.create(name: "aws", value: {
11
11
  access_key: "",
12
12
  secret_key: "",
13
- bucket: "",
13
+ bucket: ""
14
14
  })
15
15
  debug Tomify::Setting::Json.create(name: "email", value: {
16
16
  username: "",
@@ -0,0 +1,14 @@
1
+ class Tomify::UserPreview < Tomify.previews.base
2
+ def admin
3
+ Tomify.mailers.feedback.admin(feedback)
4
+ end
5
+
6
+ def user
7
+ Tomify.mailers.feedback.user(feedback)
8
+ end
9
+
10
+ private
11
+ def feedback
12
+ Tomify.models.feedback.first
13
+ end
14
+ end
@@ -1,3 +1,3 @@
1
1
  module Tomify
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
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.0
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-08 00:00:00.000000000 Z
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