zena 1.0.0.beta2 → 1.0.0.beta3

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 (112) hide show
  1. data/.gitignore +2 -0
  2. data/History.txt +12 -0
  3. data/app/controllers/application_controller.rb +0 -1
  4. data/app/controllers/columns_controller.rb +11 -1
  5. data/app/controllers/nodes_controller.rb +79 -19
  6. data/app/controllers/versions_controller.rb +0 -2
  7. data/app/controllers/virtual_classes_controller.rb +19 -6
  8. data/app/models/column.rb +5 -1
  9. data/app/models/comment.rb +1 -6
  10. data/app/models/node.rb +21 -3
  11. data/app/models/role.rb +21 -0
  12. data/app/models/site.rb +7 -2
  13. data/app/models/template.rb +3 -3
  14. data/app/models/text_document.rb +4 -4
  15. data/app/models/user.rb +21 -8
  16. data/app/views/columns/_li.html.erb +1 -0
  17. data/app/views/nodes/_groups.rhtml +1 -1
  18. data/app/views/sites/_form.erb +3 -1
  19. data/app/views/sites/_li.erb +1 -0
  20. data/app/views/sites/index.erb +1 -1
  21. data/app/views/virtual_classes/_form.erb +11 -2
  22. data/app/views/virtual_classes/_li.erb +5 -2
  23. data/bin/zena +1 -1
  24. data/bricks/math/lib/bricks/math.rb +1 -1
  25. data/bricks/mongrel/README +3 -0
  26. data/bricks/mongrel/zena/deploy.rb +56 -0
  27. data/bricks/passenger/README +3 -0
  28. data/bricks/passenger/zena/deploy.rb +49 -0
  29. data/config/bricks.yml +6 -0
  30. data/config/deploy.rb +24 -18
  31. data/config/gems.yml +3 -3
  32. data/db/migrate/20100915062903_add_api_group_id_to_site.rb +9 -0
  33. data/lib/tasks/zena.rake +39 -35
  34. data/lib/zena.rb +5 -6
  35. data/lib/zena/acts/enrollable.rb +37 -6
  36. data/lib/zena/app.rb +4 -2
  37. data/lib/zena/deploy.rb +110 -150
  38. data/lib/zena/deploy/awstats.conf.rhtml +4 -4
  39. data/lib/zena/deploy/httpd.rhtml +2 -1
  40. data/lib/zena/deploy/stats.vhost.rhtml +7 -7
  41. data/lib/zena/deploy/vhost.rhtml +1 -1
  42. data/lib/zena/deploy/vhost_www.rhtml +4 -4
  43. data/lib/zena/foxy_parser.rb +6 -5
  44. data/lib/zena/info.rb +1 -1
  45. data/lib/zena/integration/test_case.rb +8 -3
  46. data/lib/zena/parser.rb +11 -11
  47. data/lib/zena/parser/zafu_tags.rb +2 -2
  48. data/lib/zena/remote.rb +16 -0
  49. data/lib/zena/remote/connection.rb +67 -0
  50. data/lib/zena/remote/interface.rb +405 -0
  51. data/lib/zena/remote/klass.rb +14 -0
  52. data/lib/zena/remote/mock.rb +58 -0
  53. data/lib/zena/remote/node.rb +76 -0
  54. data/lib/zena/routes.rb +2 -1
  55. data/lib/zena/use.rb +9 -4
  56. data/lib/zena/use/ajax.rb +3 -3
  57. data/lib/zena/use/authlogic.rb +8 -1
  58. data/lib/zena/use/context.rb +22 -21
  59. data/lib/zena/use/dates.rb +26 -3
  60. data/lib/zena/use/display.rb +33 -5
  61. data/lib/zena/use/forms.rb +90 -12
  62. data/lib/zena/use/fulltext.rb +1 -1
  63. data/lib/zena/use/i18n.rb +118 -31
  64. data/lib/zena/use/query_builder.rb +7 -5
  65. data/lib/zena/use/query_node.rb +30 -4
  66. data/lib/zena/use/rendering.rb +1 -1
  67. data/lib/zena/use/search.rb +10 -7
  68. data/lib/zena/use/urls.rb +3 -3
  69. data/lib/zena/use/zafu_attributes.rb +2 -2
  70. data/lib/zena/use/zafu_eval.rb +1 -1
  71. data/lib/zena/use/zafu_safe_definitions.rb +1 -0
  72. data/lib/zena/use/zafu_templates.rb +1 -1
  73. data/lib/zena/zafu_compiler.rb +5 -1
  74. data/public/javascripts/zena.js +4 -4
  75. data/public/stylesheets/admin.css +1 -0
  76. data/test/custom_queries/complex.host.yml +3 -3
  77. data/test/fixtures/files/translations_fr.yml +4 -1
  78. data/test/functional/application_controller_test.rb +2 -2
  79. data/test/functional/nodes_controller_test.rb +57 -5
  80. data/test/functional/users_controller_test.rb +10 -9
  81. data/test/functional/virtual_classes_controller_test.rb +48 -0
  82. data/test/integration/navigation_test.rb +13 -1
  83. data/test/integration/query_node/filters.yml +5 -0
  84. data/test/integration/query_node_test.rb +1 -1
  85. data/test/integration/zafu_compiler/ajax.yml +13 -19
  86. data/test/integration/zafu_compiler/basic.yml +0 -72
  87. data/test/integration/zafu_compiler/complex.yml +1 -1
  88. data/test/integration/zafu_compiler/complex_ok.yml +19 -0
  89. data/test/integration/zafu_compiler/dates.yml +62 -1
  90. data/test/integration/zafu_compiler/display.yml +4 -4
  91. data/test/integration/zafu_compiler/forms.yml +19 -7
  92. data/test/integration/zafu_compiler/i18n.yml +56 -1
  93. data/test/integration/zafu_compiler/later.yml +23 -1
  94. data/test/integration/zafu_compiler/relations.yml +1 -1
  95. data/test/integration/zafu_compiler/roles.yml +29 -1
  96. data/test/integration/zafu_compiler/safe_definitions.yml +1 -1
  97. data/test/integration/zafu_compiler/zafu_attributes.yml +2 -1
  98. data/test/integration/zafu_compiler_test.rb +5 -3
  99. data/test/sites/zena/columns.yml +3 -0
  100. data/test/sites/zena/roles.yml +0 -1
  101. data/test/sites/zena/sites.yml +1 -0
  102. data/test/sites/zena/versions.yml +2 -0
  103. data/test/unit/node_test.rb +27 -9
  104. data/test/unit/relation_proxy_test.rb +7 -4
  105. data/test/unit/remote_test.rb +379 -0
  106. data/test/unit/user_test.rb +47 -0
  107. data/test/unit/zena/acts/enrollable_test.rb +36 -7
  108. data/test/unit/zena/acts/serializable_test.rb +14 -2
  109. data/test/unit/zena/use/i18n_test.rb +32 -5
  110. data/test/unit/zena/use/query_node_test.rb +13 -1
  111. data/zena.gemspec +25 -11
  112. metadata +24 -10
