zena 1.0.0.rc2 → 1.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (274) hide show
  1. data/History.txt +23 -0
  2. data/README.rdoc +1 -1
  3. data/app/controllers/columns_controller.rb +3 -31
  4. data/app/controllers/comments_controller.rb +8 -3
  5. data/app/controllers/data_entries_controller.rb +1 -1
  6. data/app/controllers/documents_controller.rb +2 -2
  7. data/app/controllers/nodes_controller.rb +29 -12
  8. data/app/controllers/relations_controller.rb +2 -2
  9. data/app/controllers/sites_controller.rb +1 -1
  10. data/app/controllers/user_sessions_controller.rb +6 -3
  11. data/app/controllers/users_controller.rb +18 -16
  12. data/app/controllers/versions_controller.rb +20 -18
  13. data/app/controllers/virtual_classes_controller.rb +103 -17
  14. data/app/helpers/users_helper.rb +1 -1
  15. data/app/models/column.rb +19 -50
  16. data/app/models/comment.rb +2 -1
  17. data/app/models/node.rb +45 -22
  18. data/app/models/relation.rb +13 -0
  19. data/app/models/relation_proxy.rb +3 -2
  20. data/app/models/role.rb +213 -4
  21. data/app/models/site.rb +18 -11
  22. data/app/models/template.rb +37 -35
  23. data/app/models/version.rb +1 -1
  24. data/app/models/virtual_class.rb +154 -86
  25. data/app/views/columns/_li.html.erb +1 -1
  26. data/app/views/columns/index.html.erb +1 -9
  27. data/app/views/comments/index.rhtml +10 -8
  28. data/app/views/documents/_crop.rhtml +5 -6
  29. data/app/views/documents/crop_form.rjs +3 -2
  30. data/app/views/groups/index.rhtml +1 -1
  31. data/app/views/iformats/index.rhtml +1 -1
  32. data/app/views/nodes/_import_results.rhtml +1 -1
  33. data/app/views/nodes/_parent.rhtml +1 -2
  34. data/app/views/nodes/update.rjs +3 -4
  35. data/app/views/relations/index.erb +1 -1
  36. data/app/views/sites/index.erb +1 -1
  37. data/app/views/templates/drive_tabs/_drive.rhtml +0 -2
  38. data/app/views/templates/edit_tabs/_image.rhtml +1 -1
  39. data/app/views/templates/edit_tabs/_title.rhtml +0 -6
  40. data/app/views/users/index.rhtml +1 -1
  41. data/app/views/users/preferences.html.erb +2 -2
  42. data/app/views/versions/backup.rjs +1 -1
  43. data/app/views/versions/custom_tab.rhtml +9 -4
  44. data/app/views/versions/destroy.rjs +2 -2
  45. data/app/views/versions/update.rjs +2 -9
  46. data/app/views/virtual_classes/_form.erb +3 -2
  47. data/app/views/virtual_classes/import_prepare.html.erb +13 -0
  48. data/app/views/virtual_classes/index.erb +28 -8
  49. data/app/views/zafu/default/Node-+adminLayout.zafu +1 -13
  50. data/app/views/zafu/default/Node-+login.zafu +1 -0
  51. data/app/views/zafu/default/Node-+notFound.zafu +1 -1
  52. data/app/views/zafu/default/Node-+popupLayout.zafu +1 -2
  53. data/app/views/zafu/default/Node-+search.zafu +1 -1
  54. data/app/views/zafu/default/Node-admin.zafu +205 -0
  55. data/app/views/zafu/default/Node.zafu +11 -11
  56. data/bricks/captcha/lib/bricks/captcha.rb +3 -2
  57. data/bricks/mongrel/zena/init.rb +2 -1
  58. data/bricks/pdf/README +5 -5
  59. data/bricks/pdf/lib/bricks/pdf/engine/prince.rb +2 -2
  60. data/bricks/pdf/lib/bricks/pdf/engine/xhtml2pdf.rb +2 -2
  61. data/bricks/pdf/lib/bricks/pdf/install.rb +5 -5
  62. data/bricks/pdf/lib/bricks/pdf.rb +11 -11
  63. data/bricks/pdf/test/engines/test_prince.rb +4 -4
  64. data/bricks/pdf/test/engines/test_xhtml2pdf.rb +4 -4
  65. data/bricks/pdf/test/shoulda_macros/shoulda_pdf.rb +2 -2
  66. data/bricks/pdf/zena/init.rb +2 -2
  67. data/bricks/pdf/zena/tasks.rb +2 -2
  68. data/bricks/sphinx/lib/bricks/sphinx.rb +6 -2
  69. data/bricks/sphinx/zena/{sphinx.yml → sphinx.yml.erb} +2 -2
  70. data/bricks/sphinx/zena/tasks.rb +28 -2
  71. data/bricks/tags/lib/bricks/tags.rb +16 -1
  72. data/bricks/tags/zena/test/unit/tags_test.rb +15 -0
  73. data/bricks/tags/zena/test/zafu/tags.yml +5 -1
  74. data/bricks/worker/lib/bricks/worker.rb +39 -0
  75. data/bricks/worker/zena/deploy.rb +0 -2
  76. data/bricks/worker/zena/init.rb +1 -0
  77. data/bricks/worker/zena/test/sites/zena/delayed_jobs.yml +16 -0
  78. data/bricks/worker/zena/test/zafu/worker.yml +8 -0
  79. data/bricks/zena/zena/migrate/01_base.rb +36 -60
  80. data/bricks/zena/zena/migrate/02_zerox1_schema.rb +388 -0
  81. data/bricks/zena/zena/migrate/03_zerox1_data.rb +380 -0
  82. data/bricks/zena/zena/migrate/20110315161158_add_reverse_scope_to_roles.rb +9 -0
  83. data/config/database_example.yml +1 -1
  84. data/config/environment.rb +1 -1
  85. data/config/gems.yml +17 -14
  86. data/db/init/base/skins/default/Node-+index.zafu +8 -1
  87. data/db/init/base/skins/default/Node-+login.zafu +1 -0
  88. data/db/init/base/skins/default/Node-+popupLayout.zafu +1 -2
  89. data/db/init/base/skins/default/Node-+search.zafu +2 -2
  90. data/db/init/base/skins/default/Node.zafu +9 -9
  91. data/db/init/base/skins/default/{favicon.png → img/favicon.png} +0 -0
  92. data/db/init/base/skins/default/{style.css → img/style.css} +0 -0
  93. data/db/init/base/skins/default/img/translations.yml +11 -0
  94. data/db/init/base/skins/default/notes.zafu +7 -9
  95. data/doc/zafu_changes.yml +12 -0
  96. data/lib/bricks/loader.rb +38 -15
  97. data/lib/tasks/zena.rake +74 -24
  98. data/lib/zena/acts/enrollable.rb +4 -1
  99. data/lib/zena/acts/secure.rb +2 -48
  100. data/lib/zena/acts/serializable.rb +13 -1
  101. data/lib/zena/app.rb +9 -0
  102. data/lib/zena/code_syntax.rb +154 -151
  103. data/lib/zena/console.rb +141 -0
  104. data/lib/zena/controller/test_case.rb +1 -1
  105. data/lib/zena/db_helper/abstract_db.rb +17 -5
  106. data/lib/zena/db_helper/mysql.rb +14 -12
  107. data/lib/zena/db_helper/postgresql.rb +1 -2
  108. data/lib/zena/db_helper/sqlite3.rb +6 -6
  109. data/lib/zena/deploy/awstats.conf.rhtml +1 -1
  110. data/lib/zena/deploy/httpd.rhtml +6 -1
  111. data/lib/zena/deploy/vhost.rhtml +9 -1
  112. data/lib/zena/deploy.rb +12 -7
  113. data/lib/zena/foxy_parser.rb +3 -1
  114. data/lib/zena/info.rb +1 -1
  115. data/lib/zena/parser/zafu_tags.rb +1 -0
  116. data/lib/zena/parser/zazen_rules.rb +1 -1
  117. data/lib/zena/remote/node.rb +15 -3
  118. data/lib/zena/remote/serializable_array.rb +19 -0
  119. data/lib/zena/remote.rb +1 -0
  120. data/lib/zena/routes.rb +7 -2
  121. data/lib/zena/site_worker.rb +11 -1
  122. data/lib/zena/unit/test_case.rb +68 -0
  123. data/lib/zena/use/action.rb +6 -2
  124. data/lib/zena/use/ajax.rb +127 -53
  125. data/lib/zena/use/ancestry.rb +11 -8
  126. data/lib/zena/use/calendar.rb +265 -129
  127. data/lib/zena/use/conditional.rb +1 -1
  128. data/lib/zena/use/context.rb +5 -5
  129. data/lib/zena/use/dates.rb +172 -60
  130. data/lib/zena/use/display.rb +70 -39
  131. data/lib/zena/use/error_rendering.rb +1 -3
  132. data/lib/zena/use/field_index.rb +4 -1
  133. data/lib/zena/use/forms.rb +94 -72
  134. data/lib/zena/use/fulltext.rb +16 -24
  135. data/lib/zena/use/html_tags.rb +20 -12
  136. data/lib/zena/use/i18n.rb +37 -37
  137. data/lib/zena/use/image_builder.rb +8 -1
  138. data/lib/zena/use/ml_index.rb +16 -16
  139. data/lib/zena/use/prop_eval.rb +10 -5
  140. data/lib/zena/use/query_builder.rb +55 -23
  141. data/lib/zena/use/query_node.rb +51 -25
  142. data/lib/zena/use/refactor.rb +2 -28
  143. data/lib/zena/use/relations.rb +1 -1
  144. data/lib/zena/use/rendering.rb +29 -0
  145. data/lib/zena/use/scope_index.rb +75 -14
  146. data/lib/zena/use/search.rb +5 -10
  147. data/lib/zena/use/test_helper.rb +2 -2
  148. data/lib/zena/use/urls.rb +125 -104
  149. data/lib/zena/use/workflow.rb +2 -1
  150. data/lib/zena/use/zafu_attributes.rb +2 -2
  151. data/lib/zena/use/zafu_safe_definitions.rb +20 -0
  152. data/lib/zena/use/zafu_templates.rb +20 -6
  153. data/lib/zena/use/zazen.rb +31 -20
  154. data/lib/zena/view/test_case.rb +5 -0
  155. data/lib/zena/zafu_compiler.rb +24 -2
  156. data/lib/zena.rb +12 -6
  157. data/locale/de/LC_MESSAGES/zena.mo +0 -0
  158. data/locale/de/zena.po +1345 -1164
  159. data/locale/en/LC_MESSAGES/zena.mo +0 -0
  160. data/locale/en/zena.po +1275 -1129
  161. data/locale/fr/LC_MESSAGES/zena.mo +0 -0
  162. data/locale/fr/zena.mo +0 -0
  163. data/locale/fr/zena.po +1617 -1441
  164. data/locale/log.txt +9 -0
  165. data/locale/zena.pot +957 -748
  166. data/public/javascripts/prototype.js +1 -1
  167. data/public/javascripts/zena.js +99 -44
  168. data/public/stylesheets/admin.css +6 -4
  169. data/public/stylesheets/backend.css +71 -0
  170. data/public/stylesheets/calendar.css +24 -25
  171. data/public/stylesheets/code.css +11 -6
  172. data/public/stylesheets/comment.css +2 -1
  173. data/public/stylesheets/popup.css +7 -8
  174. data/test/custom_queries/complex.host.yml +15 -1
  175. data/test/fixtures/files/Node-test.zafu +29 -28
  176. data/test/fixtures/files/translations_de.yml +12 -1
  177. data/test/fixtures/files/translations_fr.yml +12 -1
  178. data/test/functional/comments_controller_test.rb +9 -0
  179. data/test/functional/iformats_controller_test.rb +1 -1
  180. data/test/functional/nodes_controller_test.rb +124 -35
  181. data/test/functional/users_controller_test.rb +132 -3
  182. data/test/functional/virtual_classes_controller_test.rb +75 -4
  183. data/test/integration/navigation_test.rb +51 -9
  184. data/test/integration/query_node/basic.yml +19 -7
  185. data/test/integration/query_node/complex.yml +1 -1
  186. data/test/integration/query_node/dates.yml +27 -1
  187. data/test/integration/query_node/filters.yml +1 -1
  188. data/test/integration/query_node/relations.yml +13 -4
  189. data/test/integration/query_node_test.rb +4 -0
  190. data/test/integration/xml_api_test.rb +6 -1
  191. data/test/integration/zafu_compiler/action.yml +3 -3
  192. data/test/integration/zafu_compiler/ajax.yml +103 -22
  193. data/test/integration/zafu_compiler/basic.yml +0 -52
  194. data/test/integration/zafu_compiler/calendar.yml +44 -20
  195. data/test/integration/zafu_compiler/comments.yml +53 -0
  196. data/test/integration/zafu_compiler/complex.yml +11 -11
  197. data/test/integration/zafu_compiler/complex_ok.yml +16 -3
  198. data/test/integration/zafu_compiler/conditional.yml +15 -5
  199. data/test/integration/zafu_compiler/context.yml +9 -0
  200. data/test/integration/zafu_compiler/dates.yml +43 -15
  201. data/test/integration/zafu_compiler/display.yml +60 -6
  202. data/test/integration/zafu_compiler/errors.yml +6 -2
  203. data/test/integration/zafu_compiler/forms.yml +45 -6
  204. data/test/integration/zafu_compiler/i18n.yml +8 -1
  205. data/test/integration/zafu_compiler/meta.yml +38 -0
  206. data/test/integration/zafu_compiler/query.yml +43 -4
  207. data/test/integration/zafu_compiler/relations.yml +26 -33
  208. data/test/integration/zafu_compiler/rubyless.yml +10 -0
  209. data/test/integration/zafu_compiler/safe_definitions.yml +21 -1
  210. data/test/integration/zafu_compiler/urls.yml +75 -5
  211. data/test/integration/zafu_compiler/version.yml +2 -2
  212. data/test/integration/zafu_compiler/zafu_attributes.yml +5 -1
  213. data/test/integration/zafu_compiler/zazen.yml +14 -6
  214. data/test/integration/zafu_compiler_test.rb +5 -1
  215. data/test/sites/complex/columns.yml +5 -0
  216. data/test/sites/complex/roles.yml +4 -0
  217. data/test/sites/zena/nodes.yml +13 -2
  218. data/test/sites/zena/roles.yml +13 -5
  219. data/test/sites/zena/versions.yml +27 -9
  220. data/test/unit/column_test.rb +51 -5
  221. data/test/unit/iformat_test.rb +2 -2
  222. data/test/unit/node_test.rb +29 -17
  223. data/test/unit/note_test.rb +1 -1
  224. data/test/unit/relation_proxy_test.rb +4 -5
  225. data/test/unit/relation_test.rb +16 -0
  226. data/test/unit/remote_test.rb +2 -2
  227. data/test/unit/role_test.rb +292 -4
  228. data/test/unit/site_test.rb +12 -0
  229. data/test/unit/template_test.rb +1 -1
  230. data/test/unit/text_document_test.rb +1 -1
  231. data/test/unit/virtual_class_test.rb +200 -83
  232. data/test/unit/zena/acts/enrollable_test.rb +26 -31
  233. data/test/unit/zena/use/calendar_test.rb +90 -37
  234. data/test/unit/zena/use/field_index_test.rb +28 -0
  235. data/test/unit/zena/use/html_tags_test.rb +7 -3
  236. data/test/unit/zena/use/ml_index_test.rb +2 -16
  237. data/test/unit/zena/use/nested_attributes_alias_view_test.rb +2 -2
  238. data/test/unit/zena/use/prop_eval_test.rb +50 -8
  239. data/test/unit/zena/use/query_node_test.rb +11 -0
  240. data/test/unit/zena/use/rendering_test.rb +72 -0
  241. data/test/unit/zena/use/scope_index_test.rb +37 -2
  242. data/test/unit/zena/use/urls_test.rb +10 -0
  243. data/test/unit/zena/use/zazen_test.rb +3 -3
  244. data/vendor/plugins/gettext_i18n_rails/Gemfile +11 -0
  245. data/vendor/plugins/gettext_i18n_rails/Gemfile.lock +92 -0
  246. data/vendor/plugins/gettext_i18n_rails/Rakefile +12 -17
  247. data/vendor/plugins/gettext_i18n_rails/Readme.md +215 -0
  248. data/vendor/plugins/gettext_i18n_rails/VERSION +1 -1
  249. data/vendor/plugins/gettext_i18n_rails/gettext_i18n_rails.gemspec +38 -34
  250. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/active_record.rb +1 -1
  251. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/backend.rb +30 -14
  252. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/haml_parser.rb +1 -1
  253. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/html_safe_translations.rb +29 -0
  254. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/i18n_hacks.rb +29 -1
  255. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/model_attributes_finder.rb +7 -1
  256. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/railtie.rb +10 -0
  257. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/ruby_gettext_extractor.rb +6 -2
  258. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/string_interpolate_fix.rb +20 -0
  259. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails/tasks.rb +120 -0
  260. data/vendor/plugins/gettext_i18n_rails/lib/gettext_i18n_rails.rb +10 -3
  261. data/vendor/plugins/gettext_i18n_rails/lib/tasks/gettext_rails_i18n.rake +1 -74
  262. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails/active_record_spec.rb +51 -20
  263. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails/backend_spec.rb +12 -7
  264. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails/string_interpolate_fix_spec.rb +32 -0
  265. data/vendor/plugins/gettext_i18n_rails/spec/gettext_i18n_rails_spec.rb +38 -1
  266. data/vendor/plugins/gettext_i18n_rails/spec/rails2/Gemfile +11 -0
  267. data/vendor/plugins/gettext_i18n_rails/spec/spec_helper.rb +1 -8
  268. data/zena.gemspec +2241 -2217
  269. metadata +123 -83
  270. data/.gitignore +0 -36
  271. data/app/views/nodes/_dates.rhtml +0 -13
  272. data/db/init/base/skins/default/Node-+adminLayout.zafu +0 -46
  273. data/db/init/base/skins/default/Node-tree.zafu +0 -19
  274. data/vendor/plugins/gettext_i18n_rails/README.markdown +0 -143
