Changeset 1061

Show
Ignore:
Timestamp:
2008-06-15 00:51:32 (7 months ago)
Author:
gaspard
Message:

Better 'auto_create_discussion' rule that avoids having many objects with open comments on a single clic.
The rule has been changed to "create a new discussion if 'auto_create_discussion' is on" during object
creation instead of doing this on the first comment. To block comments: simply delete the discussion. Refs #99(1.5).
Fixed a bug where a [form] deep inside an [each] would get used. Added visitor clause to tests: <r:if visitor='anon'/>.
Added 'alt' class in [each] to produce stripes.
Changed comments so that titles can be left blank. Refs #99.
Fixed a lot of stuff related to comments (publish, remove).
Added striping (alternate rows) possibility in [each] through 'alt_class'.
Added 'has discussion' clause to [if]
Fixed bug with data_entries list (template compile crash)
Added sites dynamic attributes (enables custom settings for plugins).
Added new 'patch' system to ease plugins creation (drop inside 'vendor/plugins' without having to write any code in zena).
Added new plugin 'zena_captcha' to avoid comment spam (uses recaptcha). Closes #99(2).
Changed 'zena_captcha' plugin to only display captcha for anonymous user.
Fixed bugs in zena_captcha plugin.

Added theme and lang support to zena_captcha.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/README

    r1045 r1061  
    9999rake mongrel mongrel_cluster rmagick tzinfo RedCloth syntax gettext mongrel_upload_progress uuidtools daemons ruby-debug 
    100100 
     101gem install --source http://www.loonsoft.com/recaptcha/pkg/ recaptcha 
     102 
    101103=== Advised tools 
    102104monit (debian package to monitor your mongrel processes) 
  • trunk/app/controllers/application.rb

    r1050 r1061  
    734734    def error_messages_for(obj, opts={}) 
    735735      return '' if obj.errors.empty? 
    736       res = ["<ul class='#{opts[:class] || 'errors'}'>"] 
     736      res = ["<table class='#{opts[:class] || 'errors'}'>"] 
    737737      obj.errors.each do |er,msg| 
    738         res << "<li><b>#{er}</b> #{_(msg)}</li>" 
    739       end 
    740       res << '</ul>' 
     738        res << "<tr><td><b>#{er}</b></td><td>#{_(msg)}</td></tr>" 
     739      end 
     740      res << '</table>' 
    741741      res.join("\n") 
    742742    end 
     
    815815      adate.strftime(format) 
    816816    end 
     817     
     818    # Read the parameters and add errors to the object if it is considered spam. Save it otherwize. 
     819    def save_if_not_spam(obj, params) 
     820      # do nothing (overwritten by plugins like zena_captcha) 
     821      obj.save 
     822    end 
     823end 
    817824 
    818 end 
     825load_patches_from_plugins 
  • trunk/app/controllers/comments_controller.rb

    r1060 r1061  
    11# FIXME: rewrite ! 
    22class CommentsController < ApplicationController 
    3   before_filter :find_comment, :except => [:create, :index
     3  before_filter :find_comment, :except => [:create, :index, :empty_bin
    44  before_filter :find_node_and_discussion, :only => [:create] 
    55  before_filter :check_is_admin, :only=>[:index, :empty_bin] 
    6   helper_method :bin_content 
     6  helper_method :bin_content, :bin_content_size 
     7  layout :admin_layout 
    78   
    89  # TODO: test 
     
    2223    @discussion.save if @discussion.new_record? && @node.can_comment? 
    2324     
    24     @comment = secure!(Comment) { Comment.create(filter_attributes(params[:comment])) } 
     25    @comment = secure!(Comment) { Comment.new(filter_attributes(params[:comment])) } 
     26     
     27    save_if_not_spam(@comment, params) 
    2528     
    2629    respond_to do |format| 
     
    6669    @node = secure(Node) { Node.find(@discussion[:node_id]) } 
    6770    if @node && visitor.is_admin? || @node.can_drive? 
     71      Node.logger.error "\n\nremoving...\n\n" 
    6872      @comment.remove 
    6973    else 
     
    8892  def index 
    8993    @node = visitor.contact 
    90     @comment_pages, @comments = 
    91           paginate :comments, :order => 'status ASC, created_at DESC', :conditions=>"status > #{Zena::Status[:rem]}", :per_page => 20 
    92     render :layout=>admin_layout 
     94    secure!(Node) do 
     95      @comment_pages, @comments = paginate :comments,  
     96                        :select => "comments.*", 
     97                        :order => 'status ASC, comments.created_at DESC', 
     98                        :join  => 'INNER JOIN (discussions, nodes) on nodes.id = discussions.node_id and comments.discussion_id = discussions.id', 
     99                        :conditions=>"status > #{Zena::Status[:rem]} AND (#{secure_scope('nodes')})",  
     100                        :per_page => 20 
     101    end 
    93102  end 
    94103   
     
    115124     
    116125    def bin_content 
    117       @bin_content ||= Comment.find(:all, :conditions=>['status <= ?', Zena::Status[:rem]]) 
     126      @bin_content ||= secure(Comment) { Comment.find(:all, :conditions=>['status <= ?', Zena::Status[:rem]]) } 
     127    end 
     128     
     129    def bin_content_size 
     130      secure(Comment)  { Comment.count(:all, :conditions=>['status <= ?', Zena::Status[:rem]]) } 
    118131    end 
    119132     
  • trunk/app/helpers/application_helper.rb

    r1057 r1061  
    12801280  end 
    12811281end 
    1282 =begin 
    1283  
    1284 # Methods added to this helper will be available to all templates in the application. 
    1285 module ApplicationHelper 
    1286   include Zena::Acts::SecureScope 
    1287   include Zena::Acts::Secure::InstanceMethods 
    1288   include ZenaGlobals 
    1289   uses_strips :base, :admin, :calendar 
    1290    
    1291   # Overwrite error_messages_for to include translation 
    1292   def error_messages_for(object_name, options = {}) 
    1293     options = options.symbolize_keys 
    1294     object = instance_variable_get("@#{object_name}") 
    1295     if object && !object.errors.empty? 
    1296       content_tag("div", 
    1297       content_tag("ul", object.errors.full_messages.collect { |msg| content_tag("li", t(msg)) }), 
    1298       "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation" 
    1299       ) 
    1300     else 
    1301       "" 
    1302     end 
    1303   end 
    1304    
    1305    
    1306    
    1307 end 
    1308  
    1309 =end 
     1282load_patches_from_plugins 
  • trunk/app/models/comment.rb

    r1060 r1061  
    8787        errors.add('base', 'you cannot comment here') unless visitor.commentator? && discussion && discussion.open? 
    8888      else 
    89         if !is_author? 
     89        if discussion.node.can_drive? 
     90          # OK 
     91          # can edit/delete comments 
     92          # TODO: should be restricted to 'delete' or 'erase text'... 
     93        elsif is_author? 
     94          errors.add('base', 'discussion closed, comment cannot be updated') if !can_write? 
     95        else 
    9096          errors.add('base', 'you do not have the rights to do this') 
    91         else 
    92           errors.add('base', 'discussion closed, comment cannot be updated') if !can_write? 
    9397        end 
    9498      end 
    95       errors.add('text', "can't be blank") unless self[:text] && self[:text] != '' 
    96       errors.add('title', "can't be blank") unless self[:title] && self[:title] != '' 
     99      errors.add('text', "can't be blank") if self[:text].blank? 
    97100      errors.add('discussion', 'invalid') unless discussion 
    98101      errors.add('ip', "can't be blank") unless self[:ip] || !visitor.is_anon? 
  • trunk/app/models/node.rb

    r1060 r1061  
    151151  before_create      :node_before_create 
    152152  after_save         :spread_project_and_section 
     153  after_create       :node_after_create 
    153154  attr_protected     :site_id, :zip, :id, :section_id, :project_id, :publish_from, :max_status 
    154155  attr_protected     :c_version_id, :c_node_id # TODO: test 
     
    10361037 
    10371038  # Find the discussion for the current context (v_status and v_lang). This automatically creates a new #Discussion if there is 
    1038   # no closed or open discussion for the current lang and any of the following conditions are met: 
    1039   # - there already exists an +outside+, +open+ discussion for another language 
    1040   # - the virtual_class field auto_create_discussion is true 
    1041   # - the user has drive access to the node 
     1039  # no closed or open discussion for the current lang and Node#can_auto_create_discussion? is true 
    10421040  def discussion 
    10431041    return @discussion if defined?(@discussion) 
     
    10521050  end 
    10531051   
     1052  # Automatically create a discussion if any of the following conditions are met: 
     1053  # - there already exists an +outside+, +open+ discussion for another language 
     1054  # - the node is not published (creates an internal discussion) 
     1055  # - the user has drive access to the node 
    10541056  def can_auto_create_discussion? 
    10551057    can_drive? || 
    10561058    (v_status != Zena::Status[:pub]) || 
    10571059    Discussion.find(:first, :conditions=>[ "node_id = ? AND inside = ? AND open = ?",  
    1058                              self[:id], false, true ]) || 
    1059     (vclass.auto_create_discussion) 
    1060   end 
    1061    
    1062   # Comments for the current context. Returns [] when none found. 
     1060                             self[:id], false, true ]) 
     1061  end 
     1062   
     1063  # Comments for the current context. Returns nil when there is no discussion. 
    10631064  def comments 
    10641065    if discussion 
    1065       discussion.comments(:with_prop=>can_drive?) 
     1066      res = discussion.comments(:with_prop=>can_drive?) 
     1067      res == [] ? nil : res 
    10661068    else 
    1067       [] 
     1069      nil 
    10681070    end 
    10691071  end 
     
    12461248    end 
    12471249     
     1250    # Create an 'outside' discussion if the virtual class has auto_create_discussion set 
     1251    def node_after_create 
     1252      if vclass.auto_create_discussion 
     1253        Discussion.create(:node_id=>self[:id], :lang=>v_lang, :inside => false) 
     1254      end 
     1255    end 
     1256     
    12481257    # Called after a node is 'unpublished' 
    12491258    def after_unpublish 
  • trunk/app/models/site.rb

    r1008 r1061  
    1919  validate :valid_site 
    2020  validates_uniqueness_of :host 
    21   attr_accessible :name, :languages, :default_lang, :authentication, :monolingual, :allow_private, :http_auth, :auto_publish, :redit_time 
     21  # we are using 'attr_protected' instead of attr_accessible because we have dynamic attributes 
     22  attr_protected *(column_names.map{|e| e.to_sym} - [:name, :languages, :default_lang, :authentication, :monolingual, :allow_private, :http_auth, :auto_publish, :redit_time]) 
    2223  has_many :groups, :order => "name" 
    2324  has_many :nodes 
    2425  has_many :participations, :dependent => :destroy 
    2526  has_many :users, :through => :participations 
     27  uses_dynamic_attributes :table_name => 'site_attributes' 
     28   
     29  @@attributes_for_form = { 
     30    :bool => [:authentication, :monolingual, :allow_private, :http_auth, :auto_publish], 
     31    :text => [:name, :languages, :default_lang], 
     32  } 
    2633   
    2734  class << self 
     
    152159      site 
    153160    end 
     161   
     162    # List of attributes that can be configured in the admin form 
     163    def attributes_for_form 
     164      @@attributes_for_form 
     165    end 
    154166  end 
    155167   
     
    310322    end 
    311323end 
     324 
     325load_patches_from_plugins 
  • trunk/app/views/comments/_bin.rhtml

    r512 r1061  
    1 <% if bin_content.size == 0 %> 
     1<% if bin_content_size == 0 %> 
    22  <img src='/images/bin_empty.gif'/> 
    33<% else %> 
    4   <img src='/images/bin_full.gif'/><span><%= bin_content.size %></span> <%= link_to_remote(_('btn_bomb'), :url=>{:controller=>'comments', :action=>'empty_bin'}, :confirm=>_('Are you sure you want to permanently remove the content of this rubbish bin ?') ) %> 
     4  <img src='/images/bin_full.gif'/><span><%= bin_content_size %></span> <%= link_to_remote(_('btn_bomb'), :url => empty_bin_comments_path, :confirm=>_('Are you sure you want to permanently remove the content of this rubbish bin ?'), :method => :delete ) %> 
    55<% end %> 
  • trunk/app/views/comments/_li.rhtml

    r1060 r1061  
    1 <% puts li.inspect %> 
    21<li id='comment<%= li[:id] %>' <%= "class='c40' " if li[:status] < Zena::Status[:pub] %>><div class='header'> 
    3     <span class='reply'><% if li.can_write? -%> 
    4     <%= link_to_remote(_('edit'),  :url=>{:controller=>'comments', :action=>'edit', :id=>li[:id] })  %> 
    5     <% elsif @node && @node.can_comment? -%> 
    6     <%= link_to_remote(_('reply'), :url=>{:controller=>'comments', :action=>'reply_to', :id=>li[:id] }) %> 
    7     <% end -%> 
    8     </span> 
    92    <span class='actions'> 
    10     <%= link_to_remote(_('btn_remove'), :url=>{:controller=>'comments', :action=>'remove', :id=>li[:id], :bin=>@admin }) if @admin || (@node.can_drive? && li[:status] < Zena::Status[:pub]) %> 
    11     <%= link_to_remote(_('btn_publish'), :url=>{:controller=>'comments', :action=>'publish', :id=>li[:id], :bin=>@admin }) if (@admin || @node.can_drive?) && li[:status] < Zena::Status[:pub] %> 
     3    <%= link_to_remote(_('btn_remove'), :url => remove_comment_path(:id=>li[:id], :bin=>@admin), :method => :put) %> 
     4    <%= link_to_remote(_('btn_publish'), :url=> publish_comment_path(:id=>li[:id], :bin=>@admin), :method => :put) if li[:status] < Zena::Status[:pub] %> 
    125    </span> 
    136    <span class='date'><%= long_date(li.created_at) + " " + short_time(li.created_at) %></span> 
     
    1710  <div class='body'> 
    1811    <div class='zazen'><%= zazen(li.text) %></div> 
    19     <ul class='sub' id='replies_to<%= li[:id] %>'><%= render(:partial=>'comments/li', :collection=>li.replies(:with_prop=>@node.can_publish?)) unless @admin %></ul> 
    2012  </div> 
    2113</li> 
  • trunk/app/views/comments/_list.rhtml

    r692 r1061  
    22if @comments || node.discussion %> 
    33<div class='comments' id='comments'> 
     4  <div id='comment_errors'></div> 
    45  <ul><%= render :partial=>'comments/li', :collection => @comments || node.comments %> 
    56    <% if node.can_comment? -%> 
  • trunk/app/views/comments/index.rhtml

    r796 r1061  
    11<h3><%= _('manage comments') %></h3> 
     2<div id='comment_errors'></div> 
    23 
    34<ul class='comments' id='comments_list'> 
  • trunk/app/views/comments/publish.rjs

    r1060 r1061  
    1 page.visual_effect :highlight, "comment#{@comment[:id]}", :duration => 1.0 
    2 page.replace "comment#{@comment[:id]}", :partial=>'comments/li', :locals=>{:li=>@comment} 
     1if @comment.errors.empty? 
     2  page.visual_effect :highlight, "comment#{@comment[:id]}", :duration => 1.0 
     3  page.replace "comment#{@comment[:id]}", :partial=>'comments/li', :collection => [@comment] 
     4  page.replace_html "comment_errors", :inline=>"" 
     5else 
     6  page.replace_html "comment_errors", :inline=>error_messages_for(@comment) 
     7end 
  • trunk/app/views/comments/remove.rjs

    r484 r1061  
    1 page.visual_effect :highlight, "comment#{@comment[:id]}", :duration => 0.3 
    2 page.visual_effect :fade, "comment#{@comment[:id]}", :duration => 0.5 
    3 if params[:bin] 
    4   page.replace_html 'bin', :partial=>'comments/bin' 
     1if @comment.errors.empty? 
     2  page.visual_effect :fade, "comment#{@comment[:id]}", :duration => 0.2 
     3  if params[:bin] 
     4    page.replace_html 'bin', :partial=>'comments/bin' 
     5  end 
     6  page.replace_html "comment_errors", :inline=>"" 
     7else 
     8  page.replace_html "comment_errors", :inline=>error_messages_for(@comment) 
    59end 
  • trunk/app/views/nodes/create.rjs

    r857 r1061  
    55  ref = params[:reference] || "#{dom_id_from_template_url}_add" 
    66  page.insert_html pos.to_sym, ref, :file => fullpath_from_template_url + ".erb" 
    7   @node = @node.parent.new_child(:class => @node.class) 
    8   page.replace "#{dom_id_from_template_url}_form", :file => fullpath_from_template_url + "_form.erb" 
    97  if params[:done] 
    10     page << params[:done] 
     8    page << params[:done].gsub("NODE_ID", @node.zip).gsub("PARENT_ID", @node.parent_zip) 
    119  else 
    1210    page.toggle "#{dom_id_from_template_url}_form", "#{dom_id_from_template_url}_add" 
    1311  end 
     12   
     13  @node = @node.parent.new_child(:class => @node.class) 
     14  page.replace "#{dom_id_from_template_url}_form", :file => fullpath_from_template_url + "_form.erb" 
    1415end 
  • trunk/app/views/sites/_form.erb

    r957 r1061  
    1515  <table cellspacing='0' class='edit_site'> 
    1616    <tr><td class='label'><%= _('host') %></td><td><%= @site[:host] %></td></tr> 
    17     <% [:name, :languages, :default_lang].each do |sym| -%> 
    18     <tr><td class='label'><%= _(sym.to_s) %></td><td><%= text_field('site', sym, :size=>15) %></td></tr> 
     17    <% Site.attributes_for_form[:text].each do |sym| -%> 
     18    <tr><td class='label'><%= _(sym.to_s.sub(/\Ad_/,'').sub('_',' ')) %></td><td><%= text_field('site', sym, :size=>nil) %></td></tr> 
    1919    <% end -%> 
    2020    <tr><td class='label'><%= _('redit_time') %></td><td><%= text_field('site', :redit_time, :size=>15, :value => @site.redit_time) %></td></tr> 
     
    2222    <tr><td class='label'><%= _('site group') %></td><td><%= @site.site_group.name %></td></tr> 
    2323    <tr><td class='label'><%= _('options') %></td><td> 
    24     <% [:authentication, :monolingual, :allow_private, :http_auth, :auto_publish].each do |sym| -%> 
     24    <% Site.attributes_for_form[:bool].each do |sym| -%> 
    2525    <input type='hidden' name='site[<%= sym %>]' value=''/><input type='checkbox' name='site[<%= sym %>]' value='1'<%= @site[sym] ? " checked='checked'" : '' %>/> <%= _(sym.to_s) %> 
    2626    <% end -%> 
  • trunk/app/views/virtual_classes/_li.erb

    r1060 r1061  
    66  <td><%= li.name %></td> 
    77  <td class="adm_icon" ><%= li.icon ? "<img src='#{li.icon}'/>" : "" %></td> 
    8   <td class="auto_create_discussion" ><%= li.auto_create_discussion ? _('yes') : _('no') %></td> 
     8  <td class="auto_create_discussion" ><%= li.auto_create_discussion ? _('auto discussion') : '' %></td> 
    99  <td class="create_group"><%= li.create_group.name %></td> 
    1010  <td class="superclass" ><%= li.superclass %></td> 
  • trunk/config/environment.rb

    r964 r1061  
    7272require File.join(File.dirname(__FILE__), '../lib/core_ext/fixnum') 
    7373require File.join(File.dirname(__FILE__), '../lib/core_ext/dir') 
     74require File.join(File.dirname(__FILE__), '../lib/core_ext/patcher') 
    7475ZazenParser = Parser.parser_with_rules(Zazen::Rules, Zazen::Tags) 
    7576ZafuParser  = Parser.parser_with_rules(Zafu::Rules, Zena::Rules, Zafu::Tags, Zena::Tags) 
  • trunk/config/routes.rb

    r1057 r1061  
    5151  map.resources :sites, 
    5252    :member => { :clear_cache => :post} 
    53   map.resources :comments 
     53  map.resources :comments, 
     54                :collection => { :empty_bin => :delete }, 
     55                :member => { :remove  => :put, 
     56                             :publish => :put, 
     57                             :reply_to => :post, 
     58                           } 
    5459  map.resources :data_entries, :member => { :zafu => :get } 
    5560 
     
    9398   
    9499  # temporary routes... 
    95   map.connect 'comments/:action/:id', :controller => 'comments' 
    96100  map.connect 'discussions/:action/:id', :controller => 'discussions' 
    97101  map.connect 'z/link/:action/:id', :controller => 'link' 
  • trunk/db/schema.rb

    r1060 r1061  
    212212  end 
    213213 
     214  create_table "site_attributes", :force => true do |t| 
     215    t.column "owner_id", :integer 
     216    t.column "key",      :string 
     217    t.column "value",    :text 
     218  end 
     219 
     220  add_index "site_attributes", ["owner_id"], :name => "index_site_attributes_on_owner_id" 
     221 
    214222  create_table "sites", :force => true do |t| 
    215223    t.column "host",               :string 
  • trunk/lib/parser/lib/rules/zena.rb

    r1059 r1061  
    819819            hidden_fields['done'] = "$('#{dom_id_from_template_url}_#{add_block.params[:focus] || 'v_title'}').focus();" 
    820820          elsif params[:done] 
    821             hidden_fields['done'] = params[:done].inspect 
     821            hidden_fields['done'] = CGI.escape(params[:done]) 
    822822          end 
    823823        end 
     
    834834      hidden_fields.each do |k,v| 
    835835        next if set_fields.include?(k) 
    836         form << "<input type='hidden' name='#{k}' value=\"#{v}\"/>\n" 
     836        v = "\"#{v}\"" unless v.kind_of?(String) && ['"', "'"].include?(v[0..0]) 
     837        form << "<input type='hidden' name='#{k}' value=#{v}/>\n" 
    837838      end 
    838839      form << "</div>" 
     
    10501051      end 
    10511052      #dom_id = "#{@context[:dom_id]}.\#{#{node_id}}" 
    1052       dom_id = "#{@context[:erb_dom_id]}.#{erb_node_id}" 
     1053      erb_dom_id = "#{@context[:erb_dom_id]}.#{erb_node_id}" 
    10531054      if node_kind_of?(Node) 
    10541055        out "<% if #{node}[:link_id] -%>" 
    1055         out "<a class='#{@params[:class] || 'unlink'}' href='/nodes/#{erb_node_id}/links/<%= #{node}[:link_id] %>?remove=#{dom_id}' onclick=\"new Ajax.Request('/nodes/#{erb_node_id}/links/<%= #{node}[:link_id] %>?remove=#{dom_id}', {asynchronous:true, evalScripts:true, method:'delete'}); return false;\">" 
     1056        out "<a class='#{@params[:class] || 'unlink'}' href='/nodes/#{erb_node_id}/links/<%= #{node}[:link_id] %>?remove=#{erb_dom_id}' onclick=\"new Ajax.Request('/nodes/#{erb_node_id}/links/<%= #{node}[:link_id] %>?remove=#{erb_dom_id}', {asynchronous:true, evalScripts:true, method:'delete'}); return false;\">" 
    10561057        if !@blocks.empty? 
    10571058          out expand_with 
     
    10601061        end 
    10611062        out "</a><% end -%>" 
    1062       elsif node_kind_of?(DataEntry)   
     1063      elsif node_kind_of?(DataEntry) 
     1064        dom_id = "#{@context[:dom_id]}.\#{#{node_id}}" 
    10631065        out "<%= link_to_remote(#{text.inspect}, {:url => \"/data_entries/\#{#{node}[:id]}?remove=#{dom_id}\", :method => :delete}, :class=>#{(@params[:class] || 'unlink').inspect}) %>" 
    10641066      end 
     
    11531155          out "<% #{var}_dom_ids = [] -%>" 
    11541156        end 
    1155          
    1156         if join = @params[:join] 
     1157        @params[:alt_class] ||= @html_tag_params.delete(:alt_class) 
     1158         
     1159        if @params[:alt_class] || @params[:join] 
     1160          join = @params[:join] || '' 
    11571161          join = join.gsub(/&lt;([^%])/, '<\1').gsub(/([^%])&gt;/, '\1>') 
    11581162          out "<% #{list}.each_index do |#{var}_index| -%>" 
    11591163          out "<%= #{var}=#{list}[#{var}_index]; #{var}_index > 0 ? #{join.inspect} : '' %>" 
     1164           
     1165          if alt_class = @params[:alt_class] 
     1166            if html_class = @html_tag_params.delete(:class) 
     1167              html_append = " class='<%= #{var}_index % 2 != 0 ? #{alt_class.inspect} : #{html_class.inspect} %>'" 
     1168            else 
     1169              html_append = "<%= #{var}_index % 2 != 0 ? ' class=#{alt_class.inspect}' : '' %>" 
     1170            end 
     1171          else 
     1172            html_append = nil 
     1173          end 
    11601174        else 
    11611175          out "<% #{list}.each do |#{var}| -%>" 
     1176          html_append = nil 
    11621177        end 
    11631178         
     
    11761191         
    11771192        if @context[:template_url] || @params[:draggable] == 'true' || descendant('unlink') 
    1178           # ajax, set id 
     1193          # ajax, set id, class 
    11791194          id_hash = {:id=> "#{erb_dom_id}.#{erb_node_id(var)}"} 
    11801195          if @html_tag 
     
    11871202          res = expand_with(:node=>var) 
    11881203        end 
    1189         out render_html_tag(res
     1204        out render_html_tag(res, html_append
    11901205        out "<% end -%>" 
    11911206         
     
    11981213        if @html_tag 
    11991214          @html_tag_params.merge!(id_hash) 
    1200           out render_html_tag(expand_with(:dom_id => dom_id)) # dom_id is needed by 'unlink' 
     1215          out render_html_tag(expand_with(:dom_id => dom_id), html_append) # dom_id is needed by 'unlink' 
    12011216        else 
    12021217          out add_params(expand_with, id_hash) 
     
    19031918        form_block = descendant('form') || each_block 
    19041919        if list_finder 
    1905           out "<% if (#{list_var} = #{list_finder}) || (#{node}.can_write? && #{list_var}=[]) -%>" 
     1920          out "<% if (#{list_var} = #{list_finder}) || (#{node}.#{node_kind_of?(Comment) ? "can_comment?" : "can_write?"} && #{list_var}=[]) -%>" 
    19061921        end 
    19071922         
     
    19391954        if list_finder 
    19401955          if descendant('add') || descendant('add_document') 
    1941             out "<% if (#{list_var} = #{list_finder}) || (#{node}.can_write? && #{list_var}=[]) -%>" 
     1956            out "<% if (#{list_var} = #{list_finder}) || (#{node}.#{node_kind_of?(Comment) ? "can_comment?" : "can_write?"} && #{list_var}=[]) -%>" 
    19421957          elsif list_finder != 'nil' 
    19431958            out "<% if #{list_var} = #{list_finder} -%>" 
     
    20112026          k = $2.to_sym 
    20122027        end 
    2013         if [:kind_of, :klass, :status, :lang, :can, :node, :in].include?(k) 
     2028        if [:kind_of, :klass, :status, :lang, :can, :node, :in, :visitor, :has].include?(k) 
    20142029          tests << [k, v] 
    20152030        elsif k == :test 
     
    20412056          when 'drive', 'publish' 
    20422057            "#{node}.can_drive?" 
     2058          else 
     2059            nil 
     2060          end 
     2061        when :has 
     2062          case value 
     2063          when 'discussion' 
     2064            "#{node}.discussion" 
     2065          else 
     2066            nil 
    20432067          end 
    20442068        when :test 
     
    21052129            'false' 
    21062130          end 
     2131        when :visitor 
     2132          if value == 'anon' 
     2133            "visitor.is_anon?" 
     2134          else 
     2135            nil 
     2136          end 
    21072137        else 
    21082138          nil 
     
    21142144    # Block visibility of descendance with 'do_list'. 
    21152145    def public_descendants 
    2116       if ['do_list'].include?(@method) 
    2117         {} 
    2118       else 
    2119         super 
    2120       end 
     2146      all = super 
     2147      if @method == 'each' 
     2148        # do not propagate 'form' up 
     2149        all.delete('form') 
     2150      end 
     2151      all 
    21212152    end 
    21222153     
     
    24182449        value = attribute ? "<%= #{node_attribute(attribute)} %>" : "" 
    24192450      end 
    2420       "<textarea name='#{name}'>#{value}</textarea>" 
     2451      input_id = @context[:template_url] ? " id='#{(dom_id_from_template_url + '_' + attribute.to_s)}#{erb_node_id}'" : '' 
     2452      "<textarea name='#{name}'#{input_id}>#{value}</textarea>" 
    24212453    end 
    24222454     
  • trunk/po/en/zena.po

    r1057 r1061  
    22msgstr "" 
    33"Project-Id-Version: 0.9.0\n" 
    4 "POT-Creation-Date: 2008-06-09 20:09-0000\n" 
     4"POT-Creation-Date: 2008-06-12 15:07-0000\n" 
    55"PO-Revision-Date: 2007-07-07 14:13+0100\n" 
    66"Last-Translator: Gaspard Bucher <gaspard@teti.ch>\n" 
     
    134134msgstr "%A, %B %d %Y" 
    135135 
    136 #: app/helpers/application_helper.rb:136 lib/parser/lib/rules/zena.rb:1346 
     136#: app/helpers/application_helper.rb:136 lib/parser/lib/rules/zena.rb:1359 
    137137msgid "long_date" 
    138138msgstr "%Y-%m-%d" 
     
    494494msgstr "" 
    495495 
    496 #: app/models/data_entry.rb:35 app/models/node.rb:620 
     496#: app/models/data_entry.rb:35 app/models/node.rb:621 
    497497#: app/views/data_entries/_li.html.erb:2 
    498498msgid "datetime" 
     
    14541454 
    14551455#: app/views/virtual_classes/_li.erb:8 
    1456 msgid "yes" 
    1457 msgstr "" 
    1458  
    1459 #: app/views/virtual_classes/_li.erb:8 
    1460 msgid "no" 
    1461 msgstr "" 
     1456#, fuzzy 
     1457msgid "auto discussion" 
     1458msgstr "<img src='/images/comments_add.png' alt='add a discussion'/>" 
    14621459 
    14631460#: lib/gettext_strings.rb:5 
     
    18181815msgstr "<img src='/images/add.png' alt='add'/>" 
    18191816 
    1820 #: lib/parser/lib/rules/zena.rb:1396 
     1817#: lib/parser/lib/rules/zena.rb:1409 
    18211818msgid "riding zena" 
    18221819msgstr "" 
    18231820 
    1824 #: lib/parser/lib/rules/zena.rb:1398 
     1821#: lib/parser/lib/rules/zena.rb:1411 
    18251822msgid "in peace with zena" 
    18261823msgstr "" 
    18271824 
    1828 #: lib/parser/lib/rules/zena.rb:1400 
     1825#: lib/parser/lib/rules/zena.rb:1413 
    18291826msgid "a zen garden" 
    18301827msgstr "" 
    18311828 
    1832 #: lib/parser/lib/rules/zena.rb:1402 
     1829#: lib/parser/lib/rules/zena.rb:1415 
    18331830msgid "made with zena" 
    18341831msgstr "" 
    18351832 
    1836 #: lib/parser/lib/rules/zena.rb:1418 
     1833#: lib/parser/lib/rules/zena.rb:1431 
    18371834msgid "%{skin}, design by %{name}" 
    18381835msgstr "" 
  • trunk/po/fr/zena.po

    r1057 r1061  
    22msgstr "" 
    33"Project-Id-Version: 0.9.0\n" 
    4 "POT-Creation-Date: 2008-06-09 20:09-0000\n" 
     4"POT-Creation-Date: 2008-06-12 15:07-0000\n" 
    55"PO-Revision-Date: 2007-07-07 14:13+0100\n" 
    66"Last-Translator: Gaspard Bucher <gaspard@teti.ch>\n" 
     
    135135msgstr "%A, %d %B %Y" 
    136136 
    137 #: app/helpers/application_helper.rb:136 lib/parser/lib/rules/zena.rb:1346 
     137#: app/helpers/application_helper.rb:136 lib/parser/lib/rules/zena.rb:1359 
    138138msgid "long_date" 
    139139msgstr "%d.%m.%Y" 
     
    515515msgstr "objet" 
    516516 
    517 #: app/models/data_entry.rb:35 app/models/node.rb:620 
     517#: app/models/data_entry.rb:35 app/models/node.rb:621 
    518518#: app/views/data_entries/_li.html.erb:2 
    519519msgid "datetime" 
     
    15271527 
    15281528#: app/views/virtual_classes/_li.erb:8 
    1529 msgid "yes" 
    1530 msgstr "" 
    1531  
    1532 #: app/views/virtual_classes/_li.erb:8 
    1533 #, fuzzy 
    1534 msgid "no" 
    1535 msgstr "objet" 
     1529#, fuzzy 
     1530msgid "auto discussion" 
     1531msgstr "discussion" 
    15361532 
    15371533#: lib/gettext_strings.rb:5 
     
    18901886msgstr "<img src='/images/add.png' alt='ajouter'/>" 
    18911887 
    1892 #: lib/parser/lib/rules/zena.rb:1396 
     1888#: lib/parser/lib/rules/zena.rb:1409 
    18931889msgid "riding zena" 
    18941890msgstr "en balade avec zena" 
    18951891 
    1896 #: lib/parser/lib/rules/zena.rb:1398 
     1892#: lib/parser/lib/rules/zena.rb:1411 
    18971893msgid "in peace with zena" 
    18981894msgstr "paisible avec zena" 
    18991895 
    1900 #: lib/parser/lib/rules/zena.rb:1400 
     1896#: lib/parser/lib/rules/zena.rb:1413 
    19011897msgid "a zen garden" 
    19021898msgstr "un jardin zen" 
    19031899 
    1904 #: lib/parser/lib/rules/zena.rb:1402 
     1900#: lib/parser/lib/rules/zena.rb:1415 
    19051901msgid "made with zena" 
    19061902msgstr "conçu avec zena" 
    19071903 
    1908 #: lib/parser/lib/rules/zena.rb:1418 
     1904#: lib/parser/lib/rules/zena.rb:1431 
    19091905msgid "%{skin}, design by %{name}" 
    19101906msgstr "%{skin}, conception %{name}" 
     1907 
     1908#, fuzzy 
     1909#~ msgid "no" 
     1910#~ msgstr "objet" 
    19111911 
    19121912#, fuzzy 
  • trunk/po/zena.pot

    r1057 r1061  
    88msgstr "" 
    99"Project-Id-Version: 0.9.0\n" 
    10 "POT-Creation-Date: 2008-06-09 20:09-0000\n" 
     10"POT-Creation-Date: 2008-06-12 15:07-0000\n" 
    1111"PO-Revision-Date: 2007-05-13 19:08-0000\n" 
    1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" 
     
    135135msgstr "" 
    136136 
    137 #: app/helpers/application_helper.rb:136 lib/parser/lib/rules/zena.rb:1346 
     137#: app/helpers/application_helper.rb:136 lib/parser/lib/rules/zena.rb:1359 
    138138msgid "long_date" 
    139139msgstr "" 
     
    494494msgstr "" 
    495495 
    496 #: app/models/data_entry.rb:35 app/models/node.rb:620 
     496#: app/models/data_entry.rb:35 app/models/node.rb:621 
    497497#: app/views/data_entries/_li.html.erb:2 
    498498msgid "datetime" 
     
    14311431 
    14321432#: app/views/virtual_classes/_li.erb:8 
    1433 msgid "yes" 
    1434 msgstr "" 
    1435  
    1436 #: app/views/virtual_classes/_li.erb:8 
    1437 msgid "no" 
     1433msgid "auto discussion" 
    14381434msgstr "" 
    14391435 
     
    17901786msgstr "" 
    17911787 
    1792 #: lib/parser/lib/rules/zena.rb:1396 
     1788#: lib/parser/lib/rules/zena.rb:1409 
    17931789msgid "riding zena" 
    17941790msgstr "" 
    17951791 
    1796 #: lib/parser/lib/rules/zena.rb:1398 
     1792#: lib/parser/lib/rules/zena.rb:1411 
    17971793msgid "in peace with zena" 
    17981794msgstr "" 
    17991795 
    1800 #: lib/parser/lib/rules/zena.rb:1400 
     1796#: lib/parser/lib/rules/zena.rb:1413 
    18011797msgid "a zen garden" 
    18021798msgstr "" 
    18031799 
    1804 #: lib/parser/lib/rules/zena.rb:1402 
     1800#: lib/parser/lib/rules/zena.rb:1415 
    18051801msgid "made with zena" 
    18061802msgstr "" 
    18071803 
    1808 #: lib/parser/lib/rules/zena.rb:1418 
     1804#: lib/parser/lib/rules/zena.rb:1431 
    18091805msgid "%{skin}, design by %{name}" 
    18101806msgstr "" 
  • trunk/public/stylesheets/comment.css

    r1060 r1061  
    4040.comments .btn_x a { display:block; } 
    4141.comments .btn_add a { visibility:visible;} 
    42 .comments .actions { position:absolute; right:-20px;} 
     42.comments .actions { position:absolute; left:-23px;} 
    4343.comments .actions a { display:block;} 
  • trunk/public/stylesheets/popup.css

    r1048 r1061  
    1616 
    1717/* search box */ 
    18 #search { z-index:999; z-index:999; position:absolute; top:1px; right:1px; padding-right:1px; font-size:0.8em; display:table;} 
     18#search { z-index:999; position:absolute; top:1px; right:1px; padding-right:1px; font-size:0.8em; display:table;} 
    1919 
    2020/* messages and ajax-loader */ 
     
    3939.tab { padding:0; } 
    4040.tab form { } 
    41 .tab .validate { position:absolute; right:0; margin:0 2px; background:none; padding:3px;
     41.tab .validate { position:absolute; right:0; margin:0 2px; background:none; padding:3px; z-index:888;
    4242.tab .validate div { float:right; margin-left:4px; } 
    4343.tab .validate input { font-size:0.9em; padding:0;}