data/.gitignore CHANGED
@@ -1,4 +1,6 @@
1
1
  .DS_Store
2
+ *.swp
3
+ *.gem
2
4
  restart.txt
3
5
  config/deploy_config.rb
4
6
  config/database.yml
data/History.txt CHANGED
@@ -1,3 +1,15 @@
1
+ == 1.0.0.beta3 2010-09-17
2
+
3
+ * major changes
4
+ * created Zena::Remote API
5
+ * fixed Ajax operations and tests
6
+ * fixed forms to create/update new objects
7
+ * added dynamic translations
8
+ * better querybuilder integration relative to SELECT clauses
9
+ * fixed date parsing
10
+ * added safe 'send' method
11
+ * added support to create and update Roles
12
+
1
13
  == 1.0.0.beta2 2010-07-24
2
14
 
3
15
  * major changes
@@ -3,5 +3,4 @@ class ApplicationController < ActionController::Base
3
3
 
4
4
  include Zena::App
5
5
 
6
- include Zafu::ControllerMethods
7
6
  end
@@ -7,8 +7,18 @@ class ColumnsController < ApplicationController
7
7
  # GET /columns
8
8
  # GET /columns.xml
9
9
  def index
10
+ roles = {}
10
11
  secure(Column) do
11
- @columns = Column.paginate(:all, :order => 'name', :per_page => 20, :page => params[:page])
12
+ @columns = Column.paginate(:all, :order => 'role_id ASC, name ASC', :per_page => 20, :page => params[:page]).sort! do |a, b|
13
+ role_a = (roles[a.role_id] ||= a.role).name
14
+ role_b = (roles[b.role_id] ||= b.role).name
15
+
16
+ if role_a == role_b
17
+ a.name <=> b.name
18
+ else
19
+ role_a <=> role_b
20
+ end
21
+ end
12
22
  end