@@ -5,7 +5,7 @@ module UsersHelper
5
5
  begin
6
6
  @node = @user.node || visitor.prototype
7
7
  begin
8
- res =render :file => template_url(:mode => '+user', :format => 'html')
8
+ res = render :file => template_url(:mode => '+user', :format => 'html')
9
9
  rescue ActiveRecord::RecordNotFound
10
10
  res = render :file => 'versions/custom_tab'
11
11
  end
data/app/models/column.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  class Column < ActiveRecord::Base
2
- attr_accessor :import_result
3
2
  include RubyLess
4
3
  include Property::StoredColumn
5
4
  TYPES_FOR_FORM = %w{string datetime integer float}
@@ -14,6 +13,7 @@ class Column < ActiveRecord::Base
14
13
  validates_presence_of :role
15
14
  validates_uniqueness_of :name, :scope => :site_id
16
15
  validate :name_not_in_models
16
+ validate :valid_ptype_and_index
17
17
 
18
18
  after_save :expire_vclass_cache
19
19
  after_destroy :expire_vclass_cache
@@ -48,55 +48,6 @@ class Column < ActiveRecord::Base
48
48
  FIELD_INDICES << idx
49
49
  end
50
50
  end
51
-
52
- # Import a hash of virtual class definitions and try to build the virtual classes.
53
- def import(data)
54
- data.keys.map do |klass|
55
- build_column(klass, data)
56
- end
57
- end
58
-
59
- # Build a virtual class from a name and a hash of virtual class definitions. If
60
- # the superclass is in the data hash, it is built first.
61
- def build_column(klass, data)
62
- # TODO
63
- # return data[klass]['result'] if data[klass].has_key?('result')
64
- # if virtual_class = Node.get_class(klass)
65
- # if virtual_class.superclass.to_s == data[klass]['superclass']
66
- # virtual_class.import_result = 'same'
67
- # return data[klass]['result'] = virtual_class
68
- # else
69
- # virtual_class.errors.add(:base, 'conflict')
70
- # return data[klass]['result'] = virtual_class
71
- # end
72
- # else
73
- # superclass_name = data[klass]['superclass']
74
- # if data[superclass_name]
75
- # superclass = build_virtual_class(superclass_name, data)
76
- # unless superclass.errors.empty?
77
- # virtual_class = VirtualClass.new(:name => klass, :superclass => superclass_name, :create_group_id => current_site.public_group_id)
78
- # virtual_class.errors.add(:base, 'conflict in superclass')
79
- # return data[klass]['result'] = virtual_class
80
- # end
81
- # elsif superclass = Node.get_class(superclass_name)
82
- # # ok
83
- # else
84
- # virtual_class = VirtualClass.new(:name => klass, :superclass => superclass_name, :create_group_id => current_site.public_group_id)
85
- # virtual_class.errors.add(:base, 'missing superclass')
86
- # return data[klass]['result'] = virtual_class
87
- # end
88
- #
89
- # # build
90
- # create_group_id = superclass.kind_of?(VirtualClass) ? superclass.create_group_id : current_site.public_group_id
91
- # virtual_class = create(data[klass].merge(:name => klass, :create_group_id => create_group_id))
92
- # virtual_class.import_result = 'new'
93
- # return data[klass]['result'] = virtual_class
94
- # end
95
- end
96
-
97
- def export
98
- # TODO
99
- end
100
51
  end
