zena 1.2.5 → 1.2.6
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +9 -0
- data/app/controllers/nodes_controller.rb +1 -1
- data/app/controllers/versions_controller.rb +1 -1
- data/app/models/node.rb +0 -58
- data/app/models/site.rb +34 -27
- data/app/models/user.rb +1 -1
- data/app/views/sites/_form.erb +1 -1
- data/app/views/sites/_li.erb +2 -2
- data/app/views/zafu/default/Node.zafu +2 -2
- data/bricks/pdf/lib/bricks/pdf.rb +2 -1
- data/bricks/pdf/lib/bricks/pdf/engine/prince.rb +2 -1
- data/bricks/zena/zena/migrate/20130715171232_rename_orphan_and_root_in_site.rb +11 -0
- data/config/bricks.yml +5 -0
- data/config/gems.yml +1 -1
- data/db/init/base/skins/default/Node.zafu +2 -2
- data/lib/zena.rb +2 -0
- data/lib/zena/controller/test_case.rb +1 -1
- data/lib/zena/info.rb +1 -1
- data/lib/zena/use/ancestry.rb +1 -1
- data/lib/zena/use/authlogic.rb +2 -2
- data/lib/zena/use/query_node.rb +7 -1
- data/lib/zena/use/test_helper.rb +9 -3
- data/test/integration/query_node/filters.yml +8 -0
- data/test/integration/query_node/relations.yml +21 -1
- data/test/integration/query_node_test.rb +2 -2
- data/test/integration/zafu_compiler/context.yml +8 -1
- data/test/integration/zafu_compiler_test.rb +1 -1
- data/test/sites/zena/sites.yml +3 -3
- data/test/unit/site_test.rb +5 -1
- data/test/unit/zena/use/upload_test.rb +2 -2
- data/zena.gemspec +60 -59
- metadata +131 -130
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 1.2.6
|
2
|
+
|
3
|
+
* Major changes
|
4
|
+
* Support for multiple domains (site alias) for a single site.
|
5
|
+
|
6
|
+
* Minor changes
|
7
|
+
* Added 'cmd' option for pdf brick. <== TODO: Document
|
8
|
+
* Added 'local_ips' option to bricks.yml (used when pdf engine cannot render non-public assets). <== TODO: Document
|
1
9
|
== 1.2.5 2013-07-15
|
2
10
|
|
3
11
|
* Major changes
|
@@ -6,6 +14,7 @@
|
|
6
14
|
* WARN: Properties in list context do not resolve with first element anymore. "first" prefix needed.
|
7
15
|
* Enable VirtualClass on native classes !
|
8
16
|
* Added 'activity' brick which records visitor's last "seen_at" date. <== TODO: Document
|
17
|
+
* Support for site alias (other domain pointing to same data) "rake mkalias". <== TODO: Document
|
9
18
|
|
10
19
|
* Minor changes
|
11
20
|
* Fixed encode_params in Ajax requests.
|
@@ -30,7 +30,7 @@ class NodesController < ApplicationController
|
|
30
30
|
layout :popup_layout, :only => [:edit, :import]
|
31
31
|
|
32
32
|
def index
|
33
|
-
if @node = secure(Node) { Node.find(current_site.
|
33
|
+
if @node = secure(Node) { Node.find(current_site.home_id) }
|
34
34
|
respond_to do |format|
|
35
35
|
format.html { render_and_cache :mode => '+index' }
|
36
36
|
format.xml { render :xml => @node.to_xml }
|
data/app/models/node.rb
CHANGED
@@ -1280,64 +1280,6 @@ class Node < ActiveRecord::Base
|
|
1280
1280
|
# Id to zip mapping for user_id. Used by zafu and forms.
|
1281
1281
|
def user_zip; self[:user_id]; end
|
1282
1282
|
|
1283
|
-
# transform to another class
|
1284
|
-
# def vclass=(new_class)
|
1285
|
-
# if new_class.kind_of?(String)
|
1286
|
-
# klass = Module.const_get(new_class)
|
1287
|
-
# else
|
1288
|
-
# klass = new_class
|
1289
|
-
# end
|
1290
|
-
# raise NameError if !klass.ancestors.include?(Node) || klass.version_class != self.class.content_class
|
1291
|
-
#
|
1292
|
-
#
|
1293
|
-
#
|
1294
|
-
# rescue NameError
|
1295
|
-
# errors.add('klass', 'invalid')
|
1296
|
-
# end
|
1297
|
-
|
1298
|
-
# transform an Node into another Object. This is a two step operation :
|
1299
|
-
# 1. create a new object with the attributes from the old one
|
1300
|
-
# 2. move old object out of the way (setting parent_id and section_id to -1)
|
1301
|
-
# 3. try to save new object
|
1302
|
-
# 4. delete old and set new object id to old
|
1303
|
-
# THIS IS DANGEROUS !! NEEDS TESTING
|
1304
|
-
# def change_to(klass)
|
1305
|
-
# return nil if self[:id] == current_site[:root_id]
|
1306
|
-
# # ==> Check for class specific information (file to remove, participations, tags, etc) ... should we leave these things and
|
1307
|
-
# # not care ?
|
1308
|
-
# # ==> When changing into something else : update version type and data !!!
|
1309
|
-
# my_id = self[:id].to_i
|
1310
|
-
# my_parent = self[:parent_id].to_i
|
1311
|
-
# my_project = self[:section_id].to_i
|
1312
|
-
# connection = self.class.connection
|
1313
|
-
# # 1. create a new object with the attributes from the old one
|
1314
|
-
# new_obj = secure!(klass) { klass.new(self.attributes) }
|
1315
|
-
# # 2. move old object out of the way (setting parent_id and section_id to -1)
|
1316
|
-
# self.class.connection.execute "UPDATE #{self.class.table_name} SET parent_id='0', section_id='0' WHERE id=#{my_id}"
|
1317
|
-
# # 3. try to save new object
|
1318
|
-
# if new_obj.save
|
1319
|
-
# tmp_id = new_obj[:id]
|
1320
|
-
# # 4. delete old and set new object id to old. Delete tmp Version.
|
1321
|
-
# self.class.connection.execute "DELETE FROM #{self.class.table_name} WHERE id=#{my_id}"
|
1322
|
-
# self.class.connection.execute "DELETE FROM #{Version.table_name} WHERE node_id=#{tmp_id}"
|
1323
|
-
# self.class.connection.execute "UPDATE #{self.class.table_name} SET id='#{my_id}' WHERE id=#{tmp_id}"
|
1324
|
-
# self.class.connection.execute "UPDATE #{self.class.table_name} SET section_id=id WHERE id=#{my_id}" if new_obj.kind_of?(Section)
|
1325
|
-
# self.class.logger.info "[#{self[:id]}] #{self.class} --> #{klass}"
|
1326
|
-
# if new_obj.kind_of?(Section)
|
1327
|
-
# # update section_id for children
|
1328
|
-
# sync_section(my_id)
|
1329
|
-
# elsif self.kind_of?(Section)
|
1330
|
-
# # update section_id for children
|
1331
|
-
# sync_section(parent[:section_id])
|
1332
|
-
# end
|
1333
|
-
# secure ( klass ) { klass.find(my_id) }
|
1334
|
-
# else
|
1335
|
-
# # set object back
|
1336
|
-
# self.class.connection.execute "UPDATE #{self.class.table_name} SET parent_id='#{my_parent}', section_id='#{my_project}' WHERE id=#{my_id}"
|
1337
|
-
# self
|
1338
|
-
# end
|
1339
|
-
# end
|
1340
|
-
|
1341
1283
|
# Find the discussion for the current context (v_status and v_lang). This automatically creates a new #Discussion if there is
|
1342
1284
|
# no closed or open discussion for the current lang and Node#can_auto_create_discussion? is true
|
1343
1285
|
def discussion
|
data/app/models/site.rb
CHANGED
@@ -6,8 +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
|
-
+
|
10
|
-
+
|
9
|
+
+root_id+:: Site seed node id. This is the only node in the site without a parent.
|
10
|
+
+home_id+:: This is the apparent root of the site (home page).
|
11
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.
|
12
12
|
+public_group_id+:: Id of the 'public' group. Every user of the site (with 'anonymous user') belongs to this group.
|
13
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.
|
@@ -57,12 +57,12 @@ class Site < ActiveRecord::Base
|
|
57
57
|
|
58
58
|
include RubyLess
|
59
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',
|
61
|
-
safe_method :
|
60
|
+
safe_method :root => Proc.new {|h, r, s| {:method => 'root_node', :class => current_site.root_node.vclass, :nil => true}}
|
61
|
+
safe_method :home => Proc.new {|h, r, s| {:method => 'home_node', :class => current_site.home_node.vclass, :nil => true}}
|
62
62
|
|
63
63
|
validate :valid_site
|
64
64
|
validates_uniqueness_of :host
|
65
|
-
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, :home_zip
|
66
66
|
has_many :groups, :order => "name"
|
67
67
|
has_many :nodes
|
68
68
|
has_many :users
|
@@ -181,8 +181,8 @@ class Site < ActiveRecord::Base
|
|
181
181
|
|
182
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)
|
183
183
|
|
184
|
+
site.home_id = root[:id]
|
184
185
|
site.root_id = root[:id]
|
185
|
-
site.orphan_id = root[:id]
|
186
186
|
|
187
187
|
# Make sure safe definitions on Time/Array/String are available on prop_eval validation.
|
188
188
|
Zena::Use::ZafuSafeDefinitions
|
@@ -228,14 +228,21 @@ class Site < ActiveRecord::Base
|
|
228
228
|
def find_by_host(host)
|
229
229
|
host = $1 if host =~ /^(.*)\.$/
|
230
230
|
if site = self.find(:first, :conditions => ['host = ?', host]) rescue nil
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
231
|
+
setup_master(site)
|
232
|
+
else
|
233
|
+
nil
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def setup_master(site)
|
238
|
+
if id = site.master_id
|
239
|
+
# The loaded site is an alias, load master site.
|
240
|
+
master = self.find(:first, :conditions => ['id = ?', id])
|
241
|
+
master.alias = site
|
242
|
+
master
|
243
|
+
else
|
244
|
+
site
|
237
245
|
end
|
238
|
-
site
|
239
246
|
end
|
240
247
|
|
241
248
|
# List of attributes that can be configured in the admin form
|
@@ -293,9 +300,9 @@ class Site < ActiveRecord::Base
|
|
293
300
|
@root ||= secure(Node) { Node.find(root_id) } || Node.new(:title => host)
|
294
301
|
end
|
295
302
|
|
296
|
-
# Return the
|
297
|
-
def
|
298
|
-
@
|
303
|
+
# Return the home node.
|
304
|
+
def home_node
|
305
|
+
@home ||= secure(Node) { Node.find(home_id) } || Node.new(:title => host)
|
299
306
|
end
|
300
307
|
|
301
308
|
# Return the public group: the one in which every visitor belongs.
|
@@ -383,19 +390,19 @@ class Site < ActiveRecord::Base
|
|
383
390
|
@alias && @alias[:authentication] || self[:authentication]
|
384
391
|
end
|
385
392
|
|
386
|
-
def
|
387
|
-
@
|
393
|
+
def home_id
|
394
|
+
@home_id ||= @alias && @alias[:home_id] || self[:home_id] || self[:root_id]
|
388
395
|
end
|
389
396
|
|
390
|
-
def
|
391
|
-
|
397
|
+
def home_zip
|
398
|
+
home_node.zip
|
392
399
|
end
|
393
400
|
|
394
|
-
def
|
401
|
+
def home_zip=(zip)
|
395
402
|
if id = secure(Node) { Node.translate_pseudo_id(zip) }
|
396
|
-
self[:
|
403
|
+
self[:home_id] = id
|
397
404
|
else
|
398
|
-
@
|
405
|
+
@home_zip_error = _('could not be found')
|
399
406
|
end
|
400
407
|
end
|
401
408
|
|
@@ -404,8 +411,8 @@ class Site < ActiveRecord::Base
|
|
404
411
|
ali = Site.new(self.attributes)
|
405
412
|
ali.host = hostname
|
406
413
|
ali.master_id = self.id
|
407
|
-
ali.orphan_id = self.orphan_id
|
408
414
|
ali.root_id = self.root_id
|
415
|
+
ali.home_id = self.home_id
|
409
416
|
ali.prop = self.prop
|
410
417
|
ali.save
|
411
418
|
ali
|
@@ -605,9 +612,9 @@ class Site < ActiveRecord::Base
|
|
605
612
|
self[:default_lang] = nil
|
606
613
|
end
|
607
614
|
|
608
|
-
if @
|
609
|
-
errors.add('root_id', @
|
610
|
-
@
|
615
|
+
if @home_zip_error
|
616
|
+
errors.add('root_id', @home_zip_error)
|
617
|
+
@home_zip_error = nil
|
611
618
|
end
|
612
619
|
end
|
613
620
|
|
data/app/models/user.rb
CHANGED
data/app/views/sites/_form.erb
CHANGED
@@ -14,7 +14,7 @@
|
|
14
14
|
<% end %>
|
15
15
|
<table cellspacing='0' class='edit_site'>
|
16
16
|
<tr><td class='label'><%= _('host') %></td><td><%= @site[:host] %></td></tr>
|
17
|
-
<tr><td class='label'><%= _('
|
17
|
+
<tr><td class='label'><%= _('home') %></td><td><%= text_field('site', :home_zip, :size=>15, :value => @site.home_zip) %></td></tr>
|
18
18
|
<% Site.attributes_for_form(@site.is_alias?)[:text].each do |name| -%>
|
19
19
|
<tr><td class='label'><%= _(name) %></td><td><%= text_field('site', name, :size=>nil) %></td></tr>
|
20
20
|
<% end -%>
|
data/app/views/sites/_li.erb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
:method => :get) %></td>
|
6
6
|
<td class="name"><%= li.host %></td>
|
7
7
|
<% if !li.master_id %>
|
8
|
-
<td class='
|
8
|
+
<td class='home'><%= li.home_node.title.limit(10) %></td>
|
9
9
|
<% [:languages, :default_lang].each do |sym| -%>
|
10
10
|
<td class="<%= sym %>"><%= li[sym] %></td>
|
11
11
|
<% end -%>
|
@@ -15,6 +15,6 @@
|
|
15
15
|
<td class='redit_time' ><%= li.redit_time %></td>
|
16
16
|
<td class='options'><%= Site.attributes_for_form(li.is_alias?)[:bool].reject{|sym| !li.send(sym)}.join(', ') %></td>
|
17
17
|
<% else %>
|
18
|
-
<td colspan='8' class='root'><%= li.
|
18
|
+
<td colspan='8' class='root'><%= li.home_node.title %></td>
|
19
19
|
<% end -%>
|
20
20
|
</tr>
|
@@ -15,12 +15,12 @@
|
|
15
15
|
</head>
|
16
16
|
|
17
17
|
<body do='void' name='body'>
|
18
|
-
<div id='top' do='
|
18
|
+
<div id='top' do='home' do='link'/>
|
19
19
|
<div id='header'>
|
20
20
|
<div id='login' do='login_link'/>
|
21
21
|
<div id='visitor'><r:visitor_link/> <r:if test='can_edit?' do='link' mode='admin' do='t'>btn_edit</r:if></div>
|
22
22
|
<div id='search' do='search_box' type='search'/>
|
23
|
-
<ol id='menu' do='
|
23
|
+
<ol id='menu' do='home' do='pages'>
|
24
24
|
<li do='each' on_if='is_ancestor?(main)' do='link'/>
|
25
25
|
</ol>
|
26
26
|
</div>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class RenameOrphanAndRootInSite < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
rename_column :sites, :root_id, :home_id
|
4
|
+
rename_column :sites, :orphan_id, :root_id
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.down
|
8
|
+
rename_column :sites, :root_id, :orphan_id
|
9
|
+
rename_column :sites, :home_id, :root_id
|
10
|
+
end
|
11
|
+
end
|
data/config/bricks.yml
CHANGED
@@ -54,7 +54,12 @@ development:
|
|
54
54
|
production:
|
55
55
|
# Port used only to provide assets during custom rendering (pdf).
|
56
56
|
# We need this to avoid deadlocks with round-robin based serving.
|
57
|
+
# Do not use with Passenger.
|
57
58
|
# asset_port: 7999
|
59
|
+
#
|
60
|
+
# When external rendering engines (pdf) cannot get non-public assets, add the server ip to
|
61
|
+
# what Zena considers 'local'
|
62
|
+
# local_ips: 123.45.67.89, 127.0.0.1
|
58
63
|
activity: ON
|
59
64
|
cache_path: '/public/cache'
|
60
65
|
mongrel: OFF
|
data/config/gems.yml
CHANGED
@@ -15,12 +15,12 @@
|
|
15
15
|
</head>
|
16
16
|
|
17
17
|
<body do='void' name='body'>
|
18
|
-
<div id='top' do='
|
18
|
+
<div id='top' do='home' do='link'/>
|
19
19
|
<div id='header'>
|
20
20
|
<div id='login' do='login_link'/>
|
21
21
|
<div id='visitor'><r:visitor_link/> <r:if test='can_edit?' do='link' mode='admin' do='t'>btn_edit</r:if></div>
|
22
22
|
<div id='search' do='search_box' type='search'/>
|
23
|
-
<ol id='menu' do='
|
23
|
+
<ol id='menu' do='home' do='pages'>
|
24
24
|
<li do='each' on_if='is_ancestor?(main)' do='link'/>
|
25
25
|
</ol>
|
26
26
|
</div>
|
data/lib/zena.rb
CHANGED
@@ -21,6 +21,8 @@ module Zena
|
|
21
21
|
end
|
22
22
|
|
23
23
|
ASSET_PORT = Bricks.raw_config['asset_port'].to_i
|
24
|
+
LOCAL_IPS = ((Bricks.raw_config['local_ips'] || '').split(',').map(&:strip) + %w{127.0.0.1 ::1}).uniq
|
25
|
+
|
24
26
|
|
25
27
|
ENABLE_LATEX = true && has_executable('pdflatex') # enable LateX post-rendering
|
26
28
|
ENABLE_FOP = true && has_executable('fop', 'xsltproc') # enable xsl-fo post-rendering
|
data/lib/zena/info.rb
CHANGED
data/lib/zena/use/ancestry.rb
CHANGED
@@ -10,7 +10,7 @@ module Zena
|
|
10
10
|
TITLE_ML_JOIN = %Q{INNER JOIN idx_nodes_ml_strings AS id1 ON id1.node_id = nodes.id AND id1.key = 'title'}
|
11
11
|
|
12
12
|
# (slow). Find a node by it's path. This is used during node importation when stored as zml files or to resolve custom_base url until we have an "alias" table.
|
13
|
-
def find_by_path(path, parent_id = current_site.
|
13
|
+
def find_by_path(path, parent_id = current_site.root_id, multilingual = false)
|
14
14
|
res = nil
|
15
15
|
path = path.split('/') unless path.kind_of?(Array)
|
16
16
|
last = path.size - 1
|
data/lib/zena/use/authlogic.rb
CHANGED
@@ -86,11 +86,11 @@ module Zena
|
|
86
86
|
User.find_by_single_access_token(user_token)
|
87
87
|
end
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
# Create a fake cookie based on HTTP_AUTH using session_id and render_token. This is
|
91
91
|
# only used for requests from localhost (asset host).
|
92
92
|
def forge_cookie_with_http_auth
|
93
|
-
if (request.headers['REMOTE_ADDR']
|
93
|
+
if Zena::LOCAL_IPS.include?(request.headers['REMOTE_ADDR']) &&
|
94
94
|
(Zena::ASSET_PORT.to_i == 0 || request.port.to_i == Zena::ASSET_PORT)
|
95
95
|
authenticate_with_http_basic do |login, password|
|
96
96
|
# login = visitor.id
|
data/lib/zena/use/query_node.rb
CHANGED
@@ -546,9 +546,15 @@ module Zena
|
|
546
546
|
when 'root'
|
547
547
|
# Special pseudo-context
|
548
548
|
add_table(main_table)
|
549
|
-
set_main_class(
|
549
|
+
set_main_class(current_site.root_node.vclass)
|
550
550
|
add_filter "#{table}.id = #{current_site.root_id}"
|
551
551
|
return true
|
552
|
+
when 'home'
|
553
|
+
# Special pseudo-context
|
554
|
+
add_table(main_table)
|
555
|
+
set_main_class(current_site.home_node.vclass)
|
556
|
+
add_filter "#{table}.id = #{current_site.home_id}"
|
557
|
+
return true
|
552
558
|
#when 'author', 'traductions', 'versions'
|
553
559
|
# # TODO: not implemented yet...
|
554
560
|
# return nil
|
data/lib/zena/use/test_helper.rb
CHANGED
@@ -7,11 +7,17 @@ module Zena
|
|
7
7
|
include Zena::Use::Upload::UploadedFile
|
8
8
|
|
9
9
|
# Set visitor for unit testing
|
10
|
-
def login(fixture)
|
10
|
+
def login(fixture, site_name = nil)
|
11
11
|
user = users(fixture)
|
12
|
-
|
12
|
+
if site_name
|
13
|
+
site = Site.setup_master(Site.find_by_name(site_name))
|
14
|
+
else
|
15
|
+
# Not an alias
|
16
|
+
site = user.site
|
17
|
+
end
|
18
|
+
setup_visitor(user, site)
|
13
19
|
user.ip = '10.0.0.44'
|
14
|
-
$_test_site =
|
20
|
+
$_test_site = site.name
|
15
21
|
::I18n.locale = user.lang
|
16
22
|
end
|
17
23
|
|