zena 1.2.4 → 1.2.5

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 (104) hide show
  1. data/History.txt +18 -0
  2. data/app/controllers/nodes_controller.rb +11 -6
  3. data/app/controllers/sites_controller.rb +3 -2
  4. data/app/controllers/user_sessions_controller.rb +1 -1
  5. data/app/controllers/virtual_classes_controller.rb +3 -2
  6. data/app/models/document.rb +2 -2
  7. data/app/models/node.rb +6 -1
  8. data/app/models/note.rb +3 -27
  9. data/app/models/role.rb +11 -5
  10. data/app/models/site.rb +140 -43
  11. data/app/models/string_hash.rb +2 -0
  12. data/app/models/user.rb +9 -4
  13. data/app/models/user_session.rb +1 -1
  14. data/app/models/virtual_class.rb +49 -18
  15. data/app/views/sites/_form.erb +7 -4
  16. data/app/views/sites/_li.erb +15 -9
  17. data/app/views/users/_li.rhtml +3 -0
  18. data/app/views/users/index.rhtml +1 -1
  19. data/app/views/virtual_classes/_form.erb +1 -1
  20. data/bricks/acls/zena/init.rb +1 -2
  21. data/bricks/acls/zena/test/sites/erebus/roles.yml +4 -0
  22. data/bricks/activity/lib/bricks/activity.rb +24 -0
  23. data/bricks/activity/zena/migrate/20130711135905_add_activity_to_user.rb +9 -0
  24. data/bricks/activity/zena/test/integration/activity_integration_test.rb +29 -0
  25. data/bricks/captcha/zena/init.rb +0 -3
  26. data/bricks/fs_skin/lib/bricks/fs_skin.rb +3 -3
  27. data/bricks/fs_skin/zena/skins/blog/Node.zafu +1 -1
  28. data/bricks/fs_skin/zena/tasks.rb +2 -2
  29. data/bricks/fs_skin/zena/test/integration/fs_skin_integration_test.rb +2 -2
  30. data/bricks/fs_skin/zena/test/unit/fs_skin_test.rb +1 -1
  31. data/bricks/math/zena/init.rb +0 -3
  32. data/bricks/pdf/zena/init.rb +1 -5
  33. data/bricks/worker/zena/init.rb +0 -2
  34. data/bricks/zena/zena/migrate/20130617164527_add_master_id_to_site.rb +13 -0
  35. data/bricks/zena/zena/migrate/20130712081512_alter_login_users.rb +9 -0
  36. data/config/bricks.yml +4 -1
  37. data/lib/bricks/loader.rb +17 -9
  38. data/lib/tasks/zena.rake +40 -5
  39. data/lib/zafu/process/ruby_less_processing.rb +3 -5
  40. data/lib/zena.rb +2 -0
  41. data/lib/zena/acts/secure.rb +11 -2
  42. data/lib/zena/console.rb +5 -4
  43. data/lib/zena/core_ext/string.rb +2 -2
  44. data/lib/zena/deploy.rb +22 -6
  45. data/lib/zena/deploy/logrotate_app.rhtml +10 -7
  46. data/lib/zena/deploy/logrotate_host.rhtml +21 -26
  47. data/lib/zena/deploy/vhost.rhtml +3 -3
  48. data/lib/zena/foxy_parser.rb +1 -1
  49. data/lib/zena/info.rb +1 -1
  50. data/lib/zena/site_worker.rb +2 -2
  51. data/lib/zena/use.rb +0 -1
  52. data/lib/zena/use/ancestry.rb +1 -1
  53. data/lib/zena/use/authlogic.rb +2 -1
  54. data/lib/zena/use/display.rb +6 -0
  55. data/lib/zena/use/query_builder.rb +1 -1
  56. data/lib/zena/use/query_node.rb +3 -3
  57. data/lib/zena/use/rendering.rb +47 -13
  58. data/lib/zena/use/test_helper.rb +1 -1
  59. data/lib/zena/use/urls.rb +6 -104
  60. data/lib/zena/use/zafu_safe_definitions.rb +9 -4
  61. data/lib/zena/use/zafu_templates.rb +1 -0
  62. data/locale/app.pot +4 -0
  63. data/locale/de/LC_MESSAGES/zena.mo +0 -0
  64. data/locale/de/zena.po +6 -2
  65. data/locale/en/LC_MESSAGES/zena.mo +0 -0
  66. data/locale/en/zena.po +6 -2
  67. data/locale/fr/LC_MESSAGES/zena.mo +0 -0
  68. data/locale/fr/zena.po +5 -1
  69. data/locale/it/LC_MESSAGES/zena.mo +0 -0
  70. data/locale/it/zena.po +6 -2
  71. data/locale/zena.pot +4 -0
  72. data/test/functional/nodes_controller_test.rb +2 -133
  73. data/test/functional/sites_controller_test.rb +1 -1
  74. data/test/integration/multiple_hosts_test.rb +2 -1
  75. data/test/integration/navigation_test.rb +37 -0
  76. data/test/integration/query_node/basic.yml +1 -1
  77. data/test/integration/zafu_compiler/ajax.yml +7 -0
  78. data/test/integration/zafu_compiler/comments.yml +2 -2
  79. data/test/integration/zafu_compiler/context.yml +6 -2
  80. data/test/integration/zafu_compiler/display.yml +16 -4
  81. data/test/integration/zafu_compiler/forms.yml +1 -0
  82. data/test/integration/zafu_compiler/query.yml +7 -1
  83. data/test/integration/zafu_compiler/relations.yml +12 -7
  84. data/test/integration/zafu_compiler/roles.yml +3 -3
  85. data/test/integration/zafu_compiler/rubyless.yml +1 -2
  86. data/test/integration/zafu_compiler/safe_definitions.yml +5 -0
  87. data/test/sites/complex/roles.yml +1 -1
  88. data/test/sites/ocean/roles.yml +4 -0
  89. data/test/sites/zena/columns.yml +3 -1
  90. data/test/sites/zena/roles.yml +5 -1
  91. data/test/sites/zena/sites.yml +22 -0
  92. data/test/unit/document_test.rb +14 -0
  93. data/test/unit/node_test.rb +14 -0
  94. data/test/unit/role_test.rb +19 -4
  95. data/test/unit/site_test.rb +67 -0
  96. data/test/unit/user_test.rb +20 -0
  97. data/test/unit/virtual_class_test.rb +116 -11
  98. data/test/unit/zena/use/rendering_test.rb +1 -1
  99. data/zena.gemspec +67 -66
  100. metadata +126 -125
  101. data/bricks/fs_skin/zena/init.rb +0 -1
  102. data/bricks/grid/zena/init.rb +0 -4
  103. data/bricks/single/zena/init.rb +0 -1
  104. data/bricks/spreadsheet/zena/init.rb +0 -3