101
52
 
102
53
  def kpath
@@ -108,8 +59,16 @@ class Column < ActiveRecord::Base
108
59
  self.index.to_s.gsub(/\A\./,'')
109
60
  end
110
61
 
62
+ def export
63
+ {
64
+ 'ptype' => ptype,
65
+ 'index' => index,
66
+ }
67
+ end
68
+
111
69
  protected
112
70
  def set_defaults
71
+ self.index = nil if index.blank?
113
72
  self[:site_id] = current_site.id
114
73
  end
115
74
 
@@ -134,4 +93,14 @@ class Column < ActiveRecord::Base
134
93
  def expire_vclass_cache
135
94
  VirtualClass.expire_cache!
136
95
  end
96
+
97
+ def valid_ptype_and_index
98
+ if !TYPES_FOR_FORM.include?(self.ptype)
99
+ errors.add(:ptype, 'invalid')
100
+ end
101
+
102
+ if !index.blank? && !(INDICES_FOR_FORM + FIELD_INDICES.map {|i| ".#{i}"}).include?(index)
103
+ errors.add(:index, 'invalid')
104
+ end
105
+ end
137
106
  end
@@ -13,7 +13,8 @@ class Comment < ActiveRecord::Base
13
13
  :discussion_id => Number,
14
14
  :user_name => {:class => String, :nil => true},
