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
data/app/models/site.rb CHANGED
@@ -50,7 +50,7 @@ class Site < ActiveRecord::Base
50
50
  :languages => '',
51
51
  :default_lang => "en",
52
52
  }.merge(opts)
53
- langs = params[:languages].split('.')
53
+ langs = params[:languages].split(',').map(&:strip)
54
54
  langs += [params[:default_lang]] unless langs.include?(params[:default_lang])
55
55
  params[:languages] = langs.map{|l| l[0..1]}.join(',')
56
56
  params[:default_lang] = params[:default_lang][0..1]
@@ -90,11 +90,11 @@ class Site < ActiveRecord::Base
90
90
  raise Exception.new("Could not create public group for site [#{host}] (site#{site[:id]})\n#{pub.errors.map{|k,v| "[#{k}] #{v}"}.join("\n")}") if pub.new_record?
91
91
 
92
92
  # create admin group
93
- admin = site.send(:secure,Group) { Group.create( :name => 'admin') }
94
- raise Exception.new("Could not create admin group for site [#{host}] (site#{site[:id]})\n#{admin.errors.map{|k,v| "[#{k}] #{v}"}.join("\n")}") if admin.new_record?
93
+ editors = site.send(:secure,Group) { Group.create( :name => 'editors') }
94
+ raise Exception.new("Could not create editors group for site [#{host}] (site#{site[:id]})\n#{editors.errors.map{|k,v| "[#{k}] #{v}"}.join("\n")}") if editors.new_record?
95
95
 
96
- # add admin to the 'admin group'
97
- admin.users << admin_user
96
+ # add admin to the 'editors group'
97
+ editors.users << admin_user
98
98
 
99
99
  # create site group
100
100
  sgroup = site.send(:secure,Group) { Group.create( :name => 'site') }
@@ -102,7 +102,7 @@ class Site < ActiveRecord::Base
102
102
 
103
103
  site.public_group_id = pub[:id]
104
104
  site.site_group_id = sgroup[:id]
105
- site.groups << pub << sgroup << admin
105
+ site.groups << pub << sgroup << editors
106
106
 
107
107
  # Reload group_ids in admin
108
108
  admin_user.instance_variable_set(:@group_ids, nil)
@@ -122,7 +122,7 @@ class Site < ActiveRecord::Base
122
122
  # =========== CREATE ROOT NODE ============================
123
123
 
124
124
  root = site.send(:secure,Project) do
125
- Project.create( :title => site.name, :rgroup_id => pub[:id], :wgroup_id => sgroup[:id], :dgroup_id => admin[:id], :title => site.name, :v_status => Zena::Status[:pub])
125
+ Project.create( :title => site.name, :rgroup_id => pub[:id], :wgroup_id => sgroup[:id], :dgroup_id => editors[:id], :title => site.name, :v_status => Zena::Status[:pub])
126
126
  end
127
127
 
128
128
  raise Exception.new("Could not create root node for site [#{host}] (site#{site[:id]})\n#{root.errors.map{|k,v| "[#{k}] #{v}"}.join("\n")}") if root.new_record?
@@ -147,7 +147,7 @@ class Site < ActiveRecord::Base
147
147
 
148
148
  # =========== LOAD INITIAL DATA (default skin) =============
149
149
 
150
- nodes = site.send(:secure, Node) { Node.create_nodes_from_folder(:folder => File.join(Zena::ROOT, 'db', 'init', 'base'), :parent_id => root[:id], :defaults => { :v_status => Zena::Status[:pub], :rgroup_id => pub[:id], :wgroup_id => sgroup[:id], :dgroup_id => admin[:id] } ) }.values
150
+ nodes = site.send(:secure, Node) { Node.create_nodes_from_folder(:folder => File.join(Zena::ROOT, 'db', 'init', 'base'), :parent_id => root[:id], :defaults => { :v_status => Zena::Status[:pub], :rgroup_id => pub[:id], :wgroup_id => sgroup[:id], :dgroup_id => editors[:id] } ) }.values
151
151
  # == set skin id to 'default' for all elements in the site == #
152
152
  skin = nodes.detect {|n| n.kind_of?(Skin) }
153
153
  Node.connection.execute "UPDATE nodes SET skin_id = '#{skin.id}' WHERE site_id = '#{site[:id]}'"
@@ -176,6 +176,7 @@ class Site < ActiveRecord::Base
176
176
  property.string 'usr_prototype_attributes'
177
177
 
178
178
  Site.attributes_for_form[:text] << 'usr_prototype_attributes'
179
+ attr_accessible :usr_prototype_attributes
179
180
 
180
181
  # Return path for static/cached content served by proxy: RAILS_ROOT/sites/_host_/public
181
182
  # If you need to serve from another directory, we do not store the path into the sites table
@@ -201,6 +202,11 @@ class Site < ActiveRecord::Base
201
202
  @anon ||= User.find_by_id_and_site_id(self[:anon_id], self.id)
202
203
  end
203
204
 
205
+ # Return an admin user, this user is used to rebuild index/vhash/etc.
206
+ def any_admin
207
+ @any_admin ||= User.find_by_status_and_site_id(User::Status[:admin], self.id)
208
+ end
209
+
204
210
  # TODO: test
205
211
  def root_node
206
212
  secure(Node) { Node.find(self[:root_id]) }
@@ -297,7 +303,7 @@ class Site < ActiveRecord::Base
297
303
  $iformats[self[:id]] = @iformats = nil
298
304
  end
299
305
  end
300
-
306
+
301
307
  def virtual_classes
302
308
  @iformats ||= begin
303
309
  $iformats ||= {} # mem cache
@@ -359,7 +365,7 @@ class Site < ActiveRecord::Base
359
365
  end
360
366
 
361
367
  # Recreates the fullpath ('/zip/zip/zip').
362
- # TODO: find a way to use SiteWorker (need to remove get_nodes)
368
+ # TODO: find a way to use SiteWorker (need to remove get_nodes): fix rake when this is done.
363
369
  def rebuild_fullpath(parent_id = nil, parent_fullpath = "", parent_basepath = "", start=[])
364
370
  raise Zena::InvalidRecord, "Infinit loop in 'ancestors' (#{start.inspect} --> #{parent_id})" if start.include?(parent_id)
365
371
  start += [parent_id]
@@ -400,7 +406,8 @@ class Site < ActiveRecord::Base
400
406
  # Rebuild property indices for the Site. This method uses the Worker thread to rebuild and works on
401
407
  # chunks of 50 nodes.
402
408
  #
403
- # The visitor used during index rebuild is the anonymous user.
409
+ # The visitor used during index rebuild should be an admin user (to index
410
+ # unpublished templates).
404
411
  def rebuild_index(nodes = nil, page = nil, page_count = nil)
405
412
  if !nodes
406
413
  Site.logger.error("\n----------------- REBUILD INDEX FOR SITE #{host} -----------------\n")
@@ -34,8 +34,8 @@ class Template < TextDocument
34
34
  end
35
35
 
36
36
  attr_protected :tkpath
37
+ before_validation :rebuild_tkpath
37
38
  validate :validate_section, :validate_target_klass
38
- before_validation :template_content_before_validation
39
39
 
40
40
  # Class Methods
41
41
  class << self
@@ -49,12 +49,30 @@ class Template < TextDocument
49
49
  end
50
50
 
51
51
  def skin
52
- @skin ||= secure(Skin) { Skin.find(prop['skin_id']) }
52
+ @skin ||= secure(Skin) { Skin.find(skin_id) }
53
+ end
54
+
55
+ def rebuild_tkpath(rebuild_tklass = nil)
56
+ if target_klass
57
+ # this is a master template (found when choosing the template for rendering)
58
+ if klass = rebuild_tklass
59
+ # rebuilding index
60
+ self.target_klass = klass.name
61
+ self.tkpath = klass.kpath
62
+ self.title = title_from_mode_and_format
63
+ elsif klass = VirtualClass[target_klass]
64
+ self.tkpath = klass.kpath
65
+ end
66
+ else
67
+ self.tkpath = nil
68
+ end
53
69
  end
54
70
 
55
71
  private
56
72
 
57
73
  def set_defaults
74
+ self.mode = nil if mode.blank?
75
+ self.target_klass = nil if target_klass.blank?
58
76
  super
59
77
 
60
78
  # Force template extension to zafu
@@ -66,29 +84,29 @@ class Template < TextDocument
66
84
  if prop.title_changed?
67
85
  if title =~ /^([A-Z][a-zA-Z]+?)(-(([a-zA-Z_\+]*)(-([a-zA-Z_]+)|))|)(\.|\Z)/
68
86
  # title changed force update
69
- prop['target_klass'] = $1 unless prop.target_klass_changed?
70
- prop['mode'] = ($4 || '') unless prop.mode_changed?
71
- prop['format'] = ($6 || 'html') unless prop.format_changed?
87
+ self.target_klass = $1 unless prop.target_klass_changed?
88
+ self.mode = ($4 || '') unless prop.mode_changed?
89
+ self.format = ($6 || 'html') unless prop.format_changed?
72
90
  else
73
91
  # title set but it is not a master template name
74
- prop['target_klass'] = nil
75
- prop['mode'] = nil
76
- prop['format'] = nil
92
+ self.target_klass = nil
93
+ self.mode = nil
94
+ self.format = nil
77
95
  end
78
96
  end
79
97
 
80
98
  if version.edited?
81
- prop['mode'] = prop['mode'].gsub(/[^a-zA-Z\+]/, '') if prop['mode']
99
+ self.mode = mode.gsub(/[^a-zA-Z\+]/, '') if mode
82
100
 
83
- if !prop['target_klass'].blank?
101
+ if !target_klass.blank?
84
102
  # update title
85
- prop['format'] = 'html' if prop['format'].blank?
103
+ self.format = 'html' if format.blank?
86
104
  self.title = title_from_mode_and_format
87
105
 
88
- if text.blank? && prop['format'] == 'html' && prop['mode'] != '+edit'
106
+ if text.blank? && format == 'html' && mode != '+edit'
89
107
  # set a default text
90
108
 
91
- if prop['target_klass'] == 'Node'
109
+ if target_klass == 'Node'
92
110
  self.text = <<END_TXT
93
111
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
94
112
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
@@ -121,9 +139,9 @@ END_TXT
121
139
  end
122
140
 
123
141
  def title_from_mode_and_format(opts={})
124
- opts[:format] ||= prop['format']
125
- opts[:mode ] ||= prop['mode']
126
- opts[:target_klass ] ||= prop['target_klass']
142
+ opts[:format] ||= format
143
+ opts[:mode ] ||= mode
144
+ opts[:target_klass ] ||= target_klass
127
145
  format = opts[:format] == 'html' ? '' : "-#{opts[:format]}"
128
146
  mode = (!opts[:mode].blank? || format != '') ? "-#{opts[:mode]}" : ''
129
147
  "#{opts[:target_klass]}#{mode}#{format}"
@@ -134,25 +152,9 @@ END_TXT
134
152
  end
135
153
 
136
154
  def validate_target_klass
137
- if prop.target_klass_changed? && prop['target_klass']
138
- errors.add('format', "can't be blank") unless prop['format']
139
- # this is a master template (found when choosing the template for rendering)
140
- if target_klass = Node.get_class(prop['target_klass'])
141
- prop['tkpath'] = target_klass.kpath
142
- else
143
- errors.add('target_klass', 'invalid')
144
- end
145
- end
146
- end
147
-
148
- def template_content_before_validation
149
- prop['mode'] = nil if prop['mode' ].blank?
150
- prop['target_klass'] = nil if prop['target_klass'].blank?
151
- unless prop['target_klass']
152
- # this template is not meant to be accessed directly (partial used for inclusion)
153
- prop['tkpath'] = nil
154
- prop['mode'] = nil
155
- prop['format'] = nil
155
+ if target_klass
156
+ errors.add('format', "can't be blank") unless format
157
+ errors.add('target_klass', 'invalid') unless VirtualClass[target_klass]
156
158
  end
157
159
  end
158
160
  end
@@ -5,7 +5,7 @@ class Version < ActiveRecord::Base
5
5
  include RubyLess
6
6
  safe_attribute :created_at, :updated_at, :publish_from, :status, :lang
7
7
  safe_method :node => 'Node', :id => {:class => Number, :method => 'number'},
8
- :number => Number, :user => 'User',
8
+ :number => Number, :user => 'User', :user_id => Number, :user_zip => {:class => Number, :method => 'user_id'},
9
9
  :author => Node.author_proc
10
10
 
11
11
  # We need to include Property::Base so that we can read the properties that
@@ -30,10 +30,20 @@
30
30
  # VirtualClass itself for real classes.
31
31
  #
32
32
  class VirtualClass < Role
33
+ class << self
34
+ attr_accessor :export_attributes
35
+ end
36
+
37
+ self.export_attributes = %w{auto_create_discussion}
38
+
33
39
  attr_accessor :import_result
34
40
  belongs_to :create_group, :class_name => 'Group', :foreign_key => 'create_group_id'
35
41
  validate :valid_virtual_class
36
- attr_accessible :create_group_id, :auto_create_discussion
42
+
43
+ attr_accessible *self.export_attributes
44
+ attr_accessible :create_group_id
45
+
46
+ after_update :propagate_kpath_change
37
47
 
38
48
  include Property::StoredSchema
39
49
  include Zena::Use::Relations::ClassMethods
@@ -154,6 +164,7 @@ class VirtualClass < Role
154
164
  vclass.real_class = real_class
155
165
  vclass.include_role real_class.schema
156
166
  vclass.instance_variable_set(:@is_real_class, true)
167
+ vclass.site_id = current_site.id
157
168
  vclass
158
169
  end
159
170
 
@@ -168,13 +179,6 @@ class VirtualClass < Role
168
179
  class << self
169
180
  attr_accessor :caches_by_site
170
181
 
171
- # Import a hash of virtual class definitions and try to build the virtual classes.
172
- def import(data)
173
- data.keys.map do |klass|
174
- build_virtual_class(klass, data)
175
- end
176
- end
177
-
178
182
  def [](name)
179
183
  find_by_name(name)
180
184
  end
@@ -199,48 +203,6 @@ class VirtualClass < Role
199
203
  def all_classes(base_kpath = 'N', without_list = nil)
200
204
  (self.caches_by_site[current_site.id] ||= Cache.new).all_classes(base_kpath, without_list)
201
205
  end
202
-
203
- # Build a virtual class from a name and a hash of virtual class definitions. If
204
- # the superclass is in the data hash, it is built first.
205
- def build_virtual_class(klass, data)
206
- return data[klass]['result'] if data[klass].has_key?('result')
207
- if virtual_class = VirtualClass[klass]
208
- virtual_class = virtual_class.dup
209
- if virtual_class.superclass.to_s == data[klass]['superclass']
210
- virtual_class.import_result = 'same'
211
- return data[klass]['result'] = virtual_class
212
- else
213
- virtual_class.errors.add(:base, 'conflict')
214
- return data[klass]['result'] = virtual_class
215
- end
216
- else
217
- superclass_name = data[klass]['superclass']
218
- if data[superclass_name]
219
- superclass = build_virtual_class(superclass_name, data)
220
- unless superclass.errors.empty?
221
- virtual_class = VirtualClass.new(:name => klass, :superclass => superclass_name, :create_group_id => current_site.public_group_id)
222
- virtual_class.errors.add(:base, 'conflict in superclass')
223
- return data[klass]['result'] = virtual_class
224
- end
225
- elsif superclass = Node.get_class(superclass_name)
226
- # ok
227
- else
228
- virtual_class = VirtualClass.new(:name => klass, :superclass => superclass_name, :create_group_id => current_site.public_group_id)
229
- virtual_class.errors.add(:base, 'missing superclass')
230
- return data[klass]['result'] = virtual_class
231
- end
232
-
233
- # build
234
- create_group_id = superclass.id ? superclass.create_group_id : current_site.public_group_id
235
- virtual_class = create(data[klass].merge(:name => klass, :create_group_id => create_group_id))
236
- virtual_class.import_result = 'new'
237
- return data[klass]['result'] = virtual_class
238
- end
239
- end
240
-
241
- def export
242
- # TODO
243
- end
244
206
  end
245
207
 
246
208
  self.caches_by_site ||= {}
@@ -261,11 +223,13 @@ class VirtualClass < Role
261
223
 
262
224
  kpath_len = base_kpath.size
263
225
 
226
+ attribute = opts[:class_attr] || 'name'
227
+
264
228
  VirtualClass.all_classes(base_kpath, opts[:without]).map do |vclass|
265
229
  if vclass.create_group_id.nil? || group_ids.include?(vclass.create_group_id)
266
230
  # white spaces are insecable spaces (not ' ')
267
231
  a, b = vclass.kpath, vclass.name
268
- [('  ' * (a.size - kpath_len)) + b, b]
232
+ [('  ' * (a.size - kpath_len)) + b, vclass[attribute]]
269
233
  else
270
234
  nil
271
235
  end
@@ -293,8 +257,56 @@ class VirtualClass < Role
293
257
  name
294
258
  end
295
259
 
296
- def icon=(txt)
297
- self[:icon] = txt.gsub('..', '.') # SECURITY
260
+ def export
261
+ res = super
262
+ self.class.export_attributes.each do |k|
263
+ value = self[k]
264
+ next if value.blank?
265
+ res[k] = value
266
+ end
267
+ subclasses = secure(::Role) do
268
+ ::Role.find(:all, :conditions => [
269
+ '(kpath LIKE ? and type = ?) OR (kpath = ? AND id <> ?)', "#{kpath}_", "VirtualClass", kpath, self.id.to_i
270
+ ], :order => 'kpath ASC')
271
+ end
272
+ if real_class?
273
+ # insert native subclasses
274
+ Node.native_classes.each do |kpath, klass|
275
+ if kpath =~ /\A#{self.kpath}.\Z/
276
+ subclasses ||= []
277
+ subclasses << VirtualClass[klass.name]
278
+ end
279
+ end
280
+
281
+ if subclasses
282
+ subclasses.sort! do |a,b|
283
+ if a.class != b.class
284
+ # Roles before subclasses
285
+ b.class <=> a.class
286
+ else
287
+ a.kpath <=> b.kpath
288
+ end
289
+ end
290
+ end
291
+ end
292
+
293
+ if subclasses
294
+ subclasses.each do |sub|
295
+ res[sub.name] = sub.export
296
+ end
297
+ end
298
+
299
+ relations = Relation.all(
300
+ :conditions => ['source_kpath = ? AND site_id = ?', kpath, site_id],
301
+ :order => 'target_role ASC'
302
+ )
303
+ if !relations.empty?
304
+ res['relations'] = list = Zafu::OrderedHash.new
305
+ relations.each do |rel|
306
+ list[rel.target_role] = rel.export
307
+ end
308
+ end
309
+ res
298
310
  end
299
311
 
300
312
  # check inheritance chain through kpath
@@ -314,11 +326,31 @@ class VirtualClass < Role
314
326
  # explicitely declared as safe are safe. If the VirtualClass reflects a virtual
315
327
  # class, all properties are considered safe.
316
328
  def safe_method_type(signature, receiver = nil)
317
- if signature.size == 1 && (type = safe_column_types[signature.first])
318
- type
319
- else
320
- real_class.safe_method_type(signature, receiver)
329
+
330
+ if signature.size == 1
331
+ method = signature.first
332
+ if receiver && (query = receiver.opts[:query])
333
+ if query.select_keys.include?(method)
334
+ # Resolve by using information in the SELECT part
335
+ # of the custom_query that found this node
336
+
337
+ # In order to use types other then String, we use the overwritten property's
338
+ # type.
339
+ if type = safe_column_types[method]
340
+ return type.merge(:method => "attributes[#{method.inspect}]", :nil => true)
341
+ elsif type = real_class.safe_method_type(signature)
342
+ return type.merge(:nil => true)
343
+ else
344
+ return {:class => String, :method => "attributes[#{method.inspect}]", :nil => true}
345
+ end
346
+ end
347
+ end
348
+ if type = safe_column_types[method]
349
+ return type
350
+ end
321
351
  end
352
+
353
+ real_class.safe_method_type(signature, receiver)
322
354
  end
323
355
 
324
356
  # Return safe columns including super class's safe columns
@@ -426,14 +458,6 @@ class VirtualClass < Role
426
458
  real_class.do_find(*args)
427
459
  end
428
460
 
429
- def superclass=(klass)
430
- if k = VirtualClass[klass]
431
- @superclass = k
432
- else
433
- errors.add('superclass', 'invalid')
434
- end
435
- end
436
-
437
461
  # Build new nodes instances of this VirtualClass
438
462
  def new_instance(hash={})
439
463
  real_class.new(hash, self)
@@ -458,10 +482,6 @@ class VirtualClass < Role
458
482
  @real_class = klass
459
483
  end
460
484
 
461
- def import_result
462
- @import_result || errors[:base]
463
- end
464
-
465
485
  # List all relations that can be set for this class, filtering by
466
486
  # relation group.
467
487
  def filtered_relations(group_filter)
@@ -473,6 +493,27 @@ class VirtualClass < Role
473
493
  @index_groups ||= super
474
494
  end
475
495
 
496
+ protected
497
+ def rebuild_kpath(superclass)
498
+ index = 0
499
+ kpath = nil
500
+ while index < self[:name].length
501
+ try_kpath = superclass.kpath + self[:name][index..index].upcase
502
+ if found = VirtualClass.find_by_kpath(try_kpath)
503
+ if found.id && found.id == self[:id]
504
+ kpath = try_kpath
505
+ break
506
+ end
507
+ else
508
+ kpath = try_kpath
509
+ break
510
+ end
511
+ index += 1
512
+ end
513
+ errors.add('name', 'invalid (could not build unique kpath)') unless kpath
514
+ self[:kpath] = kpath
515
+ end
516
+
476
517
  private
477
518
  def attached_roles
478
519
  ::Role.all(
@@ -483,27 +524,15 @@ class VirtualClass < Role
483
524
  end
484
525
 
485
526
  def valid_virtual_class
527
+ if create_group_id.blank? && new_record?
528
+ self.create_group_id = current_site.public_group_id
529
+ end
530
+
486
531
  return if !errors.empty?
487
532
  @superclass ||= self.superclass
488
533
 
489
534
  if real_class? || name_changed? || @superclass != old.superclass
490
- index = 0
491
- kpath = nil
492
- while index < self[:name].length
493
- try_kpath = @superclass.kpath + self[:name][index..index].upcase
494
- if found = VirtualClass.find_by_kpath(try_kpath)
495
- if found.id && found.id == self[:id]
496
- kpath = try_kpath
497
- break
498
- end
499
- else
500
- kpath = try_kpath
501
- break
502
- end
503
- index += 1
504
- end
505
- errors.add('name', 'invalid (could not build unique kpath)') unless kpath
506
- self[:kpath] = kpath
535
+ rebuild_kpath(@superclass)
507
536
  end
508
537
 
509
538
  self[:real_class] = get_real_class(@superclass)
@@ -524,4 +553,43 @@ class VirtualClass < Role
524
553
  def old
525
554
  @old ||= self.class.find(self[:id])
526
555
  end
556
+
557
+ def propagate_kpath_change
558
+ if kpath_changed?
559
+ old_kpath = kpath_was
560
+ Zena::Db.execute "UPDATE nodes SET kpath = '#{kpath}' WHERE vclass_id = #{self.id} AND site_id = #{current_site.id}"
561
+ Zena::Db.execute "UPDATE roles SET kpath = '#{kpath}' WHERE kpath = '#{old_kpath}' AND site_id = #{current_site.id} AND (type = 'Role' or type IS NULL)"
562
+ # Find templates
563
+ idx_templates = IdxTemplate.all(
564
+ :conditions => ['tkpath = ? AND site_id = ?', old_kpath, site_id]
565
+ )
566
+
567
+ if !idx_templates.empty?
568
+ # update related templates
569
+ if templates = secure(Node) { Node.all(
570
+ :conditions => "id IN (#{idx_templates.map{|r| r.node_id}.join(',')})")}
571
+ templates.each do |t|
572
+ t.visible_versions.each do |version|
573
+ t.version = version
574
+ t.instance_variable_set(:@properties, version.prop)
575
+ t.rebuild_tkpath(self)
576
+ Zena::Db.execute "UPDATE #{version.class.table_name} SET properties=#{Zena::Db.quote(version.class.encode_properties(version.prop))} WHERE id=#{version[:id]}"
577
+ end
578
+ t.rebuild_index!
579
+ end
580
+ end
581
+ end
582
+
583
+ # Sub-classes
584
+ if sub_classes = secure(VirtualClass) { VirtualClass.all(
585
+ :conditions => ['kpath LIKE ?', "#{old_kpath}_"]
586
+ )}
587
+ sub_classes.each do |sub|
588
+ sub.rebuild_kpath(self)
589
+ # What if this fails ? Abort all ?
590
+ sub.save!
591
+ end
592
+ end
593
+ end
594
+ end
527
595
  end
@@ -7,6 +7,6 @@
7
7
  <span class='spacer'><%= (li.role.kind_of?(VirtualClass) ? '+ ' : '<span>* </span>') %></span>
8
8
  <span class='constant'><%= li.role.name %></span>
9
9
  <span class='text'>p.<%= li.ptype %></span>
10
- <span class='string'>'<%= li.name %>'<span><%= li.import_result ? " (#{li.import_result})" : ''%><% if !li.index.blank? -%>, <span class='symbol'>:index</span> =&gt; <span class='symbol'>:<%= li.index_name %></span><% end -%>
10
+ <span class='string'>'<%= li.name %>'<span><% if !li.index.blank? -%>, <span class='symbol'>:index</span> =&gt; <span class='symbol'>:<%= li.index_name %></span><% end -%>
11
11
  </td>
12
12
  </tr>
@@ -1,15 +1,7 @@
1
- <h2 class='title'><%= _('manage properties') %></h2>
1
+ <h2 class='title'><%= _('properties') %></h2>
2
2
 
3
3
  <table id='column_list' class='admin' cellspacing="0">
4
4
  <tr><th class='nav' colspan='7'><%= will_paginate @columns %></th></tr>
5
5
  <%= render :partial=>'columns/li', :collection=>@columns %>
6
6
  <%= render :partial=>'columns/add' %>
7
7
  </table>
8
-
9
- <div class='admin_group'><h3><%= _('import definitions') %></h3>
10
-
11
- <%= form_tag({ :controller => 'columns', :action => 'import'}, {:multipart => true} ) %>
12
- <input style='line-height:1.5em;' name="attachment" class='file' type="file" />
13
- <p class="btn_validate"><input type="submit" value='<%= _('import') %>'/></p>
14
- </form>
15
- </div>
@@ -1,12 +1,14 @@
1
- <h2 class='title'><%= _('manage comments') %></h2>
2
- <div id='comment_errors'></div>
1
+ <h2 class='title'><%= _('comments') %></h2>
3
2
 
4
- <%= will_paginate @comments %>
5
- <ul class='comments' id='comments_list'>
6
- <%= render :partial=>'comments/li', :collection=>@comments %>
7
- </ul>
3
+ <div id='comments'>
4
+ <div id='comment_errors'></div>
5
+ <%= will_paginate @comments %>
6
+ <ul class='comments' id='comments_list'>
7
+ <%= render :partial=>'comments/li', :collection=>@comments %>
8
+ </ul>
8
9
 
9
10
 
10
- <div id='bin'>
11
- <%= render :partial=>'comments/bin' %>
11
+ <div id='bin'>
12
+ <%= render :partial=>'comments/bin' %>
13
+ </div>
12
14
  </div>
@@ -1,10 +1,9 @@
1
- <%= link_to_function(_('cancel'), "['crop', 'crop_form'].each(Element.toggle);$('crop_form').innerHTML = '';")%>
2
- <ul class='infos' id='crop_options'>
1
+ <ul class='crop_options'>
3
2
  <li>x: <input type='text' id='posx' name='node[crop][x]' size='4'/> y: <input type='text' id='posy' name='node[crop][y]' size='4'/> // w: <input type='text' id='width' name='node[crop][w]' size='4'/> h: <input type='text' id='height' name='node[crop][h]' size='4'/></li>
4
- <li><b><%= _('convert') + '</b> ' + crop_formats(@node).join(' ') %></li>
5
- <li><b><%= _('max size') %></b> <input type='text' name='node[crop][max_value]' size='4' value='<%= fs = fsize(@node.size); fs.to_f %>'/>
3
+ <li><b><%= _('convert') + '</b> ' + crop_formats(node).join(' ') %></li>
4
+ <li><b><%= _('max size') %></b> <input type='text' name='node[crop][max_value]' size='4' value='<%= fs = fsize(node.size); fs.to_f %>'/>
6
5
  <%= ['Kb', 'Mb'].inject('') {|r,e| r + "<input type='radio' name='node[crop][max_unit]' value='#{e}'#{fs =~ /#{e}/ ? " checked='checked'" : ''}/> #{e} " }%>
7
6
  </li>
8
7
  </ul>
9
- <p class='edit_info'><%= _('original') %> <%= @node.width %>x<%= @node.height %>, <%= _('original is #X this view').sub('#X', "<b>#{sprintf('%.1f', @node.width.to_f / @node.width(Iformat['edit']))}x</b>")%></p>
10
- <%= img_tag(@node, :mode=>'edit', :id=>'edit_img') %>
8
+ <p class='edit_info'><%= _('original') %> <%= node.width %>x<%= node.height %>, <%= _('original is #X this view').sub('#X', "<b>#{sprintf('%.1f', node.width.to_f / node.width(Iformat['edit']))}x</b>")%></p>
9
+ <%= img_tag(node, :mode=>'edit', :id=>img_id) %>
@@ -1,4 +1,5 @@
1
- page.replace_html 'crop_form', :partial=>'documents/crop'
1
+ page.replace_html 'crop_form', :partial=>'documents/crop', :locals => {:node => @node, :img_id => 'edit_img'}
2
+ page.insert_html :top, 'crop_form', :inline => %Q{<%= link_to_function(_('cancel'), "['crop', 'crop_form'].each(Element.toggle);$('crop_form').innerHTML = '';")%>}
2
3
  page.toggle 'crop_form', 'crop'
3
- page << "new Zena.Div_editor('edit_img', 'posx', 'posy', 'width', 'height', #{@node.width / @node.width(Iformat['edit']).to_f}, 20, 220);"
4
+ page << "new Zena.Div_editor('edit_img', 'posx', 'posy', 'width', 'height', #{@node.width / @node.width(Iformat['edit']).to_f});"
4
5
  page << "self.resizeTo(520,770);"
@@ -1,4 +1,4 @@
1
- <h2 class='title'><%= _('manage groups') %></h2>
1
+ <h2 class='title'><%= _('groups') %></h2>
2
2
  <table class="admin" cellspacing="0">
3
3
  <tr><th class='nav' colspan='3'><%= will_paginate @groups %></th></tr>
4
4
  <%= render :partial=>'groups/li', :collection=>@groups %>
@@ -1,4 +1,4 @@
1
- <h2 class='title'><%= _('manage image formats') %></h2>
1
+ <h2 class='title'><%= _('image formats') %></h2>
2
2
 
3
3
  <table id='iformat_list' class='admin' cellspacing="0">
4
4
  <tr><th class='nav' colspan='7'>&nbsp;</th></tr>
@@ -6,7 +6,7 @@
6
6
  <% @nodes.each do |node| -%>
7
7
  <tr class='import_<%= node.errors.empty? ? (node[:create_or_update]) : 'error' %>'>
8
8
  <td><%= img_tag(node, :mode=>'tiny') %></td>
9
- <td><%= node.short_path %></td>
9
+ <td><%= node.short_path.join('/') %></td>
10
10
  <% if node[:create_or_update] == 'same' && node.errors.empty? -%>
11
11
  <td colspan='3'><%= _('same') %></td>
12
12
  <% else -%>