@@ -1,3 +1,21 @@
1
+ == 1.2.5 2013-07-15
2
+
3
+ * Major changes
4
+ * To support video streaming: redirect on first document cache. WARNING: This
5
+ implies changes to vhost files !
6
+ * WARN: Properties in list context do not resolve with first element anymore. "first" prefix needed.
7
+ * Enable VirtualClass on native classes !
8
+ * Added 'activity' brick which records visitor's last "seen_at" date. <== TODO: Document
9
+
10
+ * Minor changes
11
+ * Fixed encode_params in Ajax requests.
12
+ * Fixed cached path in vhost file.
13
+ * Added "uuid" method (generates a unique id). <== TODO: Document
14
+ * Added support for JSON String in [string_hash] <== TODO: Document
15
+ * Added 'login_info' to Node.
16
+ * Added 'group_names' to User class.
17
+ * Fixed [headers] when 'Status' is set (change to redirect on 300).
18
+
1
19
  == 1.2.4 2013-06-13
2
20
 
3
21
  * Major changes
@@ -203,14 +203,19 @@ class NodesController < ApplicationController
203
203
  else
204
204
  content_path = @node.filepath
205
205
  end
206
-
206
+
207
+ # content_path is used to cache by creating a symlink
208
+ cache_ok = cache_page(:content_path => content_path, :authenticated => @node.v_public?)
209
+
207
210
  if content_path
