yodel 0.0.1

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 (200) hide show
  1. data/.document +5 -0
  2. data/.gitignore +9 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +63 -0
  5. data/LICENSE +1 -0
  6. data/README.rdoc +20 -0
  7. data/Rakefile +1 -0
  8. data/bin/yodel +4 -0
  9. data/lib/yodel.rb +22 -0
  10. data/lib/yodel/application/application.rb +44 -0
  11. data/lib/yodel/application/extension.rb +59 -0
  12. data/lib/yodel/application/request_handler.rb +48 -0
  13. data/lib/yodel/application/yodel.rb +25 -0
  14. data/lib/yodel/command/command.rb +94 -0
  15. data/lib/yodel/command/deploy.rb +67 -0
  16. data/lib/yodel/command/dns_server.rb +16 -0
  17. data/lib/yodel/command/installer.rb +229 -0
  18. data/lib/yodel/config/config.rb +30 -0
  19. data/lib/yodel/config/environment.rb +16 -0
  20. data/lib/yodel/config/yodel.rb +21 -0
  21. data/lib/yodel/exceptions/destroyed_record.rb +2 -0
  22. data/lib/yodel/exceptions/domain_not_found.rb +16 -0
  23. data/lib/yodel/exceptions/duplicate_layout.rb +2 -0
  24. data/lib/yodel/exceptions/exceptions.rb +3 -0
  25. data/lib/yodel/exceptions/inconsistent_lock_state.rb +2 -0
  26. data/lib/yodel/exceptions/invalid_field.rb +2 -0
  27. data/lib/yodel/exceptions/invalid_index.rb +2 -0
  28. data/lib/yodel/exceptions/invalid_mixin.rb +2 -0
  29. data/lib/yodel/exceptions/invalid_model_field.rb +2 -0
  30. data/lib/yodel/exceptions/layout_not_found.rb +2 -0
  31. data/lib/yodel/exceptions/mass_assignment.rb +2 -0
  32. data/lib/yodel/exceptions/missing_migration.rb +2 -0
  33. data/lib/yodel/exceptions/missing_root_directory.rb +15 -0
  34. data/lib/yodel/exceptions/unable_to_acquire_lock.rb +2 -0
  35. data/lib/yodel/exceptions/unauthorised.rb +2 -0
  36. data/lib/yodel/exceptions/unknown_field.rb +2 -0
  37. data/lib/yodel/middleware/development_server.rb +180 -0
  38. data/lib/yodel/middleware/error_pages.rb +72 -0
  39. data/lib/yodel/middleware/public_assets.rb +78 -0
  40. data/lib/yodel/middleware/request.rb +16 -0
  41. data/lib/yodel/middleware/site_detector.rb +22 -0
  42. data/lib/yodel/mime_types/default_mime_set.rb +28 -0
  43. data/lib/yodel/mime_types/mime_type.rb +68 -0
  44. data/lib/yodel/mime_types/mime_type_set.rb +41 -0
  45. data/lib/yodel/mime_types/mime_types.rb +6 -0
  46. data/lib/yodel/mime_types/yodel.rb +15 -0
  47. data/lib/yodel/models/api/api.rb +1 -0
  48. data/lib/yodel/models/api/api_call.rb +87 -0
  49. data/lib/yodel/models/core/associations/association.rb +37 -0
  50. data/lib/yodel/models/core/associations/associations.rb +22 -0
  51. data/lib/yodel/models/core/associations/counts/many_association.rb +18 -0
  52. data/lib/yodel/models/core/associations/counts/one_association.rb +22 -0
  53. data/lib/yodel/models/core/associations/embedded/embedded_association.rb +47 -0
  54. data/lib/yodel/models/core/associations/embedded/embedded_record_array.rb +12 -0
  55. data/lib/yodel/models/core/associations/embedded/many_embedded_association.rb +62 -0
  56. data/lib/yodel/models/core/associations/embedded/one_embedded_association.rb +49 -0
  57. data/lib/yodel/models/core/associations/query/many_query_association.rb +10 -0
  58. data/lib/yodel/models/core/associations/query/one_query_association.rb +10 -0
  59. data/lib/yodel/models/core/associations/query/query_association.rb +64 -0
  60. data/lib/yodel/models/core/associations/record_association.rb +38 -0
  61. data/lib/yodel/models/core/associations/store/many_store_association.rb +32 -0
  62. data/lib/yodel/models/core/associations/store/one_store_association.rb +14 -0
  63. data/lib/yodel/models/core/associations/store/store_association.rb +51 -0
  64. data/lib/yodel/models/core/attachments/attachment.rb +73 -0
  65. data/lib/yodel/models/core/attachments/image.rb +38 -0
  66. data/lib/yodel/models/core/core.rb +15 -0
  67. data/lib/yodel/models/core/fields/alias_field.rb +32 -0
  68. data/lib/yodel/models/core/fields/array_field.rb +64 -0
  69. data/lib/yodel/models/core/fields/attachment_field.rb +42 -0
  70. data/lib/yodel/models/core/fields/boolean_field.rb +28 -0
  71. data/lib/yodel/models/core/fields/change_sensitive_array.rb +96 -0
  72. data/lib/yodel/models/core/fields/change_sensitive_hash.rb +53 -0
  73. data/lib/yodel/models/core/fields/color_field.rb +4 -0
  74. data/lib/yodel/models/core/fields/date_field.rb +35 -0
  75. data/lib/yodel/models/core/fields/decimal_field.rb +19 -0
  76. data/lib/yodel/models/core/fields/email_field.rb +10 -0
  77. data/lib/yodel/models/core/fields/enum_field.rb +33 -0
  78. data/lib/yodel/models/core/fields/field.rb +154 -0
  79. data/lib/yodel/models/core/fields/fields.rb +29 -0
  80. data/lib/yodel/models/core/fields/fields_field.rb +31 -0
  81. data/lib/yodel/models/core/fields/filter_mixin.rb +9 -0
  82. data/lib/yodel/models/core/fields/filtered_string_field.rb +5 -0
  83. data/lib/yodel/models/core/fields/filtered_text_field.rb +5 -0
  84. data/lib/yodel/models/core/fields/function_field.rb +28 -0
  85. data/lib/yodel/models/core/fields/hash_field.rb +54 -0
  86. data/lib/yodel/models/core/fields/html_field.rb +15 -0
  87. data/lib/yodel/models/core/fields/image_field.rb +11 -0
  88. data/lib/yodel/models/core/fields/integer_field.rb +25 -0
  89. data/lib/yodel/models/core/fields/password_field.rb +21 -0
  90. data/lib/yodel/models/core/fields/self_field.rb +27 -0
  91. data/lib/yodel/models/core/fields/string_field.rb +15 -0
  92. data/lib/yodel/models/core/fields/tags_field.rb +7 -0
  93. data/lib/yodel/models/core/fields/text_field.rb +7 -0
  94. data/lib/yodel/models/core/fields/time_field.rb +36 -0
  95. data/lib/yodel/models/core/functions/function.rb +471 -0
  96. data/lib/yodel/models/core/functions/functions.rb +2 -0
  97. data/lib/yodel/models/core/functions/trigger.rb +14 -0
  98. data/lib/yodel/models/core/log/log.rb +33 -0
  99. data/lib/yodel/models/core/log/log_entry.rb +12 -0
  100. data/lib/yodel/models/core/model/abstract_model.rb +59 -0
  101. data/lib/yodel/models/core/model/model.rb +460 -0
  102. data/lib/yodel/models/core/model/mongo_model.rb +25 -0
  103. data/lib/yodel/models/core/model/site_model.rb +17 -0
  104. data/lib/yodel/models/core/mongo/mongo.rb +3 -0
  105. data/lib/yodel/models/core/mongo/primary_key_factory.rb +12 -0
  106. data/lib/yodel/models/core/mongo/query.rb +68 -0
  107. data/lib/yodel/models/core/mongo/record_index.rb +89 -0
  108. data/lib/yodel/models/core/record/abstract_record.rb +411 -0
  109. data/lib/yodel/models/core/record/embedded_record.rb +47 -0
  110. data/lib/yodel/models/core/record/mongo_record.rb +83 -0
  111. data/lib/yodel/models/core/record/record.rb +386 -0
  112. data/lib/yodel/models/core/record/section.rb +21 -0
  113. data/lib/yodel/models/core/record/site_record.rb +31 -0
  114. data/lib/yodel/models/core/site/migration.rb +52 -0
  115. data/lib/yodel/models/core/site/remote.rb +61 -0
  116. data/lib/yodel/models/core/site/site.rb +202 -0
  117. data/lib/yodel/models/core/validations/email_address_validation.rb +24 -0
  118. data/lib/yodel/models/core/validations/embedded_records_validation.rb +31 -0
  119. data/lib/yodel/models/core/validations/errors.rb +51 -0
  120. data/lib/yodel/models/core/validations/excluded_from_validation.rb +10 -0
  121. data/lib/yodel/models/core/validations/excludes_combinations_validation.rb +18 -0
  122. data/lib/yodel/models/core/validations/format_validation.rb +10 -0
  123. data/lib/yodel/models/core/validations/included_in_validation.rb +10 -0
  124. data/lib/yodel/models/core/validations/includes_combinations_validation.rb +14 -0
  125. data/lib/yodel/models/core/validations/length_validation.rb +28 -0
  126. data/lib/yodel/models/core/validations/password_confirmation_validation.rb +11 -0
  127. data/lib/yodel/models/core/validations/required_validation.rb +9 -0
  128. data/lib/yodel/models/core/validations/unique_validation.rb +9 -0
  129. data/lib/yodel/models/core/validations/validation.rb +39 -0
  130. data/lib/yodel/models/core/validations/validations.rb +15 -0
  131. data/lib/yodel/models/email/email.rb +79 -0
  132. data/lib/yodel/models/migrations/01_record_model.rb +29 -0
  133. data/lib/yodel/models/migrations/02_page_model.rb +45 -0
  134. data/lib/yodel/models/migrations/03_layout_model.rb +38 -0
  135. data/lib/yodel/models/migrations/04_group_model.rb +61 -0
  136. data/lib/yodel/models/migrations/05_user_model.rb +24 -0
  137. data/lib/yodel/models/migrations/06_snippet_model.rb +13 -0
  138. data/lib/yodel/models/migrations/07_search_page_model.rb +32 -0
  139. data/lib/yodel/models/migrations/08_default_site_options.rb +21 -0
  140. data/lib/yodel/models/migrations/09_security_page_models.rb +36 -0
  141. data/lib/yodel/models/migrations/10_record_proxy_page_model.rb +17 -0
  142. data/lib/yodel/models/migrations/11_email_model.rb +28 -0
  143. data/lib/yodel/models/migrations/12_api_call_model.rb +23 -0
  144. data/lib/yodel/models/migrations/13_redirect_page_model.rb +13 -0
  145. data/lib/yodel/models/migrations/14_menu_model.rb +20 -0
  146. data/lib/yodel/models/models.rb +8 -0
  147. data/lib/yodel/models/pages/form_builder.rb +379 -0
  148. data/lib/yodel/models/pages/html_decorator.rb +132 -0
  149. data/lib/yodel/models/pages/layout.rb +120 -0
  150. data/lib/yodel/models/pages/menu.rb +32 -0
  151. data/lib/yodel/models/pages/page.rb +378 -0
  152. data/lib/yodel/models/pages/pages.rb +7 -0
  153. data/lib/yodel/models/pages/record_proxy_page.rb +188 -0
  154. data/lib/yodel/models/pages/redirect_page.rb +11 -0
  155. data/lib/yodel/models/search/search.rb +1 -0
  156. data/lib/yodel/models/search/search_page.rb +58 -0
  157. data/lib/yodel/models/security/facebook_login_page.rb +55 -0
  158. data/lib/yodel/models/security/group.rb +10 -0
  159. data/lib/yodel/models/security/guests_group.rb +5 -0
  160. data/lib/yodel/models/security/login_page.rb +20 -0
  161. data/lib/yodel/models/security/logout_page.rb +13 -0
  162. data/lib/yodel/models/security/noone_group.rb +5 -0
  163. data/lib/yodel/models/security/owner_group.rb +8 -0
  164. data/lib/yodel/models/security/password.rb +5 -0
  165. data/lib/yodel/models/security/password_reset_page.rb +47 -0
  166. data/lib/yodel/models/security/security.rb +10 -0
  167. data/lib/yodel/models/security/user.rb +33 -0
  168. data/lib/yodel/public/core/css/core.css +257 -0
  169. data/lib/yodel/public/core/css/reset.css +48 -0
  170. data/lib/yodel/public/core/images/cross.png +0 -0
  171. data/lib/yodel/public/core/images/spinner.gif +0 -0
  172. data/lib/yodel/public/core/images/tick.png +0 -0
  173. data/lib/yodel/public/core/images/yodel.png +0 -0
  174. data/lib/yodel/public/core/js/jquery.min.js +18 -0
  175. data/lib/yodel/public/core/js/json2.js +480 -0
  176. data/lib/yodel/public/core/js/yodel_jquery.js +238 -0
  177. data/lib/yodel/request/authentication.rb +76 -0
  178. data/lib/yodel/request/flash.rb +28 -0
  179. data/lib/yodel/request/request.rb +4 -0
  180. data/lib/yodel/requires.rb +47 -0
  181. data/lib/yodel/task_queue/queue_daemon.rb +33 -0
  182. data/lib/yodel/task_queue/queue_worker.rb +32 -0
  183. data/lib/yodel/task_queue/stats_thread.rb +27 -0
  184. data/lib/yodel/task_queue/task.rb +62 -0
  185. data/lib/yodel/task_queue/task_queue.rb +40 -0
  186. data/lib/yodel/types/date.rb +5 -0
  187. data/lib/yodel/types/object_id.rb +11 -0
  188. data/lib/yodel/types/time.rb +5 -0
  189. data/lib/yodel/version.rb +3 -0
  190. data/system/Library/LaunchDaemons/com.yodelcms.dns.plist +26 -0
  191. data/system/Library/LaunchDaemons/com.yodelcms.server.plist +26 -0
  192. data/system/etc/resolver/yodel +2 -0
  193. data/system/usr/local/bin/yodel_command_runner +2 -0
  194. data/system/usr/local/etc/yodel/development_settings.rb +28 -0
  195. data/system/usr/local/etc/yodel/production_settings.rb +27 -0
  196. data/system/var/log/yodel.log +0 -0
  197. data/test/helper.rb +18 -0
  198. data/test/test_yodel.rb +4 -0
  199. data/yodel.gemspec +47 -0
  200. metadata +501 -0