15
15
  :user => {:class => 'User', :nil => true},
16
- :author => Node.author_proc
16
+ :author => Node.author_proc,
17
+ :author_name => String
17
18
 
18
19
  safe_context :replies => ['Comment'], :node => 'Node'
19
20
 
data/app/models/node.rb CHANGED
@@ -113,6 +113,10 @@ class Node < ActiveRecord::Base
113
113
  end
114
114
  end
115
115
 
116
+ def klass_changed?
117
+ kpath_changed?
118
+ end
119
+
116
120
  def virtual_class=(vclass)
117
121
  @virtual_class = vclass
118
122
  self[:vclass_id] = vclass.id
@@ -174,9 +178,11 @@ class Node < ActiveRecord::Base
174
178
  belongs_to :skin
175
179
  before_validation :set_defaults
176
180
  before_validation :node_before_validation
181
+ # We need to change class before validation so that prop_eval is triggered with
182
+ # the new class.
183
+ before_validation :change_klass
177
184
  validate :validate_node
178
185
  before_create :node_before_create
179
- before_save :change_klass
180
186
  after_save :spread_project_and_section
181
187
  after_create :node_after_create
182
188
  attr_protected :zip, :id, :section_id, :project_id, :publish_from, :created_at, :updated_at
@@ -220,7 +226,7 @@ class Node < ActiveRecord::Base
220
226
  :traductions => ['Version'], :discussion => 'Discussion'