208
- # FIXME RAILS: remove 'stream => false' when rails streaming is fixed
209
- send_file(content_path, :filename => @node.filename, :type => @node.content_type, :disposition => 'inline', :stream => false, :x_sendfile => ENABLE_XSENDFILE)
211
+ if @node.v_public? && cache_ok
212
+ # This is the simplest working solution to save cached version and use apache for serving (all xsendfile and such do not work with video streaming).
213
+ redirect_to data_path(@node, :mode => params[:mode]) + "?1"
214
+ else
215
+ # FIXME RAILS: remove 'stream => false' when rails streaming is fixed
216
+ send_file(content_path, :filename => @node.filename, :type => @node.content_type, :disposition => 'inline', :stream => false, :x_sendfile => ENABLE_XSENDFILE)
217
+ end
210
218
  end
211
-
212
- # content_path is used to cache by creating a symlink
213
- cache_page(:content_path => content_path, :authenticated => @node.v_public?)
214
219
  else
215
220
  render_and_cache
216
221
  # FIXME: redirect to document format should occur in render_and_cache
@@ -54,8 +54,9 @@ class SitesController < ApplicationController
54
54
  end
55
55
 
56
56
  def clear_cache
57
- @site = secure!(Site) { Site.first }
58
- @site.clear_cache
57
+ secure!(Site) { Site.all }.each do |site|
58
+ site.clear_cache
59
+ end
59
60
  redirect_to '/'
60
61
  end
61
62
 
@@ -58,7 +58,7 @@ class UserSessionsController < ApplicationController
58
58
  raise ActiveRecord::RecordNotFound.new("host not found #{request.host}")
59
59
  end
60
60
 
61
- Thread.current[:visitor] = anonymous_visitor(site)
61
+ setup_visitor(anonymous_visitor(site), site)
62
62
  end
63
63
 
64
64
  def redirect_after_login
@@ -10,11 +10,12 @@ class VirtualClassesController < ApplicationController
10
10
  secure(::Role) do
11
11
  @virtual_classes = ::Role.paginate(:all, :order => 'kpath', :per_page => 200, :page => params[:page])
12
12
  end
13
-
13
+
14
+ list = @virtual_classes.map(&:name)
14
15
  if last = @virtual_classes.last
15
16
  last_kpath = last.kpath
16
17
  Node.native_classes.each do |kpath, klass|
17
- if kpath < last_kpath
18
+ if kpath < last_kpath && !list.include?(klass.name)
18
19
  @virtual_classes << klass
19
20
  end
20
21
  end
@@ -124,7 +124,7 @@ class Document < Node
124
124
  # Try to find a virtual sub-class accepting the content type
125
125
  vclass = nil
126
126
  VirtualClass[base.to_s].sub_classes.each do |v|
127
- next if v.real_class?
127
+ next if v.real_class != base
128
128
 
129
129
  if content_type =~ v.content_type_re
130
130
  vclass = v
@@ -207,7 +207,7 @@ class Document < Node
207
207
  end
208
208
  end
209
209
 
210
- # Make sure the new file
210
+ # Make sure the new file is OK with current class.
211
211
  def valid_content_type
212
212
  return true unless prop.content_type_changed?
213
213
 
@@ -225,7 +225,8 @@ class Node < ActiveRecord::Base
225
225
  :data_b => {:class => ['DataEntry'], :zafu => {:data_root => 'node_b'}},
226
226
  :data_c => {:class => ['DataEntry'], :zafu => {:data_root => 'node_c'}},
227
227
  :data_d => {:class => ['DataEntry'], :zafu => {:data_root => 'node_d'}},
228
- :traductions => ['Version'], :discussion => 'Discussion'
228
+ :traductions => ['Version'], :discussion => 'Discussion',
229
+ :login_info => 'User'
229
230
 
230
231
  # we use safe_method because the columns can be null, but the values are never null
231
232
  safe_method :kpath => String, :user_zip => Number, :user_id => Number,
@@ -1205,6 +1206,10 @@ class Node < ActiveRecord::Base
1205
1206
  def user
1206
1207
  secure!(User) { o_user }
1207
1208
  end
1209
+
1210
+ def login_info
1211
+ secure(User) { User.find(:first, :conditions => {:node_id => self.id})}
1212
+ end
1208
1213
 
1209
1214
  # Find all data entries linked to the current node
1210
1215
  def data
@@ -1,33 +1,9 @@
1
1
  =begin rdoc