@@ -0,0 +1,10 @@
1
+ class ExcludedFromValidation < Validation
2
+ def self.validate(params, field, name, value, record, errors)
3
+ prohibited_values = params['prohibited_values']
4
+ errors[field.name] << new(prohibited_values) if prohibited_values.include?(value)
5
+ end
6
+
7
+ def describe
8
+ "must not be: #{params.join(', ')}"
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ class ExcludesCombinationsValidation < Validation
2
+ def self.validate(params, field, name, value, record, errors)
3
+ combinations = params['combinations']
4
+ combinations.each do |excluded_combination|
5
+ fail = excluded_combination.all? {|prohibited| value.include?(prohibited)}
6
+ (errors[field.name] << new(combinations)) and return if fail
7
+ end
8
+ end
9
+
10
+ def describe
11
+ if params.size > 1
12
+ combinations = params.collect.with_index {|combo, index| "#{index + 1}. #{combo.to_sentence}"}
13
+ else
14
+ combinations = [params.first.to_sentence]
15
+ end
16
+ "may not contain #{combinations.size == 1 ? 'this' : 'these'} combination#{'s' if combinations.size > 1}: #{combinations.to_sentence(two_words_connector: ', and ')}"
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ class FormatValidation < Validation
2
+ def self.validate(params, field, name, value, record, errors)
3
+ format = params['format']
4
+ errors[field.name] << new(format) unless value =~ Regexp.new(format)
5
+ end
6
+
7
+ def describe
8
+ "is not in the required format"
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ class IncludedInValidation < Validation
2
+ def self.validate(params, field, name, value, record, errors)
3
+ valid_values = params['valid_values']
4
+ errors[field.name] << new(valid_values) unless valid_values.include?(value)
5
+ end
6
+
7
+ def describe
8
+ "must be one of: #{params.join(', ')}"
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ class IncludesCombinationsValidation < Validation
2
+ def self.validate(params, field, name, value, record, errors)
3
+ combinations = params['combinations']
4
+ combinations.each do |included_combination|
5
+ return if included_combination.all? {|required| value.include?(required)}
6
+ end
7
+ errors[field.name] << new(combinations)
8
+ end
9
+
10
+ def describe
11
+ combinations = params.collect(&:to_s).join(', ')
12
+ "must contain one of these combinations: #{combinations}"
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ class LengthValidation < Validation
2
+ def self.validate(params, field, name, value, record, errors)
3
+ length = params['length']
4
+ min, max = length
5
+
6
+ if min == 0
7
+ valid = (value.size <= max)
8
+ elsif max == 0
9
+ valid = (value.size >= min)
10
+ else
11
+ valid = (value.size >= min) && (value.size <= max)
12
+ end
13
+
14
+ errors[field.name] << new(length) unless valid
15
+ end
16
+
17
+ def describe
18
+ min, max = params
19
+
20
+ if min == 0
21
+ "is too long (maximum length is #{max})"
22
+ elsif max == 0
23
+ "is too short (minimum length is #{min})"
24
+ else
25
+ "must be between #{min} and #{max}"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ class PasswordConfirmationValidation < Validation
2
+ def self.validate(params, field, name, value, record, errors)
3
+ return if record.field_was(field.name).nil? || record.stash["current_#{name}"].nil? || !record.respond_to?(:passwords_match?)
4
+ match = record.passwords_match?(record.stash["current_#{name}"])
5
+ errors[field.name] << new(name) unless match
6
+ end
7
+
8
+ def describe
9
+ "didn't match your existing #{params}"
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ class RequiredValidation < Validation
2
+ def self.validate(params, field, name, value, record, errors)
3
+ errors[field.name] << new(params) if (value.blank? && !value.is_a?(FalseClass))
4
+ end
5
+
6
+ def describe
7
+ "is required"
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class UniqueValidation < Validation
2
+ def self.validate(params, field, name, value, record, errors)
3
+ errors[field.name] << new(params) if record.model.exists?(field.name => value, :_id.ne => record.id)
4
+ end
5
+
6
+ def describe
7
+ "must be unique"
8
+ end
9
+ end
@@ -0,0 +1,39 @@
1
+ class Validation
2
+ attr_accessor :params, :field
3
+
4
+ def self.validate(type, params, field, name, value, record, errors)
5
+ validation = case type
6
+ when 'excluded_from'
7
+ ExcludedFromValidation
8
+ when 'excludes_combinations'
9
+ ExcludesCombinationsValidation
10
+ when 'format'
11
+ FormatValidation
12
+ when 'included_in'
13
+ IncludedInValidation
14
+ when 'includes_combinations'
15
+ IncludesCombinationsValidation
16
+ when 'length'
17
+ LengthValidation
18
+ when 'required'
19
+ RequiredValidation
20
+ when 'unique'
21
+ UniqueValidation
22
+ when 'password_confirmation'
23
+ PasswordConfirmationValidation
24
+ end
25
+ validation.validate(params, field, name, value, record, errors)
26
+ end
27
+
28
+ def initialize(params)
29
+ @params = params
30
+ end
31
+
32
+ def describe
33
+ "is invalid"
34
+ end
35
+
36
+ def to_json(*a)
37
+ describe.to_json(*a)
38
+ end
39
+ end
@@ -0,0 +1,15 @@
1
+ Dir.chdir(File.dirname(__FILE__)) do
2
+ require './validation'
3
+ require './email_address_validation'
4
+ require './embedded_records_validation'
5
+ require './errors'
6
+ require './excluded_from_validation'
7
+ require './excludes_combinations_validation'
8
+ require './format_validation'
9
+ require './included_in_validation'
10
+ require './includes_combinations_validation'
11
+ require './length_validation'
12
+ require './password_confirmation_validation'
13
+ require './required_validation'
14
+ require './unique_validation'
15
+ end
@@ -0,0 +1,79 @@
1
+ class Email < Record
2
+
3
+ def deliver(options)
4
+ # we don't store complete records in the queue task, only IDs of records
5
+ cleaned = {_id: self.id}
6
+ options.each do |key, value|
7
+ if value.is_a?(Record)
8
+ value = {_id: value.id}
9
+ end
10
+ cleaned[key] = value
11
+ end
12
+
13
+ Task.add_task(:deliver_email, cleaned, site)
14
+ end
15
+
16
+ def perform_delivery(options)
17
+ mail = Mail.new
18
+
19
+ # retrieve records that were replaced with their ID as above
20
+ options.each do |key, value|
21
+ if value.is_a?(Hash) && value.key?('_id')
22
+ options[key] = site.records.find(value['_id'])
23
+ end
24
+ end
25
+
26
+ # email headers
27
+ %w{to from cc bcc subject}.each do |param|
28
+ options[param] ||= options[param.to_sym] || self.send(param)
29
+ mail.send("#{param}=", options[param])
30
+ end
31
+ render_binding = binding
32
+
33
+ # rendered text body
34
+ unless text_body.blank?
35
+ text_body = self.text_body
36
+ text_part = Mail::Part.new do
37
+ body Ember::Template.new(text_body).render(render_binding)
38
+ end
39
+ mail.text_part = text_part
40
+ end
41
+
42
+ # rendered html body
43
+ unless html_body.blank?
44
+ html_body = self.html_body
45
+ # FIXME: reloading should be done elsewhere, not a concern of email
46
+ #Layout.reload_layouts(site) if Yodel.env.development?
47
+ if self.html_layout && layout = site.layouts.where(name: self.html_layout, mime_type: :html).first
48
+ @content = Ember::Template.new(html_body).render(render_binding)
49
+ @binding = render_binding
50
+ html_content = layout.render(self)
51
+ else
52
+ html_content = Ember::Template.new(html_body).render(render_binding)
53
+ end
54
+ html_part = Mail::Part.new do
55
+ content_type 'text/html; charset=UTF-8'
56
+ body html_content
57
+ end
58
+ mail.html_part = html_part
59
+ end
60
+
61
+ mail.deliver!
62
+ end
63
+
64
+ def content
65
+ @content
66
+ end
67
+
68
+ def set_content(content)
69
+ @content = content
70
+ end
71
+
72
+ def get_binding
73
+ @binding
74
+ end
75
+
76
+ def set_binding(binding)
77
+ @binding = binding
78
+ end
79
+ end
@@ -0,0 +1,29 @@
1
+ class RecordModelMigration < Migration
2
+ def self.up(site)
3
+ records = Model.new(site, name: 'Record')
4
+ records.modify do |records|
5
+ # identity, hierarchy and search
6
+ add_many :children, model: :record, foreign_key: 'parent', order: 'index asc', display: false
7
+ add_field :index, :integer, validations: {required: {}}, display: false
8
+ add_one :owner, model: :user, display: false
9
+ add_field :name, :string
10
+ add_field :show_in_search, :boolean, default: true, section: 'Options'
11
+ add_field :search_keywords, :array, of: :string, display: false
12
+ add_field :search_title, :string, display: false
13
+
14
+ # modelling
15
+ add_one :eigenmodel, model: :model, destroy: true, display: false
16
+ add_one :parent, model: :record, index: true, display: false
17
+ add_one :model, index: true, display: false
18
+ records.descendants = [records]
19
+ end
20
+
21
+ site.model_types['records'] = records.id
22
+ site.model_plural_names['Record'] = 'records'
23
+ site.save
24
+ end
25
+
26
+ def self.down(site)
27
+ site.records.destroy
28
+ end
29
+ end
@@ -0,0 +1,45 @@
1
+ class PageModelMigration < Migration
2
+ def self.up(site)
3
+ site.records.create_model :pages do |pages|
4
+ # core page attributes
5
+ add_field :permalink, :string, validations: {required: {}}, index: true, searchable: false, display: false
6
+ add_field :path, :string, validations: {required: {}}, index: true, searchable: false, display: false
7
+ add_field :created_at, :time, display: false
8
+ add_field :title, :string, validations: {required: {}}
9
+ add_field :content, :html
10
+
11
+ # options section
12
+ add_field :show_in_menus, :boolean, default: true, section: 'Options'
13
+ add_field :description, :text, section: 'Options', searchable: false
14
+ add_field :keywords, :text, section: 'Options', searchable: false
15
+ add_field :custom_meta_tags, :text, section: 'Options', searchable: false
16
+ add_one :new_child_page, model: :page, section: 'Options', show_blank: true, blank_text: 'None'
17
+
18
+ # layout
19
+ add_field :page_layout, :string, section: 'Options', default: nil, searchable: false
20
+ add_one :page_layout_record, model: :layout, display: false
21
+ add_field :edit_layout, :string, searchable: false, section: 'Options'
22
+ add_one :edit_layout_record, model: :layout, display: false
23
+
24
+ add_field :name, :alias, of: :title
25
+ pages.default_child_model = pages.id
26
+ pages.allowed_children = [pages]
27
+ pages.allowed_parents = [pages]
28
+ pages.record_class_name = 'Page'
29
+ end
30
+
31
+ # glob pages are normal pages, but match paths with components at the end
32
+ # of the page's path, e.g /git/HEAD would match the glob page /git
33
+ site.pages.create_model :glob_pages do |glob_pages|
34
+ end
35
+
36
+ # default root page
37
+ page = site.pages.new
38
+ page.title = "Home"
39
+ page.save
40
+ end
41
+
42
+ def self.down(site)
43
+ site.pages.destroy
44
+ end
45
+ end
@@ -0,0 +1,38 @@
1
+ class LayoutModelMigration < Migration
2
+ def self.up(site)
3
+ site.records.create_model :layouts do |layouts|
4
+ add_field :name, :string, validations: {required: {}}, index: true
5
+ add_field :mime_type, :string, validations: {required: {}}, index: true
6
+
7
+ layouts.allowed_children = []
8
+ layouts.allowed_parents = []
9
+ layouts.searchable = false
10
+ layouts.record_class_name = 'Layout'
11
+ end
12
+
13
+ site.layouts.create_model :persistent_layouts do |persistent_layouts|
14
+ add_field :markup, :html, validations: {required: {}}
15
+ add_many :pages, foreign_key: 'page_layout_record'
16
+
17
+ persistent_layouts.allowed_children = [persistent_layouts]
18
+ persistent_layouts.allowed_parents = [persistent_layouts]
19
+ persistent_layouts.searchable = false
20
+ persistent_layouts.record_class_name = 'PersistentLayout'
21
+ end
22
+
23
+ site.layouts.create_model :file_layouts do |file_layouts|
24
+ add_field :path, :string, validations: {required: {}}
25
+
26
+ file_layouts.allowed_children = [file_layouts]
27
+ file_layouts.allowed_parents = [file_layouts]
28
+ file_layouts.searchable = false
29
+ file_layouts.record_class_name = 'FileLayout'
30
+ end
31
+ end
32
+
33
+ def self.down(site)
34
+ site.layouts.destroy
35
+ site.persistent_layouts.destroy
36
+ site.file_layouts.destroy
37
+ end
38
+ end
@@ -0,0 +1,61 @@
1
+ class GroupModelMigration < Migration
2
+ def self.up(site)
3
+ site.records.create_model :groups do |groups|
4
+ add_field :name, :string, validations: {required: {}}
5
+ add_many :users, store: false
6
+ groups.icon = '/admin/images/group_icon.png'
7
+ groups.record_class_name = 'Group'
8
+ end
9
+ site.reload
10
+
11
+ # a special singleton group representing an 'owner' of a record
12
+ site.groups.create_model :owner_groups do |group|
13
+ group.record_class_name = 'OwnerGroup'
14
+ end
15
+ site.reload
16
+
17
+ # a special singleton group representing no one
18
+ site.groups.create_model :noone_groups do |group|
19
+ group.record_class_name = 'NooneGroup'
20
+ end
21
+ site.reload
22
+
23
+ # a special singleton group representing 'everyone'
24
+ site.groups.create_model :guest_groups do |group|
25
+ group.record_class_name = 'GuestsGroup'
26
+ end
27
+ site.reload
28
+
29
+
30
+ # permissions are based on a hierarchy of groups. branches are permitted.
31
+ noone = site.noone_groups.new(name: 'No One')
32
+ noone.save
33
+
34
+ devs = site.groups.new(name: 'Developers')
35
+ devs.parent = noone
36
+ devs.save
37
+
38
+ admins = site.groups.new(name: 'Administrators')
39
+ admins.parent = devs
40
+ admins.save
41
+
42
+ owner = site.owner_groups.new(name: 'Owner')
43
+ owner.parent = admins
44
+ owner.save
45
+
46
+ users = site.groups.new(name: 'Users')
47
+ users.parent = owner
48
+ users.save
49
+
50
+ guests = site.guest_groups.new(name: 'Guests')
51
+ guests.parent = users
52
+ guests.save
53
+ end
54
+
55
+ def self.down(site)
56
+ site.groups.destroy
57
+ site.owner_groups.destroy
58
+ site.noone_groups.destroy
59
+ site.guest_groups.destroy
60
+ end
61
+ end