221
227
 
222
228
  # we use safe_method because the columns can be null, but the values are never null
223
- safe_method :kpath => String, :user_zip => Number,
229
+ safe_method :kpath => String, :user_zip => Number, :user_id => Number,
224
230
  :parent_zip => Number, :project_zip => Number, :section_zip => Number,
225
231
  :ref_lang => String,
226
232
  :position => Number, :rgroup_id => Number,
@@ -229,7 +235,7 @@ class Node < ActiveRecord::Base
229
235
  :m_text => String, :m_title => String, :m_author => String,
230
236
  :id => {:class => Number, :method => 'zip'},
231
237
  :skin => 'Skin', :ref_lang => String,
232
- :visitor => 'User', [:ancestor?, Node] => Boolean,
238
+ :visitor => 'User', [:is_ancestor?, Node] => Boolean,
233
239
  :comments_count => Number,
234
240
  :v => {:class => 'Version', :method => 'version'},
235
241
  :version => 'Version', :v_status => Number, :v_lang => String,
@@ -257,7 +263,7 @@ class Node < ActiveRecord::Base
257
263
  # compute vhash (must come before Fulltext)
258
264
  include Zena::Use::VersionHash::ModelMethods
259
265
 
260
- # computed properties (vclass prop_eval)
266
+ # computed properties (vclass prop_eval, must come after MLIndex)
261
267
  include Zena::Use::PropEval::ModelMethods
262
268
 
263
269
  # fulltext indices (must come after PropEval)
@@ -424,8 +430,8 @@ class Node < ActiveRecord::Base
424
430
  def get_class(rel, opts={})
425
431
  # mushroom_types ==> MushroomType
426
432
  class_name = rel =~ /\A[a-z]/ ? rel.singularize.camelize : rel
427
- vclass = VirtualClass.find_by_name(class_name)
428
- if opts[:create] && vclass.id
433
+ vclass = VirtualClass[class_name]
434
+ if vclass && opts[:create] && vclass.id
429
435
  # TODO: how do we deal with real class ? (Currently = pass).
430
436
  visitor.group_ids.include?(vclass.create_group_id) ? vclass : nil
431
437
  else
@@ -461,9 +467,6 @@ class Node < ActiveRecord::Base
461
467
  if str =~ /\A\d+\Z/
462
468
  # zip
463
469
  find_by_zip(str)
464
- elsif str =~ /\A:?([0-9a-zA-Z ]+)(\+*)\Z/
465
- offset = $2.to_s.size
466
- Node.search_records($1.gsub('-',' '), :offset => offset, :limit => 1).first
467
470
  elsif path = str[/\A\(([^\)]+)\)\Z/,1]
468
471
  if path[0..0] == '/'
469
472
  path = path[1..-1].split('/').map {|p| String.from_filename(p) }
@@ -495,6 +498,9 @@ class Node < ActiveRecord::Base
495
498
  # FIXME: path pseudo is needed for links... and it should be done here (egg and hen problem)
496
499
  nil
497
500
  end
501
+ elsif str =~ /\A:?([^\+]+)(\+*)\Z/
502
+ offset = $2.to_s.size
503
+ Node.search_records($1.gsub('-',' '), :offset => offset, :limit => 1).first
498
504
  end