2
- A note is a 'dated' element. It is typically used for blog entries or/and calendar events.
3
-
4
- === dates
5
-
6
- On top of the Node's dates (+created_at+, +updated_at+, +publish_from+), a note uses the following:
7
-
8
- log_at:: used to sort/display blog entries
9
- event_at:: used in calendars
10
-
11
- These two dates enable you to announce an event in the blog list a couple of days before it actually occurs (event_at). All dates are stored internally as 'utc' and are converted to/from the visitor's current timezone.
12
-
13
- === links
14
-
15
- Default links for Notes are:
16
-
17
- calendars:: make this note appear in the given calendar (there is one calendar per project). By default the note is not included in its project's calendar.
18
- projects:: make this note appear in the blog of the given project. A note always appear in its project.
19
-
20
- === subclasses
21
-
22
-
23
- === subclasses to implement before 1.0
24
- Task:: manage things to be done (parent = Note = Todo). The parent provides the 'due date'.
25
- Letter:: like the name says...
26
- Request:: subclass of Todo. Manage user/client requests.
27
- Bug:: (the class we most need!). subclass of Request.
28
- Milestone:: special event used when choosing a Request/Bug's parent.
2
+ This class is not really useful anymore since 'event_at' and 'log_at' are obsolete. This class is really used for
3
+ testing having a virtual class 'Note' with a real ruby class of the same Name.
29
4
  =end
30
5
  class Note < Node
6
+
31
7
  class << self
32
8
 
33
9
  def select_classes
@@ -271,12 +271,18 @@ class Role < ActiveRecord::Base
271
271
 
272
272
  def validate_role
273
273
  errors.add('base', 'You do not have the rights to change roles.') unless visitor.is_admin?
274
- if new_record?
275
- errors.add('superclass', 'invalid') unless @superclass.kind_of?(VirtualClass) && @superclass.kpath
276
- end
277
274
 
278
- if @superclass && self.class == ::Role
279
- self.kpath = @superclass.kpath
275
+ if name == 'Node'
276
+ self.kpath = 'N'
277
+ errors.add('base', 'cannot create role of name "Node"') if self.class == ::Role
278
+ else
279
+ if new_record?
280
+ errors.add('superclass', 'invalid') unless @superclass.kind_of?(VirtualClass) && @superclass.kpath
281
+ end
282
+
283
+ if @superclass && self.class == ::Role
284
+ self.kpath = @superclass.kpath
285
+ end
280
286
  end
281
287
  end
282
288
 
@@ -6,7 +6,8 @@ A zena installation supports many sites. Each site is uniquely identified by it'
6
6
  The #Site model holds configuration information for a site:
7
7
 
8
8
  +host+:: Unique host name. (teti.ch, zenadmin.org, dev.example.org, ...)
9
- +root_id+:: Site root node id. This is the only node in the site without a parent.
9
+ +orphan_id+:: Site seed node id. This is the only node in the site without a parent.
10
+ +root_id+:: This is the apparent root of the site.
10
11
  +anon_id+:: Anonymous user id. This user is the 'public' user of the site. Even if +authorize+ is set to true, this user is needed to configure the defaults for all newly created users.
11
12
  +public_group_id+:: Id of the 'public' group. Every user of the site (with 'anonymous user') belongs to this group.
12
13
  +site_group_id+:: Id of the 'site' group. Every user except anonymous are part of this group. This group can be seen as the 'logged in users' group.
@@ -16,6 +17,7 @@ The #Site model holds configuration information for a site:
16
17
  +default_lang+:: The default language of the site.
17
18
  =end
18
19
  class Site < ActiveRecord::Base