13
23
 
14
24
  @column = Column.new
@@ -3,22 +3,23 @@
3
3
 
4
4
  basepath class and zip optional mode format
5
5
 
6
- /projects/art/ project24 -print .html
6
+ /projects/art/ project24 _print .html
7
7
 
8
8
  Examples:
9
9
  /current/art/project24.html << a project inside the 'art' page
10
10
  /note24.html << a Note's page
11
11
  /note24_print.html << a Note in 'print' mode
12
- /current/art.html << 'art' page (this page has custom base set = no class or zip shown)
13
- /current/art-print.html << 'art' page in 'print' mode
12
+ /current/art.html << 'art' page (this page has custom base set, this means no class or zip shown)
13
+ /current/art_print.html << 'art' page in 'print' mode
14
14
  /current/art/project24/image28.html << image page (for comments, etc)
15
- /current/art/project24/image28.jpg << full image
16
- /current/art/project24/image28_pv.jpg << image in the 'pv' format
17
- /current/art/project24/image28_pv.html << image page in 'print' mode
15
+ /current/art/project24/image28.jpg << full image data
16
+ /current/art/project24/image28_pv.jpg << image in the 'pv' image format
17
+ /current/art/project24/image28_print.html << image page in 'print' mode
18
18
 
19
19
  =end
20
20
  class NodesController < ApplicationController
21
21
  before_filter :check_is_admin, :only => [:export]
22
+ before_filter :check_api_group
22
23
  before_filter :find_node, :except => [:index, :create, :not_found, :catch_all, :search]
23
24
  before_filter :check_can_drive, :only => [:edit]
24
25
  before_filter :check_path, :only => [:index, :show]
@@ -50,15 +51,46 @@ class NodesController < ApplicationController
50
51
  raise ActiveRecord::RecordNotFound
51
52
  end
52
53
 
54
+ # Find nodes starting from root node
53
55
  def search
54
- do_search
55
56
  respond_to do |format|
56
- format.html { render_and_cache :mode => '+search', :cache => false }
57
- format.xml { render :xml => @nodes ? @nodes.to_xml : []}
58
- format.js
57
+ format.html do
58
+ begin
59
+ do_search
60
+ rescue QueryBuilder::Error => err
61
+ flash[:error] = err.message
62
+ end
63
+ render_and_cache :mode => '+search', :cache => false
64
+ end
65
+
66
+ format.xml do
67
+ begin
68
+ do_search
69
+ if @nodes.kind_of?(Fixnum)
70
+ # count
71
+ render :xml => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<count type=\"integer\">#{@nodes}</count>\n"
72
+ elsif @nodes
73
+ render :xml => Array(@nodes).to_xml(:root => 'nodes')
74
+ else
75
+ render :xml => [].to_xml(:root => 'nodes')
76
+ end
77
+ rescue QueryBuilder::Error => err
78
+ render :xml => [{:message => err.message}].to_xml(:root => 'errors'), :status => 401
79
+ end
80
+ end
81
+
82
+ format.js do
83
+ do_search
84
+ render :action => 'search'
85
+ end
59
86
  end
60
87
  end
61
88
 
89
+ # Find nodes starting from a given node
90
+ def find
91
+ search
92
+ end
93
+
62
94
  # this should not be needed.... but format.js never gets called otherwize.
63
95
  def asearch
64
96
  do_search
@@ -180,7 +212,7 @@ class NodesController < ApplicationController
180
212
  flash[:notice] = 'Node was successfully created.'
181
213
  format.html { redirect_to zen_path(@node) }
182
214
  format.js
183
- format.xml { render :xml => @node, :status => :created, :location => node_url(@node) }
215
+ format.xml { render :xml => @node.to_xml(:root => 'node'), :status => :created, :location => node_url(@node) }
184
216
  else
185
217
  format.html { render :action => "new" }
186
218
  format.js
@@ -312,13 +344,19 @@ class NodesController < ApplicationController
312
344
  end
313
345
  end
314
346
  end # parent iframe (upload)
315
- elsif @node.errors.empty?
347
+ else
316
348
  respond_to do |format|
317
349
  format.html do
318
- if params[:edit] == 'popup'
319
- redirect_to edit_node_version_path(:node_id => @node[:zip], :id => 0, :close => (params[:validate] ? true : nil))
350
+ if @node.errors.empty?
351
+ if params[:edit] == 'popup'
352
+ redirect_to edit_node_version_path(:node_id => @node[:zip], :id => 0, :close => (params[:validate] ? true : nil))
353
+ else
354
+ redirect_to zen_path(@node, :mode => params[:mode])
355
+ end
320
356
  else