499
505
  end
500
506
 
@@ -601,6 +607,7 @@ class Node < ActiveRecord::Base
601
607
  parent_id = opts[:parent_id] || opts[:parent][:id]
602
608
  folder = opts[:folder]
603
609
  defaults = (opts[:defaults] || {}).stringify_keys
610
+ # Initial object class
604
611
  klass = opts[:class] || opts[:klass] || "Page"
605
612
  res = {}
606
613
 
@@ -652,6 +659,15 @@ class Node < ActiveRecord::Base
652
659
  attrs['v_lang'] = lang || attrs['v_lang'] || visitor.lang
653
660
  attrs['ext'] = $3
654
661
  document_path = path
662
+ else
663
+ # Document without extension
664
+ type = :document
665
+ title = filename
666
+ attrs = defaults.dup
667
+ lang = nil
668
+ attrs['v_lang'] = lang || attrs['v_lang'] || visitor.lang
669
+ attrs['ext'] = 'bin'
670
+ document_path = path
655
671
  end
656
672
 
657
673
  index += 1
@@ -839,6 +855,9 @@ class Node < ActiveRecord::Base
839
855
  res["#{key}_id"] = Group.translate_pseudo_id(value, :id) || value
840
856
  elsif %w{user_id}.include?(key)
841
857
  res[key] = User.translate_pseudo_id(value, :id) || value
858
+ elsif %w{link_id}.include?(key)
859
+ # Link id, not translated
860
+ res[key] = value
842
861
  elsif %w{id create_at updated_at}.include?(key)
843
862
  # ignore (can be present in xml)
844
863
  elsif %w{log_at event_at v_publish_from}.include?(key) || (is_link && %w{date}.include?(key))
@@ -890,6 +909,7 @@ class Node < ActiveRecord::Base
890
909
  RubyLess::SafeClass.safe_method_type_for(self, signature)
891
910
  else
892
911
  method = signature.first
912
+
893
913
  if type = super
894
914
  type
895
915
  elsif method == 'cached_role_ids'
@@ -898,13 +918,6 @@ class Node < ActiveRecord::Base
898
918
  elsif method =~ /^(.+)_((id|zip|status|comment)(s?))\Z/ && !instance_methods.include?(method)
899
919
  key = $3 == 'id' ? "zip#{$4}" : $2
900
920
  {:method => "rel[#{$1.inspect}].try(:other_#{key})", :nil => true, :class => ($4.blank? ? Number : [Number])}
901
- elsif receiver && query = receiver.opts[:query]
902
- # Resolve by using information in the SELECT part of the query that found this node
903
- if query.select_keys.include?(method)
904
- {:class => String, :method => "attributes[#{method.inspect}]", :nil => true}
905
- else
906
- nil
907
- end
908
921
  else
909
922
  nil
910
923
  end
@@ -978,9 +991,7 @@ class Node < ActiveRecord::Base
978
991
 
979
992
  # include virtual classes to check inheritance chain
980
993
  def vkind_of?(klass)
981
- if self.class.ancestors.map{|k| k.to_s}.include?(klass)
982
- true
983
- elsif virt = VirtualClass.find(:first, :conditions=>["site_id = ? AND name = ?",current_site[:id], klass])
994
+ if virt = VirtualClass[klass.to_s]
984
995
  kpath_match?(virt.kpath)
985
996
  end
986
997
  end
@@ -1606,6 +1617,7 @@ class Node < ActiveRecord::Base
1606
1617
  errors.add(:base, 'You do not have the rights to post comments.') if @add_comment && !can_comment?
1607
1618
 
1608
1619
  if @new_klass
1620
+ # If you change this, update 'change_klass' (triggered before_validation)
1609
1621
  if !can_drive? || !self[:parent_id]
1610
1622
  errors.add('klass', 'You do not have the rights to change class.')
1611
1623
  else
@@ -1726,17 +1738,28 @@ class Node < ActiveRecord::Base
1726
1738
  end
1727
1739
 
1728
1740
  def change_klass
1741
+ if @new_klass
1742
+ if !can_drive? || !self[:parent_id]
1743
+ return
1744
+ elsif !self.class.allowed_change_to_classes.include?(@new_klass)
1745
+ return
1746
+ end
1747
+ end
1748
+
1729
1749
  if @new_klass && !new_record?
1730
1750
  old_kpath = self.kpath
1731
- # FIXME ! (new virtual_class as schema...)
1751
+ # Reset 'schema' and 'virtual_class'
1752
+ @virtual_class = nil
1753
+
1732
1754
  klass = Node.get_class(@new_klass)
1733
1755
  if klass.kind_of?(VirtualClass)
1734
- self[:vclass_id] = klass.kind_of?(VirtualClass) ? klass[:id] : nil
1756
+ self[:vclass_id] = klass[:id]
1735
1757
  self[:type] = klass.real_class.to_s
1736
1758
  else
1737
- self[:vclass_id] = klass.kind_of?(VirtualClass) ? klass[:id] : nil
1759
+ self[:vclass_id] = nil
1738
1760
  self[:type] = klass.to_s
1739
1761
  end
1762
+
1740
1763
  self[:kpath] = klass.kpath
1741
1764
 
1742
1765
  if old_kpath[/^NPS/] && !self[:kpath][/^NPS/]
@@ -1,4 +1,6 @@
1
1
  class Relation < ActiveRecord::Base
2
+ EXPORT_FIELDS = %w{target_kpath target_icon target_unique source_role source_icon source_unique rel_group}
3
+
2
4
  before_validation :singularize_roles