20
+ attr_accessor :alias # = site alias (different settings + domain)
19
21
  CLEANUP_SQL = [
20
22
  ['attachments' , 'site_id = ?'],
21
23
  ['cached_pages' , 'site_id = ?'],
@@ -54,12 +56,13 @@ class Site < ActiveRecord::Base
54
56
  CACHE_PATH = Bricks.raw_config['cache_path'] || '/public'
55
57
 
56
58
  include RubyLess
57
- safe_method :host => String, :lang_list => [String], :default_lang => String
58
- safe_method :root => Proc.new {|h, r, s| {:method => 'root_node', :class => VirtualClass['Project'], :nil => true}}
59
+ safe_method :host => String, :lang_list => [String], :default_lang => String, :master_host => String
60
+ safe_method :root => Proc.new {|h, r, s| {:method => 'root_node', :class => VirtualClass['Project'], :nil => true}}
61
+ safe_method :orphan => Proc.new {|h, r, s| {:method => 'orphan_node', :class => VirtualClass['Project'], :nil => true}}
59
62
 
60
63
  validate :valid_site
61
64
  validates_uniqueness_of :host
62
- attr_accessible :name, :languages, :default_lang, :authentication, :http_auth, :ssl_on_auth, :auto_publish, :redit_time, :api_group_id
65
+ attr_accessible :name, :languages, :default_lang, :authentication, :http_auth, :ssl_on_auth, :auto_publish, :redit_time, :api_group_id, :root_zip
63
66
  has_many :groups, :order => "name"
64
67
  has_many :nodes
65
68
  has_many :users
@@ -74,7 +77,12 @@ class Site < ActiveRecord::Base
74
77
 
75
78
  @@attributes_for_form = {
76
79
  :bool => %w{authentication http_auth auto_publish ssl_on_auth},
77
- :text => %w{name languages default_lang},
80
+ :text => %w{languages default_lang},
81
+ }
82
+
83
+ @@alias_attributes_for_form = {
84
+ :bool => %w{authentication auto_publish ssl_on_auth},
85
+ :text => %w{},
78
86
  }
79
87
 
80
88
  class << self
@@ -117,7 +125,7 @@ class Site < ActiveRecord::Base
117
125
  :lang => site.default_lang, :status => User::Status[:admin])
118
126
  admin_user.site = site
119
127
 
120
- Thread.current[:visitor] = admin_user
128
+ setup_visitor(admin_user, site)
121
129
 
122
130
  unless admin_user.save
123
131
  # rollback
