character 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,29 @@
1
+ //---------------------------------------------------------
2
+ // TYPOGRAPHY
3
+ //---------------------------------------------------------
4
+
5
+ @import "rhythm/typography";
6
+
7
+ .character .post, .character .page {
8
+ $baseColor: rgba(0,0,0,0.8);
9
+ $linkColor: #222;
10
+ $baseFontFamily: 'Georgia', serif;
11
+ $headerFontFamily: 'Myriad Pro', sans-serif;
12
+ $codeFontFamily: 'Monaco', monospace;
13
+ $baseFontSize: 20;
14
+ $baseLineHeight: 1.5;
15
+ $h1: 0.33 * 7.3;
16
+ $h2: 0.33 * 4.3;
17
+ $h3: 0.33 * 3.3;
18
+
19
+ @include typography( $baseColor,
20
+ $linkColor,
21
+ $baseFontFamily,
22
+ $headerFontFamily,
23
+ $codeFontFamily,
24
+ $baseFontSize,
25
+ $baseLineHeight,
26
+ $h1,
27
+ $h2,
28
+ $h3 );
29
+ }
@@ -0,0 +1,170 @@
1
+ # Author: Alexander Kravets
2
+ # Slate, 2014
3
+
4
+ class Character::ApiController < ActionController::Base
5
+ include Character::InstanceConcern
6
+ include Character::ModelClassConcern
7
+ include Character::TemplatesConcern
8
+ include Character::JsonObjectConcern
9
+ include Character::AuthConcern
10
+ include Character::ParamsConcern
11
+
12
+ before_filter :authenticate_user
13
+ layout :false
14
+
15
+ # Actions -----------------------------------------------
16
+
17
+ def index
18
+ order_by = params[:o]
19
+ search_query = params[:q] || ''
20
+ page = params[:p] || 1
21
+ per_page = params[:pp] || 25
22
+
23
+ @objects = model_class.unscoped.all
24
+
25
+ # filter
26
+ scopes = params.keys.select { |s| s.starts_with? 'where__' }
27
+ scopes.each do |s|
28
+ field_name = s.gsub('where__', '')
29
+
30
+ filters = {}
31
+ filters_list = params[s].split(',')
32
+
33
+ if params[s].include? ':'
34
+ params[s].split(',').each do |f|
35
+ filters[ f.split(':').first ] = f.split(':').last
36
+ end
37
+
38
+ @objects = @objects.where( field_name => filters )
39
+ else
40
+ @objects = @objects.where( field_name => params[s] )
41
+ end
42
+ end
43
+
44
+
45
+
46
+ # search
47
+ if not search_query.empty?
48
+ @objects = @objects.full_text_search(search_query, match: :all)
49
+ end
50
+
51
+
52
+
53
+ # order
54
+ # format: &order_by=field_name:direction,field_name2:direction,...&
55
+ if order_by
56
+ filters = {}
57
+ order_by.split(',').each do |filter|
58
+ filter_options = filter.split(':')
59
+ filters[filter_options.first] = filter_options.last
60
+ object_fields.append(filter_options.first)
61
+ end
62
+
63
+ @objects = @objects.order_by(filters)
64
+ end
65
+
66
+
67
+ # callback
68
+ if character_instance.before_index
69
+ instance_exec &character_instance.before_index
70
+ end
71
+
72
+
73
+
74
+ # pagination
75
+ @objects = @objects.page(page).per(per_page)
76
+
77
+
78
+
79
+ # result
80
+ item_objects = @objects.map { |o| build_json_object(o) }
81
+
82
+ render json: item_objects
83
+ end
84
+
85
+
86
+ def show
87
+ @object = model_class.find(params[:id])
88
+ render json: @object
89
+ end
90
+
91
+
92
+ def new
93
+ @object = model_class.new
94
+ @form_action_url = form_action_url(@object)
95
+ render form_template
96
+ end
97
+
98
+
99
+ def edit
100
+ @object = model_class.find(params[:id])
101
+ @form_action_url = form_action_url(@object)
102
+ render form_template
103
+ end
104
+
105
+
106
+ def create
107
+ @object = model_class.new( permit_params(form_attributes_namespace) )
108
+ @form_action_url = form_action_url(@object)
109
+
110
+ if character_instance.before_save
111
+ instance_exec &character_instance.before_save
112
+ end
113
+
114
+ if @object.save
115
+ render json: build_json_object(@object)
116
+ else
117
+ render form_template
118
+ end
119
+ end
120
+
121
+
122
+ # process backbone model patch save
123
+ def patch
124
+ @object = model_class.find(params[:id])
125
+ @object.assign_attributes(permit_params)
126
+
127
+ if character_instance.before_save
128
+ instance_exec &character_instance.before_save
129
+ end
130
+
131
+ if @object.save
132
+ render json: :ok
133
+ else
134
+ render json: @object.errors
135
+ end
136
+ end
137
+
138
+
139
+ # process serialized form, object attributes are in the namespace
140
+ def update
141
+ @object = model_class.find(params[:id])
142
+ @object.assign_attributes( permit_params(form_attributes_namespace) )
143
+ @form_action_url = form_action_url(@object)
144
+
145
+ if character_instance.before_save
146
+ instance_exec &character_instance.before_save
147
+ end
148
+
149
+ if @object.save
150
+ render json: build_json_object(@object)
151
+ else
152
+ render form_template
153
+ end
154
+ end
155
+
156
+
157
+ def destroy
158
+ @object = model_class.find(params[:id])
159
+ @object.destroy
160
+ render json: nil, status: 204
161
+ end
162
+
163
+ private
164
+
165
+ def authenticate_user
166
+ if not auto_login!
167
+ if browserid_authenticated? then login! else render status: :unauthorized, json: { error: "Access denied." } end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,37 @@
1
+ # Author: Alexander Kravets
2
+ # Slate, 2013
3
+
4
+ class Character::ApplicationController < ActionController::Base
5
+ include Character::InstanceConcern
6
+ include Character::AuthConcern
7
+
8
+ before_filter :authenticate_user
9
+
10
+ layout false
11
+
12
+ def index
13
+ render 'character/character'
14
+ end
15
+
16
+ def login
17
+ respond_to_browserid
18
+ end
19
+
20
+ def logout
21
+ logout_browserid
22
+
23
+ if params['redirect']
24
+ redirect_to params['redirect']
25
+ else
26
+ head :ok
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def authenticate_user
33
+ if not auto_login!
34
+ if browserid_authenticated? then login! else register_first_user! end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,72 @@
1
+ # Author: Alexander Kravets
2
+ # Slate, 2013
3
+
4
+ class Character::SettingsController < ActionController::Base
5
+ include Character::InstanceConcern
6
+ include Character::AuthConcern
7
+ before_filter :authenticate_user
8
+ before_filter :set_template_name
9
+
10
+ layout :false
11
+
12
+ def set_template_name
13
+ @action_url = "/#{ character_instance.name }/settings/#{ params[:template_name] }"
14
+
15
+ @instance_template = "/#{ character_instance.name }/settings/#{ params[:template_name] }"
16
+ @character_template = "/character/settings/#{ params[:template_name] }"
17
+ @generic_template = "character/settings/settings_group"
18
+
19
+ if template_exists?(@instance_template)
20
+ @template = @instance_template
21
+ elsif template_exists?(@character_template)
22
+ @template = @character_template
23
+ else
24
+ @settings_group_name = params[:template_name].gsub('_', ' ').titleize
25
+ @template = @generic_template
26
+ end
27
+ end
28
+
29
+ def show
30
+ render @template
31
+ end
32
+
33
+ def update
34
+ @objects = []
35
+ class_name = params[:class_name]
36
+ model_class = class_name.constantize
37
+
38
+ if not params[:objects].nil?
39
+ params[:objects].first.each_pair do |id_or_slug, attributes|
40
+ if attributes.size > 0
41
+ begin
42
+ object = model_class.find id_or_slug
43
+ rescue Mongoid::Errors::DocumentNotFound
44
+ object = model_class.new
45
+ end
46
+
47
+ if attributes[:_destroy] == 'true'
48
+ object.destroy
49
+ else
50
+ object.update_attributes(attributes)
51
+ @objects << object
52
+ end
53
+ end
54
+ end
55
+
56
+ # Hack: this helps to save new objects using unique ids
57
+ @objects.each do |o|
58
+ o.new_record = false if o.new_record
59
+ end
60
+ end
61
+
62
+ render @template
63
+ end
64
+
65
+ private
66
+
67
+ def authenticate_user
68
+ if not auto_login!
69
+ if browserid_authenticated? then login! else render status: :unauthorized, json: { error: "Access denied." } end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,41 @@
1
+ module Character::AuthConcern
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+
6
+ force_ssl if: :ssl_required?
7
+
8
+ def ssl_required?
9
+ if Rails.env.production?
10
+ character_instance.force_ssl
11
+ else
12
+ false
13
+ end
14
+ end
15
+
16
+ def auto_login!
17
+ if ( character_instance.development_auto_login and Rails.env.development? ) or Rails.env.test?
18
+ @browserid_email = 'developer@character.org'
19
+ @current_user = character_instance.user_class.new(email: @browserid_email)
20
+ return true
21
+ else
22
+ return false
23
+ end
24
+ end
25
+
26
+ def login!
27
+ @current_user = browserid_current_user
28
+ end
29
+
30
+ def register_first_user!
31
+ @browserid_email = browserid_email
32
+
33
+ if character_instance.user_class.first
34
+ return false
35
+ else
36
+ @current_user = character_instance.user_class.create(email: @browserid_email) if @browserid_email
37
+ return true
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ module Character::InstanceConcern
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ before_filter :set_instance_name
6
+ end
7
+
8
+ private
9
+
10
+ def set_instance_name
11
+ @character_instance_name ||= (/\/([^\/&]+)/.match request.path)[1]
12
+ end
13
+
14
+ def character_instance
15
+ @character_instance ||= begin
16
+ Character.instances[@character_instance_name]
17
+ end
18
+ end
19
+
20
+ def browserid_config
21
+ @browserid_config ||= begin
22
+ config = Rails.configuration.browserid.clone
23
+ config.user_model = character_instance.user_model
24
+ config.session_variable = "#{ character_instance.name }_browserid_email"
25
+ config.login.text = 'Sign-in with Persona'
26
+ config.login.path = "/#{ character_instance.name }/login"
27
+ config.logout.path = "/#{ character_instance.name }/logout"
28
+ config
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ module Character::JsonObjectConcern
2
+ extend ActiveSupport::Concern
3
+
4
+ def object_fields
5
+ @object_fields ||= begin
6
+ @object_fields = []
7
+
8
+ if params[:f]
9
+ @object_fields = params[:f].split(',')
10
+ else
11
+ fields = model_class.fields.keys - %w( _id _type _position _keywords created_at updated_at deleted_at )
12
+
13
+ @object_fields << fields[0] if fields.size > 0
14
+ @object_fields << fields[1] if fields.size > 1
15
+ @object_fields << :character_thumb_url if model_class.method_defined? :character_thumb_url
16
+ end
17
+
18
+ @object_fields << :_position if model_class.fields.keys.include? '_position'
19
+ @object_fields << :hidden if model_class.fields.keys.include? 'hidden'
20
+ @object_fields << :created_at
21
+ @object_fields << :updated_at
22
+
23
+ @object_fields
24
+ end
25
+ end
26
+
27
+ def build_json_object(o)
28
+ hash = { _id: o.id.to_s }
29
+ object_fields.each { |f| hash[f] = o.try(f) }
30
+ hash
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ module Character::ModelClassConcern
2
+ extend ActiveSupport::Concern
3
+
4
+ def model_slug
5
+ @model_slug ||= begin
6
+ params[:model_slug]
7
+ end
8
+ end
9
+
10
+
11
+ # model class to be used in actions, class name is defined
12
+ # in URL with a slag where - replaced by ::
13
+ # e.g. Character::BlogPost would be /api/Character-BlogPost/new
14
+ def model_class
15
+ @model_class ||= begin
16
+ model_slug.gsub('-', '::').constantize
17
+ end
18
+ end
19
+
20
+
21
+ # form_attributes_namespace is used while form processing
22
+ # in update and create methods
23
+ def form_attributes_namespace
24
+ @form_attributes_namespace ||= begin
25
+ model_class.name.underscore.gsub('/', '_').to_sym
26
+ end
27
+ end
28
+ end