321
- redirect_to zen_path(@node, :mode => params[:mode])
357
+ # FIXME: Render referring page would be better
358
+ # Get mode and details from request.referer
359
+ render_and_cache :mode => 'edit', :cache => false
322
360
  end
323
361
  end # html
324
362
 
@@ -564,6 +602,13 @@ class NodesController < ApplicationController
564
602
  end
565
603
 
566
604
  def do_search
605
+ if @node
606
+ default_scope = 'self'
607
+ else
608
+ @node = current_site.root_node
609
+ default_scope = 'site'
610
+ end
611
+
567
612
  unless query_params = params[:q]
568
613
  query_params = params.dup
569
614
  %w{controller action format}.each do |key|
@@ -571,10 +616,18 @@ class NodesController < ApplicationController
571
616
  end
572
617
  end
573
618
 
574
- @node = current_site.root_node
575
- @search_per_page = params[:per_page] ? params[:per_page].to_i : 20
576
- @nodes = secure(Node) { Node.search_records(query_params, :node => @node, :page => params[:page], :per_page => @search_per_page) }
577
- @search_count = 100 # FIXME: @nodes ? @nodes.total_entries : 0
619
+ if request.format != Mime::XML || params[:page] || params[:per_page]
620
+ @search_per_page = params[:per_page] ? params[:per_page].to_i : 20
621
+ @nodes = secure(Node) { Node.search_records(query_params, :node => @node, :default => {:scope => default_scope}, :page => params[:page], :per_page => @search_per_page) }
622
+ @search_count = 100 # FIXME: @nodes ? @nodes.total_entries : 0
623
+ else
624
+ # XML without pagination
625
+ @nodes = secure(Node) { Node.search_records(query_params, :node => @node, :default => {:scope => default_scope}) }
626
+ end
627
+
628
+ if @nodes.kind_of?(Node)
629
+ @nodes = [@nodes]
630
+ end
578
631
  end
579
632
 
580
633
  # Document data do not change session[:lang] and can point at cached content (no nee to redirect to AUTHENTICATED_PREFIX).
@@ -608,5 +661,12 @@ class NodesController < ApplicationController
608
661
  def check_can_drive
609
662
  raise ActiveRecord::RecordNotFound unless @node.can_drive?
610
663
  end
664
+
665
+ def check_api_group
666
+ return true if request.format != Mime::XML || visitor.api_authorized?
667
+
668
+ render :xml => [{:message => 'Not in API group.'}].to_xml(:root => 'errors'), :status => 401
669
+ false
670
+ end
611
671
  end
612
672
 
@@ -111,8 +111,6 @@ class VersionsController < ApplicationController
111
111
  source[k] || '').format_as(:html).gsub(/(\s+)<\/del>/, '</del>\1')
112
112
  end
113
113
 
114
- puts target.inspect
115
-
116
114
  show
117
115
  end
118
116
 
@@ -6,7 +6,7 @@ class VirtualClassesController < ApplicationController
6
6
 
7
7
  def index
8
8
  secure(VirtualClass) do
9
- @virtual_classes = VirtualClass.paginate(:all, :order => 'kpath', :per_page => 20, :page => params[:page])
9
+ @virtual_classes = Role.paginate(:all, :order => 'kpath', :per_page => 20, :page => params[:page])
10
10
  end
11
11
 
12
12
  last_kpath = @virtual_classes.last.kpath
@@ -16,7 +16,17 @@ class VirtualClassesController < ApplicationController
16
16
  end
17
17
  end
18
18
 
19
- @virtual_classes.sort! {|a, b| a.kpath <=> b.kpath }
19
+ @virtual_classes.sort! do |a, b|
20
+ if a.kpath == b.kpath
21
+ # Order VirtualClass first
22
+ b_type = b.kind_of?(Role) ? b.class.to_s : 'V' # sort real classes like VirtualClass
23
+ a_type = a.kind_of?(Role) ? a.class.to_s : 'V'
24
+
25
+ b_type <=> a_type
26
+ else
27
+ a.kpath <=> b.kpath
28
+ end
29
+ end
20
30
 
21
31
  @virtual_class = VirtualClass.new('')
22
32
 