3
5
  validate :valid_relation
4
6
  attr_accessor :side, :link_errors, :start, :link
@@ -16,6 +18,17 @@ class Relation < ActiveRecord::Base
16
18
  target_unique ? self[:target_role] : self[:target_role].pluralize
17
19
  end
18
20
 
21
+ def export
22
+ res = Zafu::OrderedHash.new
23
+ EXPORT_FIELDS.each do |key|
24
+ value = self[key]
25
+ if !value.blank?
26
+ res[key] = value
27
+ end
28
+ end
29
+ res
30
+ end
31
+
19
32
  private
20
33
  def singularize_roles
21
34
  self.source_role = self[:source_role].singularize unless self[:source_role].blank?
@@ -268,6 +268,7 @@ class RelationProxy < Relation
268
268
  elsif link_id = @start.link_id
269
269
  @other_link = Link.find(link_id)
270
270
  @attributes_to_update[:id] = @other_link[other_side]
271
+ @attributes_to_update[:link_id] = link_id
271
272
  elsif unique?
272
273
  if other_id
273
274
  @attributes_to_update[:id] = other_id
@@ -309,7 +310,7 @@ class RelationProxy < Relation
309
310
  # TODO: this could be optimzed (avoid loading all links...)
310
311
  other_links.each do |link|
311
312
  obj_id = link[other_side]
312
- if add_link_ids.include?(obj_id) && (@attributes_to_update[:date].nil? || @attributes_to_update[:date] == link[:date])
313
+ if add_link_ids.include?(obj_id) && (@attributes_to_update[:date].nil? || @attributes_to_update[:link_id] || @attributes_to_update[:date] == link[:date])
313
314
  # ignore existing link
314
315
  add_link_ids.delete(obj_id)
315
316
  else
@@ -337,7 +338,7 @@ class RelationProxy < Relation
337
338
  # delete
338
339
  @del_links = other_links.select {|l| @attributes_to_update[:date] == l[:date]}
339
340
  else
340
- links = other_links.select {|l| l[other_side] == @attributes_to_update[:id] && (@attributes_to_update[:date].nil? || @attributes_to_update[:date] == l[:date])}
341
+ links = other_links.select {|l| l[other_side] == @attributes_to_update[:id] && (@attributes_to_update[:date].nil? || @attributes_to_update[:link_id] || @attributes_to_update[:date] == l[:date])}
341
342
  if links != []
342
343
  # update
343
344
  if (@attributes_to_update.keys & LINK_ATTRIBUTES) != []
data/app/models/role.rb CHANGED
@@ -13,7 +13,7 @@ class Role < ActiveRecord::Base
13
13
  has_and_belongs_to_many :nodes
14
14
 
15
15
  before_validation :set_defaults
16
- validate :check_can_save
16
+ validate :validate_role
17
17
  attr_accessible :name, :superclass, :icon
18
18
 
19
19
  after_save :expire_vclass_cache
@@ -28,6 +28,144 @@ class Role < ActiveRecord::Base
28
28
  # We use property to store index information, default values and such
29
29
  include Property
30
30
 
31
+ property do |p|
32
+ p.string 'icon'
33
+ end
34
+ safe_property :icon
35
+
36
+ class << self
37
+ def export
38
+ {'Node' => VirtualClass['Node'].export}
39
+ end
40
+
41
+ def import(definitions, delete = false)
42
+ res = []
43
+ post_import = []
44
+ # Create everything in a transaction
45
+ transaction do
46
+ definitions.each do |name, definition|
47
+ klass = VirtualClass[name]
48
+ if !klass || !klass.real_class?
49
+ # Error, missing superclass
50
+ raise Exception.new("Importation needs to start with a real class: '#{name}' is not a real class.")
51
+ else
52
+ # start importing
53
+ res += import_all(klass, definition, post_import)
54
+ end
55
+ end
56
+
57
+ post_import.each do |l|
58
+ l.call
59
+ end
60
+ end
61
+ res
62
+ end
63
+
64
+ private
65
+ def import_all(superclass, definitions, post_import)
66
+ res = []
67
+ definitions.each do |name, sub|
68
+ next unless name =~ /\A[A-Z]/
69
+
70
+ case sub['type']
71
+ when 'Class'
72
+ klass = VirtualClass[name]
73
+ if klass && klass.real_class?
74
+ res += import_all(klass, sub, post_import)
75
+ else
76
+ raise Exception.new("Unknown real class '#{name}'.")
77
+ end
78
+ when 'Role'
79
+ res << import_role(superclass, name, sub)
80
+ when 'VirtualClass'
81
+ # VirtualClass
82
+ res += import_vclass(superclass, name, sub, post_import)
83
+ when nil
84
+ klass = VirtualClass[name]
85
+ if klass && klass.real_class?
86
+ res += import_all(klass, sub, post_import)
87
+ else
88
+ res += import_vclass(superclass, name, sub, post_import)
89
+ end
90
+ else
91
+ # Invalid type
92
+ raise Exception.new("Cannot create '#{name}': invalid type '#{sub['type']}'.")
93
+ end
94
+ end
95
+ res
96
+ end
97
+
98
+ def import_role(superclass, name, definition)
99
+ role = ::Role.find_by_name_and_site_id(name, current_site.id)
100
+ if role && role.class != ::Role
101
+ # Change from vclass to role ?
102
+ # Reject
103
+ raise Exception.new("Cannot convert VirtualClass '#{name}' to Role.")
104
+ elsif !role
105
+ role = ::Role.new(:name => name, :superclass => superclass)
106
+ role.save!
107
+ end
108
+
109
+ # 1. create or update attributes
110
+ # noop
111
+
112
+ # 2. create or update columns (never delete)
113
+ if !role.new_record? && columns = definition['columns']
114
+ role.import_columns(columns)
115
+ end
116
+ role
117
+ end
118
+
119
+ def import_vclass(superclass, name, definition, post_import)
120
+ res = []
121
+ vclass = ::Role.find_by_name_and_site_id(name, current_site.id)
122
+ if vclass && vclass.class != VirtualClass
123
+ # Change from role to vclass ?
124
+ # Reject
125
+ raise Exception.new("Cannot convert Role '#{name}' to VirtualClass.")
126
+ elsif !vclass
127
+ vclass = VirtualClass.new(:name => name, :superclass => superclass)
128
+ end
129
+
130
+ # 1. create or update attributes
131
+ VirtualClass.export_attributes.each do |key|
132
+ if value = definition[key]
133
+ vclass.send(:"#{key}=", value)
134
+ else
135
+ # We do not clear attributes (import is ADD/UPDATE only).
136
+ end
137
+ end
138
+ vclass.save!
139
+ res << vclass
140
+
141
+ # 2. create or update columns (never delete)
142
+ if columns = definition['columns']
143
+ vclass.import_columns(columns)
144
+ end
145
+
146
+ # 3. create relations when all is done
147
+ if relations = definition['relations']
148
+ post_import << lambda do
149
+ vclass.import_relations(relations)
150
+ end
151
+ end
152
+
153
+ # 4. create or update sub-classes
154
+ res += import_all(vclass, definition, post_import)
155
+ res
156
+ end
157
+ end # class << self
158
+
159
+ def real_class?
160
+ false
161
+ end
162
+
163
+ def icon=(txt)
164
+ # FIXME: remove gsub when we stop using ImageBuilder on
165
+ # icon images. SECURITY
166
+ super(txt.gsub('..', '.'))
167
+ end
168
+
31
169
  def superclass
