zena 1.2.5 → 1.2.6
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.
- 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
|
|