@@ -80,7 +90,12 @@ class VirtualClassesController < ApplicationController
80
90
  end
81
91
 
82
92
  def create
83
- @virtual_class = VirtualClass.new(params[:virtual_class])
93
+ type = params[:virtual_class].delete(:type)
94
+ if type == 'Role'
95
+ @virtual_class = Role.new(params[:virtual_class])
96
+ else
97
+ @virtual_class = VirtualClass.new(params[:virtual_class])
98
+ end
84
99
 
85
100
  respond_to do |format|
86
101
  if @virtual_class.save
@@ -97,8 +112,6 @@ class VirtualClassesController < ApplicationController
97
112
  end
98
113
 
99
114
  def update
100
- @virtual_class = VirtualClass.find(params[:id])
101
-
102
115
  respond_to do |format|
103
116
  if @virtual_class.update_attributes(params[:virtual_class])
104
117
  flash[:notice] = 'VirtualClass was successfully updated.'
@@ -125,6 +138,6 @@ class VirtualClassesController < ApplicationController
125
138
 
126
139
  protected
127
140
  def find_virtual_class
128
- @virtual_class = secure!(VirtualClass) { VirtualClass.find(params[:id])}
141
+ @virtual_class = secure!(VirtualClass) { Role.find(params[:id])}
129
142
  end
130
143
  end
data/app/models/column.rb CHANGED
@@ -12,11 +12,15 @@ class Column < ActiveRecord::Base
12
12
  validates_uniqueness_of :name, :scope => :site_id
13
13
  validate :name_not_in_models
14
14
 
15
+ safe_method :name => String
16
+
15
17
  class << self
16
18
  include Zena::Acts::Secure
17
19
 
18
20
  def roles_for_form
19
- secure(Role) { Role.all(:order => 'kpath ASC') }.map {|r| [r.name, r.id]}
21
+ secure(Role) { Role.all(:order => 'name ASC') }.map do |role|
22
+ [role.name, role.id]
23
+ end
20
24
  end
21
25
 
22
26
  # Import a hash of virtual class definitions and try to build the virtual classes.
@@ -10,7 +10,7 @@ class Comment < ActiveRecord::Base
10
10
 
11
11
  safe_attribute :title, :created_at, :updated_at, :status
12
12
  safe_method :text => String, :author_name => {:class => String, :nil => true},
13
- :discussion_zip => Number
13
+ :discussion_id => Number
14
14
 
15
15
  safe_context :replies => ['Comment'], :node => 'Node'
16
16
 
@@ -64,11 +64,6 @@ class Comment < ActiveRecord::Base
64
64
  self[:id]
65
65
  end
66
66
 
67
- # needed by zafu to group
68
- def discussion_zip
69
- self[:discussion_id]
70
- end
71
-
72
67
  # TODO: test
73
68
  def can_write?
74
69
  is_author? && discussion.open?
data/app/models/node.rb CHANGED
@@ -162,7 +162,6 @@ class Node < ActiveRecord::Base
162
162
  :score => Number, :comments_count => Number,
163
163
  :custom_a => Number, :custom_b => Number
164
164
 
165
- # FIXME: remove 'zip' and use :id => {:class => Number, :method => 'zip'}
166
165
  # same with parent_zip, section_zip, etc...
167
166
 
168
167
 
@@ -808,7 +807,7 @@ class Node < ActiveRecord::Base
808
807
  transform_attributes(attributes, base_node)
809
808
  end
810
809
 
811
- def safe_method_type(signature)
810
+ def safe_method_type(signature, receiver = nil)
812
811
  if signature.size > 1
813
812
  RubyLess::SafeClass.safe_method_type_for(self, signature)
814
813
  else
@@ -818,6 +817,13 @@ class Node < ActiveRecord::Base
818
817
  elsif method =~ /^(.+)_((id|zip|status|comment)(s?))\Z/ && !instance_methods.include?(method)
819
818
  key = $3 == 'id' ? "zip#{$4}" : $2
820
819
  {:method => "rel[#{$1.inspect}].try(:other_#{key})", :nil => true, :class => ($4.blank? ? Number : [Number])}
820
+ elsif receiver && query = receiver.opts[:query]
821
+ # Resolve by using information in the SELECT part of the query that found this node
822
+ if query.select_keys.include?(method)
823
+ {:class => String, :method => "attributes[#{method.inspect}]", :nil => true}
824
+ else
825
+ nil
826
+ end
821
827
  else
822
828
  nil
823
829
  end