@@ -174,6 +182,8 @@ class Site < ActiveRecord::Base
174
182
  raise Exception.new("Could not publish root node for site [#{host}] (site#{site[:id]})\n#{root.errors.map{|k,v| "[#{k}] #{v}"}.join("\n")}") unless (root.v_status == Zena::Status::Pub || root.publish)
175
183
 
176
184
  site.root_id = root[:id]
185
+ site.orphan_id = root[:id]
186
+
177
187
  # Make sure safe definitions on Time/Array/String are available on prop_eval validation.
178
188
  Zena::Use::ZafuSafeDefinitions
179
189
  # Should not be needed since we load PropEval in Node, but it does not work
@@ -210,15 +220,27 @@ class Site < ActiveRecord::Base
210
220
  site.instance_variable_set(:@being_created, false)
211
221
  site
212
222
  end
223
+
224
+ def master_sites
225
+ Site.all(:conditions => ['master_id is NULL'])
226
+ end
213
227
 
214
228
  def find_by_host(host)
215
229
  host = $1 if host =~ /^(.*)\.$/
216
- self.find(:first, :conditions => ['host = ?', host]) rescue nil
230
+ if site = self.find(:first, :conditions => ['host = ?', host]) rescue nil
231
+ if id = site.master_id
232
+ # The loaded site is an alias, load master site.
233
+ master = self.find(:first, :conditions => ['id = ?', id])
234
+ master.alias = site
235
+ site = master
236
+ end
237
+ end
238
+ site
217
239
  end
218
240
 
219
241
  # List of attributes that can be configured in the admin form
220
- def attributes_for_form
221
- @@attributes_for_form
242
+ def attributes_for_form(is_alias = false)
243
+ is_alias ? @@alias_attributes_for_form : @@attributes_for_form
222
244
  end
223
245
  end
224
246
 
@@ -229,31 +251,31 @@ class Site < ActiveRecord::Base
229
251
  Site.attributes_for_form[:text] << 'usr_prototype_attributes'
230
252
  Site.attributes_for_form[:bool] << 'expire_in_dev'
231
253
  attr_accessible :usr_prototype_attributes, :expire_in_dev
232
-
254
+
233
255
  # Return path for static/cached content served by proxy: RAILS_ROOT/sites/_host_/public
234
256
  # If you need to serve from another directory, we do not store the path into the sites table
235
257
  # for security reasons. The easiest way around this limitation is to symlink the 'public' directory.
236
258
  def public_path
237
- "/#{self[:host]}#{PUBLIC_PATH}"
259
+ "/#{host}#{PUBLIC_PATH}"
238
260
  end
239
261
 
240
262
  # This is the place where cached files should be stored in case we do not want
241
263
  # to store the cached file inside the public directory.
242
264
  def cache_path
243
- "/#{self[:host]}#{CACHE_PATH}"
265
+ "/#{host}#{CACHE_PATH}"
244
266
  end
245
267
 
246
268
  # Return path for documents data: RAILS_ROOT/sites/_host_/data
247
269
  # You can symlink the 'data' directory if you need to keep the data in some other place.
248
270
  def data_path
249
- "/#{self[:host]}/data"
271
+ "/#{master_host}/data"
250
272
  end
251
273
 
252
274
  # Return the path for zafu rendered templates: RAILS_ROOT/sites/_host_/zafu
253
275
  def zafu_path
254
- "/#{self[:host]}/zafu"
276
+ "/#{master_host}/zafu"
255
277
  end
256
-
278
+
257
279
  # Return the anonymous user, the one used by anonymous visitors to visit the public part
258
280
  # of the site.
259
281
  def anon
@@ -268,8 +290,12 @@ class Site < ActiveRecord::Base
268
290
  # Return the root node or a dummy if the visitor cannot view root
269
291
  # node (such as during a 404 or login rendering).
270
292
  def root_node
271
- @root ||= secure(Node) { Node.find(self.root_id) } ||
272
- Node.new(:title => host)
293
+ @root ||= secure(Node) { Node.find(root_id) } || Node.new(:title => host)
294
+ end
295
+
296
+ # Return the orphan node.
297
+ def orphan_node
298
+ @orphan ||= secure(Node) { Node.find(orphan_id) } || Node.new(:title => host)
273
299
  end
274
300
 
275
301
  # Return the public group: the one in which every visitor belongs.
@@ -298,16 +324,6 @@ class Site < ActiveRecord::Base
298
324
  @admin_user_ids ||= secure!(User) { User.find(:all, :conditions => "status >= #{User::Status[:admin]}") }.map {|r| r[:id]}
299
325
  end
300
326
 
301
- # Return true if the site is configured to force authentication
302
- def authentication?
303
- self[:authentication]
304
- end
305
-
306
- # Return true if the site is configured to automatically publish redactions
307
- def auto_publish?
308
- self[:auto_publish]
309
- end
310
-
311
327
  # Set redit time from a string of the form "1d 4h 5s" or "4 days"
312
328
  def redit_time=(val)
313
329
  if val.kind_of?(String)
@@ -337,6 +353,64 @@ class Site < ActiveRecord::Base
337
353
  def lang_list
338
354
  (self[:languages] || "").split(',').map(&:strip)
339
355
  end
356
+
357
+ ###### Alias handling
358
+ def is_alias?
359
+ !self[:master_id].blank?
360
+ end
361
+
362
+ # This is the host of the master site.
363
+ def master_host
364
+ self[:host]
365
+ end
366
+
367
+ # Host with aliasing (returns alias host if alias is loaded)
368
+ def host
369
+ @alias && @alias.host || master_host
370
+ end
371
+
372
+ def ssl_on_auth
373
+ @alias && @alias.prop['ssl_on_auth'] || self.prop['ssl_on_auth']
374
+ end
375
+
376
+ # Return true if the site is configured to automatically publish redactions
377
+ def auto_publish?
378
+ @alias && @alias[:auto_publish] || self[:auto_publish]
379
+ end
380
+
381
+ # Return true if the site is configured to force authentication
382
+ def authentication?
383
+ @alias && @alias[:authentication] || self[:authentication]
384
+ end
385
+
386
+ def root_id
387
+ @root_id ||= @alias && @alias[:root_id] || self[:root_id]
388
+ end
389
+
390
+ def root_zip
391
+ root_node.zip
392
+ end
393
+
394
+ def root_zip=(zip)
395
+ if id = secure(Node) { Node.translate_pseudo_id(zip) }
396
+ self[:root_id] = id
397
+ else
398
+ @root_zip_error = _('could not be found')
399
+ end
400
+ end
401
+
402
+ def create_alias(hostname)
403
+ raise "Hostname '#{hostname}' already exists" if Site.find_by_host(hostname)
404
+ ali = Site.new(self.attributes)
405
+ ali.host = hostname
406
+ ali.master_id = self.id
407
+ ali.orphan_id = self.orphan_id
408
+ ali.root_id = self.root_id
409
+ ali.prop = self.prop
410
+ ali.save
411
+ ali
412
+ end
413
+ ######
340
414
 
341
415
  def being_created?
342
416
  @being_created
@@ -383,24 +457,36 @@ class Site < ActiveRecord::Base
383
457
  end
384
458
 
385
459
  def clear_cache(clear_zafu = true)
386
- path = "#{SITES_ROOT}#{self.cache_path}"
387
- Site.logger.error("\n-----------------\nCLEAR CACHE FOR SITE #{host}\n-----------------\n")
460
+ paths = ["#{SITES_ROOT}#{self.cache_path}"]
461
+ aliases = Site.all(:conditions => {:master_id => self.id})
462
+ aliases.each do |site|
463
+ paths << "#{SITES_ROOT}#{site.cache_path}"
464
+ end
465
+ Site.logger.error("\n-----------------\nCLEAR CACHE FOR SITE #{host} (#{aliases.map(&:host).join(', ')})\n-----------------\n")
388
466
 
389
- if File.exist?(path)
390
- Dir.foreach(path) do |elem|
391
- next unless elem =~ /^(\w\w\.html|\w\w|login\.html)$/
392
- FileUtils.rmtree(File.join(path, elem))
393
- end
467
+ paths.each do |path|
468
+ if File.exist?(path)
469
+ Dir.foreach(path) do |elem|
470
+ next unless elem =~ /^(\w\w\.html|\w\w|login\.html)$/
471
+ FileUtils.rmtree(File.join(path, elem))
472
+ end
394
473
 
395
- Zena::Db.execute "DELETE FROM caches WHERE site_id = #{self[:id]}"
396
- Zena::Db.execute "DELETE FROM cached_pages_nodes WHERE cached_pages_nodes.node_id IN (SELECT nodes.id FROM nodes WHERE nodes.site_id = #{self[:id]})"
397
- Zena::Db.execute "DELETE FROM cached_pages WHERE site_id = #{self[:id]}"
474
+ Zena::Db.execute "DELETE FROM caches WHERE site_id = #{self[:id]}"
475
+ Zena::Db.execute "DELETE FROM cached_pages_nodes WHERE cached_pages_nodes.node_id IN (SELECT nodes.id FROM nodes WHERE nodes.site_id = #{self[:id]})"
476
+ Zena::Db.execute "DELETE FROM cached_pages WHERE site_id = #{self[:id]}"
477
+ end
398
478
  end
399
-
479
+
480
+ # Clear zafu
400
481
  if clear_zafu
401
- path = "#{SITES_ROOT}#{self.zafu_path}"
402
- if File.exist?(path)
403
- FileUtils.rmtree(path)
482
+ paths = ["#{SITES_ROOT}#{self.zafu_path}"]
483
+ aliases.each do |site|
484
+ paths << "#{SITES_ROOT}#{site.cache_path}"
485
+ end
486
+ paths.each do |path|
487
+ if File.exist?(path)
488
+ FileUtils.rmtree(path)
489
+ end
404
490
  end
405
491
  end
406
492
 
@@ -510,8 +596,19 @@ class Site < ActiveRecord::Base
510
596
  private
511
597
  def valid_site
512
598
  errors.add(:host, 'invalid') if self[:host].nil? || (self[:host] =~ /^\./) || (self[:host] =~ /[^\w\.\-]/)
513
- errors.add(:languages, 'invalid') unless self[:languages].split(',').inject(true){|i,l| (i && l =~ /^\w\w$/)}
514
- errors.add(:default_lang, 'invalid') unless self[:languages].split(',').include?(self[:default_lang])
599
+
600
+ if !is_alias?
601
+ errors.add(:languages, 'invalid') unless self[:languages].split(',').inject(true){|i,l| (i && l =~ /^\w\w$/)}
602
+ errors.add(:default_lang, 'invalid') unless self[:languages].split(',').include?(self[:default_lang])
603
+ else
604
+ self[:languages] = nil
605
+ self[:default_lang] = nil
606
+ end
607
+
608
+ if @root_zip_error
609
+ errors.add('root_id', @root_zip_error)
610
+ @root_zip_error = nil
611
+ end
515
612
  end
516
613
 
517
614
  end