32
170
  if new_record?
33
171
  Node
@@ -37,8 +175,8 @@ class Role < ActiveRecord::Base
37
175
  end
38
176
 
39
177
  def superclass=(klass)
40
- if k = Node.get_class(klass)
41
- self.kpath = k.kpath
178
+ if klass.kind_of?(VirtualClass) || klass = VirtualClass[klass]
179
+ @superclass = klass
42
180
  else
43
181
  errors.add('superclass', 'invalid')
44
182
  end
@@ -49,17 +187,88 @@ class Role < ActiveRecord::Base
49
187
  @safe_column ||= defined_columns.values.sort {|a,b| a.name <=> b.name}
50
188
  end
51
189
 
190
+ def export
191
+ res = Zafu::OrderedHash.new
192
+ res['type'] = real_class? ? 'Class' : type
193
+ if !defined_columns.empty?
194
+ res['columns'] = export_columns
195
+ end
196
+ res
197
+ end
198
+
199
+ def import_columns(columns)
200
+ transaction do
201
+ columns.each do |name, definition|
202
+ column = secure(::Column) { ::Column.find_by_name(name) }
203
+ if !column
204
+ # create
205
+ column = ::Column.new(:name => name)
206
+ elsif column.role_id != self.id
207
+ # error (do not move a column)
208
+ raise Exception.new("Cannot set property '#{name}' in '#{self.name}': already defined in '#{column.role.name}'.")
209
+ end
210
+ column.role_id = self.id
211
+ column.ptype = definition['ptype']
212
+ column.index = definition['index']
213
+ column.save!
214
+ end
215
+ end
216
+ end
217
+
218
+ def import_relations(relations)
219
+ relations.each do |name, definition|
220
+ relation = secure(::Relation) { ::Relation.first(
221
+ :conditions => ['target_role = ? AND source_kpath = ? AND site_id = ?',
222
+ name, self.kpath, self.site_id
223
+ ]
224
+ )}
225
+ if !relation
226
+ # create
227
+ relation = ::Relation.new(:target_role => name, :source_kpath => self.kpath)
228
+ end
229
+ Relation::EXPORT_FIELDS.each do |key|
230
+ value = definition[key]
231
+ if !value.blank?
232
+ relation[key] = value
233
+ end
234
+ end
235
+ relation.save!
236
+ end
237
+ end
238
+
52
239
  private
53
240
  def set_defaults
54
241
  self[:type] = self.class.to_s
55
242
  self.site_id = visitor.site.id
56
243
  end
57
244
 
58
- def check_can_save
245
+ def validate_role
59
246
  errors.add('base', 'You do not have the rights to change roles.') unless visitor.is_admin?
247
+ if new_record?
248
+ errors.add('superclass', 'invalid') unless @superclass.kind_of?(VirtualClass) && @superclass.kpath
249
+ end
250
+
251
+ if @superclass && self.class == ::Role
252
+ self.kpath = @superclass.kpath
253
+ end
60
254
  end
61
255
 
62
256
  def expire_vclass_cache
63
257
  VirtualClass.expire_cache!
64
258
  end
259
+
260
+ def export_columns
261
+ res = Zafu::OrderedHash.new
262
+
263
+ defined_columns.keys.sort.each do |name|
264
+ column = defined_columns[name]
265
+ col = Zafu::OrderedHash.new
266
+ col['ptype'] = column.ptype.to_s
267
+ if column.index then
268
+ col['index'] = column.index
269
+ end
270
+ res[name] = col
271
+ end
272
+ res
273
+ end
65
274
  end