@@ -1395,6 +1401,19 @@ class Node < ActiveRecord::Base
1395
1401
  secure(Node) { Node.find_node_by_pseudo(string, base_node || self) }
1396
1402
  end
1397
1403
 
1404
+ safe_method [:send, String] => {:class => String, :nil => true, :method => 'safe_send'}
1405
+
1406
+ # Safe dynamic method dispatching when the method is not known during compile time. Currently this
1407
+ # only works for methods without arguments.
1408
+ def safe_send(method)
1409
+ # We need to load roles or all properties will be ignored
1410
+ load_roles!
1411
+
1412
+ return nil unless type = safe_method_type([method])
1413
+ res = eval(type[:method])
1414
+ res ? res.to_s : nil
1415
+ end
1416
+
1398
1417
  protected
1399
1418
 
1400
1419
  # after node is saved, make sure it's children have the correct section set
@@ -1721,7 +1740,6 @@ class Node < ActiveRecord::Base
1721
1740
  end
1722
1741
  end
1723
1742
  end
1724
-
1725
1743
  end
1726
1744
 
1727
1745
  Bricks.apply_patches
data/app/models/role.rb CHANGED
@@ -8,6 +8,27 @@ class Role < ActiveRecord::Base
8
8
  before_validation :set_defaults
9
9
  validate :check_can_save
10
10
 
11
+ include RubyLess
12
+ safe_method :columns => {:class => ['Column'], :method => 'columns.values', :nil => false}
13
+ safe_method :name => String
14
+
15
+ def superclass
16
+ if new_record?
17
+ Node
18
+ else
19
+ Node.get_class_from_kpath(kpath)
20
+ end
21
+ end
22
+
23
+ def superclass=(klass)
24
+ if k = Node.get_class(klass)
25
+ self.kpath = k.kpath
26
+ else
27
+ errors.add('superclass', 'invalid')
28
+ end
29
+ end
30
+
31
+
11
32
  private
12
33
  def set_defaults
13
34
  self.site_id = visitor.site.id
data/app/models/site.rb CHANGED
@@ -19,11 +19,11 @@ The #Site model holds configuration information for a site:
19
19
  class Site < ActiveRecord::Base
20
20
  include RubyLess
21
21
  safe_method :host => String, :lang_list => [String]
22
- safe_context :root => {:class => Node, :method => 'root_node'}
22
+ safe_context :root => {:class => 'Node', :method => 'root_node'}
23
23
 
24
24
  validate :valid_site
25
25
  validates_uniqueness_of :host
26
- attr_accessible :name, :languages, :default_lang, :authentication, :http_auth, :auto_publish, :redit_time
26
+ attr_accessible :name, :languages, :default_lang, :authentication, :http_auth, :auto_publish, :redit_time, :api_group_id
27
27
  has_many :groups, :order => "name"
28
28
  has_many :nodes
29
29
  has_many :users
@@ -229,6 +229,11 @@ class Site < ActiveRecord::Base
229
229
  @site_group ||= Group.find(self[:site_group_id])
230
230
  end
231
231
 
232
+ # Return the API group: the one in which API visitors must be to use the API.
233
+ def api_group
234
+ @api_group ||= Group.find_by_id(self[:api_group_id])
235
+ end
236
+
232
237
  # Return true if the given user is an administrator for this site.
233
238
  def is_admin?(user)
234
239
  admin_user_ids.include?(user[:id])
@@ -93,14 +93,14 @@ class Template < TextDocument
93
93
  self.text = <<END_TXT
94
94
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
95
95
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
96
- <html xmlns="http://www.w3.org/1999/xhtml" do='void' lang="en" set_lang='[v_lang]' xml:lang='en'>
96
+ <html xmlns='http://www.w3.org/1999/xhtml' do='void' lang='\#{v.lang}' xml:lang='en'>
97
97
  <head do='void' name='head'>
98
98
  <title do='title_for_layout' do='show' attr='title' name='page_title'>page title</title>
99
99
  <!-- link href='favicon.png' rel='shortcut icon' type='image/png' / -->
100
- <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
100
+ <meta http-equiv='Content-type' content='text/html; charset=utf-8' />
101
101
  <r:void name='stylesheets'>
102
102
  <r:stylesheets/>
103
- <link href="style.css" rel="Stylesheet" type="text/css"/>
103
+ <link href='style.css' rel='Stylesheet' type='text/css'/>
104
104
  </r:void>
105
105
 
106
106
  <r:javascripts/>