zena 1.2.4 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
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