character 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -1
  3. data/.rspec +1 -0
  4. data/README.md +185 -14
  5. data/Rakefile +8 -1
  6. data/app/assets/images/character/logo.jpg +0 -0
  7. data/app/assets/javascripts/character.coffee +134 -0
  8. data/app/assets/javascripts/character/dashboard/_visitors.coffee +27 -0
  9. data/app/assets/javascripts/character/dashboard/layout.coffee +156 -0
  10. data/app/assets/javascripts/character/dashboard/module.coffee +51 -0
  11. data/app/assets/javascripts/character/generic/details.coffee +233 -0
  12. data/app/assets/javascripts/character/generic/helpers/compact_object.coffee +7 -0
  13. data/app/assets/javascripts/character/generic/helpers/data_inputs.coffee +21 -0
  14. data/app/assets/javascripts/character/generic/helpers/date_select.coffee +45 -0
  15. data/app/assets/javascripts/character/generic/helpers/editor.coffee +11 -0
  16. data/app/assets/javascripts/character/generic/helpers/redactor.coffee +38 -0
  17. data/app/assets/javascripts/character/generic/helpers/reorder.coffee +36 -0
  18. data/app/assets/javascripts/character/generic/layout.coffee +40 -0
  19. data/app/assets/javascripts/character/generic/list.coffee +214 -0
  20. data/app/assets/javascripts/character/generic/model.coffee +135 -0
  21. data/app/assets/javascripts/character/generic/module.coffee +157 -0
  22. data/app/assets/javascripts/character/images/module.coffee +148 -0
  23. data/app/assets/javascripts/character/pages/module.coffee +43 -0
  24. data/app/assets/javascripts/character/posts/module.coffee +113 -0
  25. data/app/assets/javascripts/character/settings/_admins.coffee +61 -0
  26. data/app/assets/javascripts/character/settings/_authors.coffee +56 -0
  27. data/app/assets/javascripts/character/settings/_categories.coffee +61 -0
  28. data/app/assets/javascripts/character/settings/_layout.coffee +7 -0
  29. data/app/assets/javascripts/character/settings/_redirects.coffee +56 -0
  30. data/app/assets/javascripts/character/settings/_website.coffee +7 -0
  31. data/app/assets/javascripts/character/settings/details.coffee +16 -0
  32. data/app/assets/javascripts/character/settings/layout.coffee +46 -0
  33. data/app/assets/javascripts/character/settings/module.coffee +78 -0
  34. data/app/assets/stylesheets/character.scss +37 -0
  35. data/app/assets/stylesheets/character/_admins.scss +30 -0
  36. data/app/assets/stylesheets/character/_authors.scss +30 -0
  37. data/app/assets/stylesheets/character/_categories.scss +32 -0
  38. data/app/assets/stylesheets/character/_dashboard.scss +143 -0
  39. data/app/assets/stylesheets/character/_posts.scss +93 -0
  40. data/app/assets/stylesheets/character/_redirects.scss +35 -0
  41. data/app/assets/stylesheets/character/base.scss +967 -0
  42. data/app/assets/stylesheets/character/typography.scss +29 -0
  43. data/app/controllers/character/api_controller.rb +170 -0
  44. data/app/controllers/character/application_controller.rb +37 -0
  45. data/app/controllers/character/settings_controller.rb +72 -0
  46. data/app/controllers/concerns/character/auth_concern.rb +41 -0
  47. data/app/controllers/concerns/character/instance_concern.rb +31 -0
  48. data/app/controllers/concerns/character/json_object_concern.rb +32 -0
  49. data/app/controllers/concerns/character/model_class_concern.rb +28 -0
  50. data/app/controllers/concerns/character/params_concern.rb +33 -0
  51. data/app/controllers/concerns/character/templates_concern.rb +32 -0
  52. data/app/controllers/concerns/not_found.rb +18 -0
  53. data/app/controllers/concerns/website_settings.rb +18 -0
  54. data/app/controllers/pages_controller.rb +8 -0
  55. data/app/controllers/posts_controller.rb +43 -0
  56. data/app/helpers/character_helper.rb +8 -0
  57. data/app/helpers/page_helper.rb +67 -0
  58. data/app/inputs/foundation_string_input.rb +44 -0
  59. data/app/inputs/foundation_switch_input.rb +35 -0
  60. data/app/models/character/image.rb +12 -0
  61. data/app/models/character/page.rb +21 -0
  62. data/app/models/character/post.rb +32 -12
  63. data/app/models/character/post_author.rb +22 -0
  64. data/app/models/character/post_category.rb +21 -0
  65. data/app/models/character/redirect.rb +15 -0
  66. data/app/models/character/settings/variable.rb +23 -0
  67. data/app/models/character/sitemap/sitemap_generator_helper.rb +15 -0
  68. data/app/models/character/user.rb +29 -0
  69. data/app/models/concerns/created_ago.rb +12 -0
  70. data/app/models/concerns/hideable.rb +27 -0
  71. data/app/models/concerns/orderable.rb +8 -0
  72. data/app/models/concerns/report.rb +11 -0
  73. data/app/models/concerns/report_daily.rb +32 -0
  74. data/app/models/concerns/report_monthly.rb +18 -0
  75. data/app/models/concerns/report_weekly.rb +19 -0
  76. data/app/models/concerns/updated_ago.rb +12 -0
  77. data/app/models/reports/analytics_daily.rb +26 -0
  78. data/app/models/reports/analytics_monthly.rb +16 -0
  79. data/app/models/reports/analytics_weekly.rb +16 -0
  80. data/app/services/google_analytics.rb +43 -0
  81. data/app/uploaders/character/image_uploader.rb +22 -0
  82. data/app/uploaders/character/settings/file_uploader.rb +5 -0
  83. data/app/views/character/character.html.erb +67 -0
  84. data/app/views/character/generic/form.html.erb +8 -0
  85. data/app/views/character/pages/form.html.erb +28 -0
  86. data/app/views/character/posts/form.html.erb +38 -0
  87. data/app/views/character/settings/admins.html.erb +29 -0
  88. data/app/views/character/settings/post_authors.html.erb +28 -0
  89. data/app/views/character/settings/post_categories.html.erb +31 -0
  90. data/app/views/character/settings/redirects.html.erb +30 -0
  91. data/app/views/character/settings/settings_group.html.erb +67 -0
  92. data/app/views/errors/not_found.html.erb +157 -0
  93. data/app/views/pages/_default.html.erb +3 -0
  94. data/app/views/pages/_redactor.html.erb +3 -0
  95. data/app/views/pages/show.html.erb +5 -0
  96. data/app/views/posts/_post.html.erb +17 -0
  97. data/app/views/posts/author.html.erb +18 -0
  98. data/app/views/posts/category.html.erb +18 -0
  99. data/app/views/posts/index.html.erb +18 -0
  100. data/app/views/posts/rss.builder +19 -0
  101. data/app/views/posts/show.html.erb +14 -0
  102. data/app/views/shared/_google_analytics.html.erb +13 -0
  103. data/character.gemspec +48 -5
  104. data/doc/README_old.md +161 -0
  105. data/doc/generic_app.md +19 -0
  106. data/doc/img/demo-1.jpg +0 -0
  107. data/doc/img/demo-2.jpg +0 -0
  108. data/doc/img/demo-3.jpg +0 -0
  109. data/doc/img/demo-4.jpg +0 -0
  110. data/doc/img/demo-5.jpg +0 -0
  111. data/doc/instances.md +39 -0
  112. data/doc/settings.md +1 -0
  113. data/lib/character.rb +29 -1
  114. data/lib/character/engine.rb +33 -1
  115. data/lib/character/generators/bootstrap_generator.rb +51 -0
  116. data/lib/character/instance.rb +59 -0
  117. data/lib/character/routing.rb +42 -5
  118. data/lib/character/settings.rb +101 -0
  119. data/lib/character/templates/admin.coffee +15 -0
  120. data/lib/character/templates/admin.scss +3 -0
  121. data/lib/character/templates/application.html.erb +44 -0
  122. data/lib/character/templates/application.scss +12 -0
  123. data/lib/character/templates/assets.rb +1 -0
  124. data/lib/character/templates/initializer.rb +5 -0
  125. data/lib/character/templates/settings.scss +11 -0
  126. data/lib/character/templates/settings.yml +67 -0
  127. data/lib/character/templates/typography.scss +13 -0
  128. data/lib/character/version.rb +2 -2
  129. data/lib/mongoid/carrierwave_serialization_patch.rb +9 -0
  130. data/lib/tasks/analytics.rake +52 -0
  131. data/test/config/application.rb +65 -0
  132. data/test/config/mongoid.yml +12 -0
  133. data/test/config/secrets.yml +22 -0
  134. data/test/controllers/character/api_controller_test.rb +94 -0
  135. data/test/factories/product_factory.rb +5 -0
  136. data/test/lib/character/engine_test.rb +33 -0
  137. data/test/lib/character/routing_test.rb +31 -0
  138. data/test/test_helper.rb +48 -0
  139. data/vendor/assets/javascripts/backbone.js +944 -794
  140. data/vendor/assets/javascripts/jquery.fileupload.js +1426 -0
  141. data/vendor/assets/javascripts/jquery.form.js +1278 -0
  142. data/vendor/assets/javascripts/jquery.iframe-transport.js +214 -0
  143. data/vendor/assets/javascripts/raphael.js +8117 -0
  144. data/vendor/assets/javascripts/raphael.morris.js +1885 -0
  145. data/vendor/assets/javascripts/underscore.inflection.js +177 -0
  146. data/vendor/assets/javascripts/underscore.string.js +1 -1
  147. data/vendor/assets/stylesheets/csspinner.css +361 -0
  148. data/vendor/assets/stylesheets/normalize.css +423 -0
  149. metadata +499 -49
  150. data/app/controllers/character/posts_controller.rb +0 -27
  151. data/lib/generators/character/install_generator.rb +0 -42
  152. data/lib/generators/character/templates/README +0 -1
  153. data/lib/generators/character/templates/admin/character.rb +0 -3
  154. data/vendor/assets/fonts/general_foundicons.eot +0 -0
  155. data/vendor/assets/fonts/general_foundicons.svg +0 -15
  156. data/vendor/assets/fonts/general_foundicons.ttf +0 -0
  157. data/vendor/assets/fonts/general_foundicons.woff +0 -0
  158. data/vendor/assets/javascripts/character/index.js.coffee +0 -53
  159. data/vendor/assets/javascripts/character/models/post.js.coffee +0 -39
  160. data/vendor/assets/javascripts/character/views/app.js.coffee +0 -81
  161. data/vendor/assets/javascripts/character/views/editor.js.coffee +0 -231
  162. data/vendor/assets/javascripts/character/views/editor_settings.js.coffee +0 -44
  163. data/vendor/assets/javascripts/character/views/index.js.coffee +0 -116
  164. data/vendor/assets/javascripts/character/views/preview.js.coffee +0 -49
  165. data/vendor/assets/javascripts/jquery.smartresize.js +0 -30
  166. data/vendor/assets/javascripts/lodash.js +0 -4258
  167. data/vendor/assets/javascripts/showdown.js +0 -62
  168. data/vendor/assets/stylesheets/character/_base.css.scss +0 -84
  169. data/vendor/assets/stylesheets/character/_icons.css.scss.erb +0 -96
  170. data/vendor/assets/stylesheets/character/_view_editor.css.scss +0 -115
  171. data/vendor/assets/stylesheets/character/_view_index.css.scss +0 -73
  172. data/vendor/assets/stylesheets/character/_view_preview.css.scss +0 -49
  173. data/vendor/assets/stylesheets/character/index.css.scss +0 -32
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5aaba1245301fea933c7e85fe1784c98dabbafc0
4
+ data.tar.gz: c8db8ace583070cc476269ba4590d355b5578503
5
+ SHA512:
6
+ metadata.gz: e3cd3b297d2a5df01ca497df51d48c279c2506dfad4651732d257b0d00b8da1fe74bb30144fa3a6befeeae18b5692bb9f659fc8594de9221aadc3042770a15b9
7
+ data.tar.gz: 2423e88b08b927a97d30c68e04a4445a79010939e92e391812bc28eba9dadd0fe8046311f793c2790b1142abc76d826285ef1c4143db4a22fcac4d4cba0177fb
data/.gitignore CHANGED
@@ -1 +1,4 @@
1
- .DS_Store
1
+ .DS_Store
2
+
3
+ /log/*.log
4
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/README.md CHANGED
@@ -1,20 +1,191 @@
1
- # Character
1
+ # Character Rails Admin
2
2
 
3
- This is blogging app based on Ghost designed to be used with Active Admin.
3
+ Have you heard of [Django](https://www.djangoproject.com/), [Wordpress](https://wordpress.org/), [Active Admin](http://activeadmin.info/), [Locomotive](http://locomotivecms.com/)? Yeah?! Those are all awesome... and **Character** is BETTER!
4
4
 
5
- ## Installation
5
+ ![Character Image](http://character.s3.amazonaws.com/character1.jpg)
6
6
 
7
+ ## Setup new [Rails 4.1](http://rubyonrails.org) project
8
+
9
+ rails new ProjectName -T -O
10
+
11
+ Add gems to the ```Gemfile```:
12
+
13
+ # Mongoid ORM + Character
14
+ gem 'carrierwave-mongoid', github: 'carrierwaveuploader/carrierwave-mongoid', require: 'carrierwave/mongoid'
15
+ gem 'character'
16
+
17
+ Run bundle and run generators:
18
+
19
+ bundle ; rails g mongoid:config ; rails g character:bootstrap
7
20
 
8
21
  ## TODO
9
22
 
10
- 1. After post date is changed fix it's position in the list if required
11
- 2. Implement quick blog post search
12
- 3. Implement image uploading functionality
13
- 4. Implement google analytics dashboard
14
- 5. Implement youtube videos support for markdown
15
- 6. Add a quick syntax helper for editor
16
- 7. Fix word counter to do not count single symbols like '-'
17
- 8. Add metadata input fields in post settings: tags, description, og fields
18
- 9. Optional tags, categories support
19
- 10. Featured image uploading via post settings
20
- 11.
23
+ . Save unpublished post
24
+ . Fix paste code issue for redactor.js + chrome
25
+
26
+
27
+ ## Routes
28
+
29
+ After character generator finishes it's dirty business, in ```/config/routes.rb``` you see:
30
+
31
+ mount_character_instance 'admin'
32
+
33
+ This mounts character instance **admin** to ```/admin``` path and make character app accessible there. There are also two optional helpers ```mount_posts_at``` and ```mount_pages_at```, they mount default controllers to routes as well. Remove them if no need in **posts** or **pages** app.
34
+
35
+ Instance name **admin** could be changed, and you can use something different. This option is here for the case when a few character instances are required.
36
+
37
+ - mount_character_instance
38
+ - mount_posts_at
39
+ - mount_pages_at
40
+
41
+
42
+ ## Running Automated Tests
43
+
44
+ `$ bundle exec rake test`
45
+
46
+
47
+ ## Modules
48
+
49
+ - chr.genericModule()
50
+ - chr.postsModule()
51
+ - chr.pagesModule()
52
+ - chr.settingsModule()
53
+ - chr.settingsWebsite()
54
+ - chr.settingsPostCategories()
55
+ - chr.settingsAdmins()
56
+ - chr.settingsRedirects()
57
+
58
+
59
+ ## Forms
60
+
61
+ To have custom form implementation for model, create ```form.html``` in ```/app/views/admin/model_names/``` — replace *model_names* with pluralized models name and if needed character instance name *admin* (default).
62
+
63
+ ### Generic form template
64
+
65
+ Generic form template looks like this:
66
+
67
+ <%= simple_form_for @object, url: @form_action_url, method: :post do |f| %>
68
+ <%= f.input :name %>
69
+ <% end %>
70
+
71
+ Checkout [Simple Form](https://github.com/plataformatec/simple_form) reference for all options (there are tons of them) which are available here.
72
+
73
+ **No need to include SUBMIT button in form!**
74
+
75
+ ### Hideable
76
+
77
+ If you want to make model hideable include ```include Hideable``` in model and add hidden field to your form:
78
+
79
+ <%= f.input :hidden, as: :hidden %>
80
+
81
+ This will add an eye button trigger in the admin header, which allows to switch state for model.
82
+
83
+ ### Inline forms
84
+
85
+ Form inline elements could be added with this code (images example):
86
+
87
+ <div class='chr-form-nested chr-form-nested-images sortable-list'>
88
+ <%= f.fields_for :images do |ff| %>
89
+ <%= ff.link_to_remove "Remove" %>
90
+ <%= image_tag ff.object.image.small.url %>
91
+ <%= ff.input :title, placeholder: 'Image title' %>
92
+ <%= ff.input :image %>
93
+ <%= ff.input :_position, as: :hidden %>
94
+ <% end %>
95
+ <%= f.link_to_add "Add an Image", :images %>
96
+ </div>
97
+
98
+ - This template is based on [Nested Forms](https://github.com/ryanb/nested_form) gem by Ryan Bates, checkout docs for implementation details.
99
+ - Including ```sortable-list``` class and ```<%= ff.input :_position, as: :hidden %>``` make inline objects reorderable.
100
+ - This example uses ```chr-form-nested-images``` class for layout styling.
101
+
102
+
103
+ ## Models
104
+
105
+ - Character::Post
106
+ - Character::PostCategory
107
+ - Character::Page
108
+ - Character::Image
109
+ - Character::Settings::Variable
110
+ - Character::User
111
+ - Character::Redirect
112
+
113
+
114
+ ## Concerns
115
+
116
+ - UpdatedAgo
117
+ - CreatedAgo
118
+ - Orderable
119
+ - Hideable
120
+
121
+
122
+ ## Helpers
123
+
124
+ - Character::SitemapGeneratorHelper
125
+
126
+
127
+ ## Shortcuts
128
+
129
+ - CMD+s — save changes
130
+ - CMD+e — toggle fullscreen
131
+
132
+
133
+ ## API
134
+
135
+ **[Character.Generic.DetailsView](https://github.com/slate-studio/character/blob/master/app/assets/javascripts/character/generic/details.coffee) and [Character.Settings.DetailsView](https://github.com/slate-studio/character/blob/master/app/assets/javascripts/character/settings/details.coffee)**
136
+
137
+ These are blank methods that could be overriden to extend view functionality. Example of ```Character.Generic.DetailsView``` override: [character/posts/module.coffee](https://github.com/slate-studio/character/blob/master/app/assets/javascripts/character/posts/module.coffee)
138
+
139
+ - @beforeContentRequest()
140
+ - @beforeRenderContent()
141
+ - @beforeFormHelpersStart()
142
+ - @afterRenderContent()
143
+ - @beforeSave()
144
+ - @beforeFormSubmit(arr, $form, options)
145
+ - @afterFormSubmitSuccess(responseText, statusText, xhr, $form)
146
+ - @beforeOnClose()
147
+ - @afterOnClose()
148
+
149
+
150
+ ## Instances
151
+
152
+ Initializers for different instances are in ```config/initializers/character.rb```:
153
+
154
+ Character.configure do |config|
155
+ config.title = 'Project Title | Admin'
156
+ end
157
+
158
+ Options:
159
+
160
+ - @title = 'Character'
161
+ - @user_model = 'Character::User'
162
+ - @development_auto_login = false
163
+ - @force_ssl = true
164
+
165
+ ## Analytics
166
+
167
+ * Create a new project (```GA_APP_NAME```) at: [https://console.developers.google.com/project](https://console.developers.google.com/project)
168
+ * Enable Analytics under **APIs & auth** -> **APIs** -> Analytics API
169
+ * Create another client under **Credentials**
170
+ * Download and put **key-file** (```GA_KEY_FILE_NAME```) to your projects ```config``` folder
171
+ * Go to you (Analytics)[www.google.com/analytics] account and add the **Service account** email address to your account (```GA_SERVICE_ACCOUNT_EMAIL```). It should be something like ```something-long@developer.gserviceaccount.com```.
172
+
173
+ Set all variables in server environment:
174
+
175
+ - ```GA_APP_NAME```
176
+ - ```GA_SERVICE_ACCOUNT_EMAIL```
177
+ - ```GA_KEY_FILE_NAME```
178
+ - ```GA_PROFILE_ID```
179
+
180
+
181
+ ## Settings
182
+
183
+
184
+ ## Reports
185
+
186
+ (to be continued...)
187
+
188
+
189
+ ## Redirects
190
+
191
+ Redirects app allows to quickly setup 301/302 redirects for the website.
data/Rakefile CHANGED
@@ -4,4 +4,11 @@ spec = Gem::Specification.load(Dir['*.gemspec'].first)
4
4
  gem = Gem::PackageTask.new(spec)
5
5
  gem.define()
6
6
 
7
- #gem push pkg/character-version.gem
7
+ # This will let us run our tests by typing "rake test" from the gem root folder
8
+ require 'rake/testtask'
9
+ Rake::TestTask.new do |t|
10
+ t.libs << 'test'
11
+ t.test_files = FileList['test/**/*_test.rb']
12
+ # t.warning = true
13
+ # t.verbose = true
14
+ end
@@ -0,0 +1,134 @@
1
+ #= require jquery
2
+ #= require jquery_ujs
3
+ #= require jquery.form
4
+ #= require jquery.ui.sortable
5
+ #= require jquery.ui.widget
6
+ #= require jquery.iframe-transport
7
+ #= require jquery.fileupload
8
+ #= require jquery_nested_form
9
+
10
+ #= require underscore
11
+ #= require underscore.string
12
+ #= require underscore.inflection
13
+ #= require backbone
14
+ #= require backbone.marionette
15
+
16
+ #= require moment
17
+ #= require character_editor
18
+ #= require redactor
19
+
20
+ #= require_self
21
+ #= require ./character/generic/module
22
+ #= require ./character/settings/module
23
+ #= require ./character/posts/module
24
+ #= require ./character/images/module
25
+ #= require ./character/pages/module
26
+ #= require ./character/dashboard/module
27
+
28
+ @Character ||= {}
29
+
30
+ _.mixin(_.str.exports())
31
+
32
+ #
33
+ # Unsuck Your Backbone
34
+ # https://speakerdeck.com/ammeep/unsuck-your-backbone
35
+ #
36
+
37
+ #
38
+ # Marionette.js Application Documentation
39
+ # https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.application.md
40
+ #
41
+ @chr = new Backbone.Marionette.Application()
42
+
43
+ # App Layout
44
+ # /app/views/character.html.erb
45
+ @chr.addRegions
46
+ menu: '#menu'
47
+ content: '#content'
48
+
49
+ API =
50
+ addMenuItem: (path, icon, title) ->
51
+ $menuItems = $('#menu_items')
52
+ $menuItems.append """<li>
53
+ <a href='#/#{ path }' class='chr-menu-item-#{ path }' title='#{ title }'>
54
+ <i class='chr-menu-icon fa fa-#{ icon }'></i><div class='chr-menu-title'>#{ title }</div>
55
+ </a>
56
+ </li>"""
57
+
58
+ showModule: (module) ->
59
+ if chr.currentModuleName != module.moduleName
60
+ chr.currentModuleName = module.moduleName
61
+
62
+ name = module.moduleName
63
+ layout = module.layout
64
+
65
+ $menuEl = $('#menu')
66
+ $menuEl.find('a.active').removeClass('active')
67
+ $menuEl.find("a.chr-menu-item-#{name}").addClass('active')
68
+
69
+ chr.content.show(layout, { preventDestroy: true })
70
+
71
+ $('#content').attr('class', "chr-content #{name}")
72
+
73
+ closeDetailsView: ->
74
+ Backbone.history.navigate('#/' + chr.currentPath, { trigger: true })
75
+
76
+ showError: (response) ->
77
+ $container = $('#character')
78
+ $overlay = $('#chr_error')
79
+
80
+ if $overlay.length == 0
81
+ $container.after """<div id='chr_error' class='chr-error'>
82
+ <div id='chr_error_message' class='chr-error-message'></div>
83
+ <button id='chr_error_close' type='button' class='chr-error-close'>
84
+ <i class='chr-icon icon-close'></i>
85
+ </button>
86
+ </div>"""
87
+ $overlay = $('#chr_error')
88
+
89
+ entityMap = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;', '/': '&#x2F;' }
90
+ escapeHtml = (string) -> String(string).replace(/[&<>"'\/]/g, (s) -> entityMap[s])
91
+ responseText = escapeHtml(response.responseText)
92
+
93
+ $('#chr_error_message').html "<pre>#{ responseText }</pre>"
94
+ $('#chr_error_close').on 'click', -> chr.execute('closeError')
95
+
96
+ $overlay.addClass('open')
97
+ $container.addClass('error-open')
98
+
99
+ closeError: ->
100
+ $('#chr_error').removeClass('open')
101
+ $('#character').removeClass('error-open')
102
+ $('#chr_error_close').off 'click'
103
+
104
+ _.map API, (method, name) => @chr.commands.setHandler(name, method)
105
+
106
+
107
+ @chr.on "before:start", (@options) -> # maps options!
108
+ # shortcuts
109
+ window.shortcuts = new window.keypress.Listener()
110
+
111
+ # close dialogs and details view on esc
112
+ window.shortcuts.register_combo
113
+ keys: 'esc'
114
+ is_exclusive: true
115
+ on_keyup: (event) ->
116
+ if $('#chr_images').hasClass 'open'
117
+ chr.execute('hideImages')
118
+ else if $('#chr_error').hasClass 'open'
119
+ chr.execute('closeError')
120
+ else
121
+ chr.execute('closeDetailsView')
122
+
123
+
124
+ @chr.on "start", ->
125
+ # start history
126
+ if Backbone.history
127
+ Backbone.history.start()
128
+
129
+ # jump to first menu item when login to admin
130
+ if location.hash == ''
131
+ location.hash = $('#menu a:eq(1)').attr('href')
132
+
133
+ # disable default action for browser when drop image to window
134
+ $(document).bind 'drop dragover', (e) -> e.preventDefault()
@@ -0,0 +1,27 @@
1
+ @Character.Dashboard.Charts ||= {}
2
+ @Character.Dashboard.Charts.visitors = (layout) ->
3
+ title = 'Visitors'
4
+ color = '#fac043'
5
+
6
+ if layout.chartType() == 'day'
7
+ reportModel = 'Reports-AnalyticsDaily'
8
+ dateFormat = (d) -> moment(d).format("MMM D")
9
+ else if layout.chartType() == 'week'
10
+ reportModel = 'Reports-AnalyticsWeekly'
11
+ dateFormat = (d) ->
12
+ weekStart = moment(d).format("MMM D")
13
+ weekEnd = moment(d).add('days', 6).format("MMM D")
14
+ "#{weekStart} - #{weekEnd}"
15
+ else if layout.chartType() == 'month'
16
+ reportModel = 'Reports-AnalyticsMonthly'
17
+ dateFormat = (d) -> moment(d).format("MMM YYYY")
18
+
19
+ fields = [ 'visitors' ].join(',')
20
+ startDate = layout.dateFrom()
21
+ stopDate = layout.dateTo()
22
+
23
+ url = "/admin/#{reportModel}?f=#{fields}&where__report_date=$gte:#{ startDate },$lte:#{ stopDate }&o=report_date:asc&pp=40"
24
+
25
+ $.get url, {}, (data) =>
26
+ d = _.map data, (r) -> { y: dateFormat(r.report_date), a: r.visitors }
27
+ layout.drawBarChart title, color, d
@@ -0,0 +1,156 @@
1
+ #= require ./_visitors
2
+
3
+ #
4
+ # Marionette.js Layout Documentation
5
+ # https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.layout.md
6
+ #
7
+ @Character.Dashboard.Layout = Backbone.Marionette.LayoutView.extend
8
+ className: 'chr-module-generic'
9
+
10
+ template: -> """<header class="chr-details-header"><div class="title">Dashboard</div></header>
11
+ <div id=dashboard_view class='dashboard-view'>
12
+ <select id=dashboard_chart_select class='dashboard-chart-select'></select>
13
+ <aside class='dashboard-chart-options'>
14
+ <select id=dashboard_chart_type_select>
15
+ <option value=day>Day</option>
16
+ <option value=week>Week</option>
17
+ <option value=month>Month</option>
18
+ </select>
19
+ <input type=date id=dashboard_chart_date_from />
20
+
21
+ <input type=date id=dashboard_chart_date_to />
22
+ </aside>
23
+ <div id=dashboard_chart_wrapper class='dashboard-chart-wrapper'>
24
+ <div id=dashboard_chart></div>
25
+ </div>
26
+ <div id=dashboard_footer></div>
27
+ </div>"""
28
+
29
+ regions:
30
+ footer: '#dashboard_footer'
31
+
32
+ ui:
33
+ view: '#dashboard_view'
34
+ chart: '#dashboard_chart'
35
+ chartWrapper: '#dashboard_chart_wrapper'
36
+ chartSelect: '#dashboard_chart_select'
37
+ typeSelect: '#dashboard_chart_type_select'
38
+ dateFrom: '#dashboard_chart_date_from'
39
+ dateTo: '#dashboard_chart_date_to'
40
+
41
+ _renderChart: ->
42
+ Character.Dashboard.Charts[@chartName](@)
43
+
44
+ _selectChart: ->
45
+ @chartName = @ui.chartSelect.val()
46
+ @_renderChart()
47
+
48
+ chartType: -> @ui.typeSelect.val()
49
+ dateFrom: -> @ui.dateFrom.val()
50
+ dateTo: -> @ui.dateTo.val()
51
+
52
+ setDateRange: (fromDate, toDate) ->
53
+ fromDate ?= moment().subtract('month', 1).format('YYYY-MM-DD')
54
+ toDate ?= moment().format('YYYY-MM-DD')
55
+
56
+ @ui.dateFrom.val(fromDate)
57
+ @ui.dateTo.val(toDate)
58
+
59
+ _selectDateFrom: ->
60
+ fromDate = @dateFrom()
61
+
62
+ if @chartType() == 'day'
63
+ toDate = moment(fromDate).add('month', 1).format('YYYY-MM-DD')
64
+ else if @chartType() == 'week'
65
+ toDate = moment(fromDate).add('weeks', 30).format('YYYY-MM-DD')
66
+ else if @chartType() == 'month'
67
+ toDate = moment(fromDate).add('months', 30).format('YYYY-MM-DD')
68
+
69
+ @setDateRange(fromDate, toDate)
70
+ @_renderChart()
71
+
72
+ _selectDateTo: ->
73
+ toDate = @dateTo()
74
+
75
+ if @chartType() == 'day'
76
+ fromDate = moment(toDate).subtract('month', 1).format('YYYY-MM-DD')
77
+ else if @chartType() == 'week'
78
+ fromDate = moment(toDate).subtract('weeks', 30).format('YYYY-MM-DD')
79
+ else if @chartType() == 'month'
80
+ fromDate = moment(toDate).subtract('months', 30).format('YYYY-MM-DD')
81
+
82
+ @setDateRange(fromDate, toDate)
83
+ @_renderChart()
84
+
85
+ _selectType: -> @_selectDateTo()
86
+
87
+ onRender: ->
88
+ @currentChartType = ''
89
+
90
+ _.chain(Character.Dashboard.Charts).keys().each (key) =>
91
+ title = _(key).capitalize()
92
+ @ui.chartSelect.append("<option value=#{key}>#{title}</option>")
93
+
94
+ # TODO: after getting back to this layout from other app events declared in layout doesn't work
95
+ # so doing this right here:
96
+ @ui.chartSelect.on 'change', (e) => @_selectChart()
97
+ @ui.typeSelect.on 'change', (e) => @_selectType()
98
+ @ui.dateFrom.on 'change', (e) => @_selectDateFrom()
99
+ @ui.dateTo.on 'change', (e) => @_selectDateTo()
100
+
101
+ @afterRenderContent?()
102
+
103
+ onDestroy: ->
104
+ # NOTE: seems to be never called in current implementation
105
+ @ui.chartSelect.off 'change'
106
+ @ui.typeSelect.off 'change'
107
+ @ui.dateFrom.off 'change'
108
+ @ui.dateTo.off 'change'
109
+
110
+ @afterOnClose?()
111
+
112
+ updateScope: (@chartName, callback) ->
113
+ @chartName ?= 'visitors'
114
+ @_renderChart()
115
+
116
+ setChartTitle: (title) ->
117
+ @ui.chartWrapper.attr('data-title', title)
118
+
119
+ resetCurrentChart: ->
120
+ @ui.chart.html('')
121
+ @currentChart = null
122
+ @currentChartType = ''
123
+
124
+ drawBarChart: (title, color, data) ->
125
+ if data.length > 0
126
+ if @currentChartType == 'bar'
127
+ @currentChart.options.colors = [ color ]
128
+ @currentChart.options.labels = [ title ]
129
+ @currentChart.setData data
130
+ else
131
+ @resetCurrentChart()
132
+
133
+ options =
134
+ element: 'dashboard_chart'
135
+ data: data
136
+ barColors: [ color ]
137
+ labels: [ title ]
138
+ xkey: 'y'
139
+ ykeys: [ 'a' ]
140
+ # styles
141
+ hideHover: true
142
+ gridTextFamily: 'sans-serif'
143
+ gridTextColor: '#a9b1b5'
144
+ gridTextSize: 11
145
+ barOpacity: 0.6
146
+ barSizeRatio: 0.97
147
+ numLines: 6
148
+ gridStrokeWidth: 0.1
149
+
150
+ # https://github.com/morrisjs/morris.js
151
+ @currentChart = Morris.Bar options
152
+
153
+ @setChartTitle(title)
154
+ @currentChartType = 'bar'
155
+ else
156
+ @setChartTitle('No data')