Changeset 425

Show
Ignore:
Timestamp:
2007-04-24 23:08:21 (2 years ago)
Author:
gaspard
Message:

[zafu] more bug fixes
[documents] updated controller. Document update, Image crop working

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/rest/TODO

    r422 r425  
    4949* replace rmagick with a more simple library or use something like system "convert #{escape(temp)} -resize 48x48! #{escape(target)}" 
    5050* show_title could guess when to display a title and when not to 
     51* why are '&' in the url escaped if escape => false is not set ??? (versions/edit.rhtml) 
    5152=== Documentation 
    5253* cleanup README_FOR_APP 
  • branches/rest/app/controllers/application.rb

    r424 r425  
    1212  private 
    1313   
    14   # TODO: test 
    15   # Our own handling of exceptions 
    16   def rescue_action_in_public(exception) 
    17     case exception 
    18     when ActiveRecord::RecordNotFound, ActionController::UnknownAction 
    19       render_404(exception) 
    20     else 
    21       render_500(exception) 
    22     end 
    23   end 
    24    
    25   # TODO: test 
    26   def render_404(exception) 
    27     respond_to do |format| 
    28       format.html { redirect_to not_found_url } # FIXME: can we keep some info on the '404' status ? 
    29       format.all  { render :nothing => true, :status => "404 Not Found" } 
    30     end 
    31   end 
    32    
    33   # TODO: test 
    34   def render_500(exception) 
    35     respond_to do |format| 
    36       format.html { render :file    => "#{RAILS_ROOT}/public/500.html", :status => '500 Error' } 
    37       format.all  { render :nothing => true, :status => "500 Error" } 
    38     end 
    39   end 
    40    
    41   # TODO: test 
    42   def visitor 
    43     return @visitor if @visitor 
    44      
    45     if session[:user] 
    46       begin 
    47         # we already have a user, check host 
    48         if session[:host] == request.host 
    49           # host hasn't changed, set visitor and site 
    50           @visitor = User.find(session[:user]) 
    51           @visitor.site = Site.find(:first, :conditions=>["host = ? ",request.host]) # raises RecordNotFound if site not found 
    52         else 
    53           # changed host 
    54           if site = Site.find(:first, :select=>"sites.*", :from=>"sites, users_sites", :conditions=>["users_sites.site_id = sites.id AND host = ? AND users_sites.user_id = ?",request.host,session[:user]]) 
    55             # current user is in the new site 
     14    # TODO: test 
     15    # Our own handling of exceptions 
     16    def rescue_action_in_public(exception) 
     17      case exception 
     18      when ActiveRecord::RecordNotFound, ActionController::UnknownAction 
     19        render_404(exception) 
     20      else 
     21        render_500(exception) 
     22      end 
     23    end 
     24   
     25    # TODO: test 
     26    def render_404(exception) 
     27      respond_to do |format| 
     28        format.html { redirect_to not_found_url } # FIXME: can we keep some info on the '404' status ? 
     29        format.all  { render :nothing => true, :status => "404 Not Found" } 
     30      end 
     31    end 
     32   
     33    # TODO: test 
     34    def render_500(exception) 
     35      respond_to do |format| 
     36        format.html { render :file    => "#{RAILS_ROOT}/public/500.html", :status => '500 Error' } 
     37        format.all  { render :nothing => true, :status => "500 Error" } 
     38      end 
     39    end 
     40   
     41    # TODO: test 
     42    def visitor 
     43      return @visitor if @visitor 
     44     
     45      if session[:user] 
     46        begin 
     47          # we already have a user, check host 
     48          if session[:host] == request.host 
     49            # host hasn't changed, set visitor and site 
    5650            @visitor = User.find(session[:user]) 
    57             @visitor.site = site 
     51            @visitor.site = Site.find(:first, :conditions=>["host = ? ",request.host]) # raises RecordNotFound if site not found 
     52          else 
     53            # changed host 
     54            if site = Site.find(:first, :select=>"sites.*", :from=>"sites, users_sites", :conditions=>["users_sites.site_id = sites.id AND host = ? AND users_sites.user_id = ?",request.host,session[:user]]) 
     55              # current user is in the new site 
     56              @visitor = User.find(session[:user]) 
     57              @visitor.site = site 
     58            end 
    5859          end 
    59         end 
    60       rescue ActiveRecord::RecordNotFound 
    61         # user was not in host or bad session id 
    62         @visitor = nil 
    63       end 
    64     end 
    65      
    66     unless @visitor 
    67       # find the anonymous visitor for the current site 
    68       if site = Site.find_by_host(request.host) 
    69         @visitor = site.anon 
    70         @visitor.site = site 
    71       else 
    72         # FIXME: error page 505 
    73         raise ActiveRecord::RecordNotFound 
    74         return false 
    75       end 
    76     end 
    77      
    78     session[:host] = request.host 
    79     session[:user] = @visitor[:id] 
    80     @visitor.visit(@visitor)      # used to check 'su', 'anon', etc 
    81     @visitor.visit(@visitor.site) # used to secure access to 'root_node' 
    82     @visitor 
    83   end 
    84    
    85   # TODO: test 
    86   def lang 
    87     visitor.lang 
    88   end 
    89      
    90   def render_and_cache(opts={}) 
    91     # cleanup before rendering 
    92     params.delete(:mode) 
    93     params.delete(:format) 
    94      
    95     opts = {:skin=>@node[:skin], :cache=>true}.merge(opts) 
     60        rescue ActiveRecord::RecordNotFound 
     61          # user was not in host or bad session id 
     62          @visitor = nil 
     63        end 
     64      end 
     65     
     66      unless @visitor 
     67        # find the anonymous visitor for the current site 
     68        if site = Site.find_by_host(request.host) 
     69          @visitor = site.anon 
     70          @visitor.site = site 
     71        else 
     72          # FIXME: error page 505 
     73          raise ActiveRecord::RecordNotFound 
     74          return false 
     75        end 
     76      end 
     77     
     78      session[:host] = request.host 
     79      session[:user] = @visitor[:id] 
     80      @visitor.visit(@visitor)      # used to check 'su', 'anon', etc 
     81      @visitor.visit(@visitor.site) # used to secure access to 'root_node' 
     82      @visitor 
     83    end 
     84     
     85    # TODO: test 
     86    def lang 
     87      visitor.lang 
     88    end 
     89     
     90    def render_and_cache(opts={}) 
     91      # cleanup before rendering 
     92      params.delete(:mode) 
     93      params.delete(:format) 
     94     
     95      opts = {:skin=>@node[:skin], :cache=>true}.merge(opts) 
    9696       
    97     @section = @node.section 
    98     @date  ||= params[:date] ? parse_date(params[:date]) : Time.now 
    99     render :file => template_url(opts), :layout=>false 
    100      
    101     cache_page if opts[:cache] 
    102   end 
    103    
    104   # Cache page content into a static file in the current sites directory : SITES_ROOT/test.host/public 
    105   def cache_page(opts={}) 
    106     return unless perform_caching && caching_allowed 
    107     opts = {:expire_after  => nil, 
    108             :path          => (visitor.site.public_path + page_cache_file), 
    109             :content_data  => response.body    
    110             }.merge(opts) 
    111     secure(CachedPage) { CachedPage.create(opts) } 
    112   end 
    113    
    114   # Return true if we can cache the current page 
    115   def caching_allowed 
    116     visitor.is_anon? 
    117   end 
    118    
    119   # Cache file path that reflects the called url 
    120   def page_cache_file 
    121     path = url_for(:only_path => true, :skip_relative_url_root => true) 
    122     path = ((path.empty? || path == "/") ? "/index" : URI.unescape(path)) 
    123     path << ".#{params[:format] || 'html'}" unless path =~ /\.#{params[:format]}$/ 
    124     path 
    125   end 
    126    
    127   # Find the best template for the current node's skin, node's class, format and mode. The template 
    128   # files are searched first into 'sites/shared/views/templates/fixed'. If the templates are not found 
    129   # there, they are searched in the database and compiled into 'app/views/templates/compiled'. 
    130   def template_url(opts={}) 
    131     skin_name = opts[:skin]   || (@node ? @node[:skin] : nil) || 'default' 
    132     skin_name = skin_name.gsub(/[^a-zA-Z]/,'') # security 
    133     mode      = opts[:mode]   || params[:mode] 
    134     format    = opts[:format] || params[:format] || 'html' 
    135     klass     = @node.class 
    136     # FIXME: rescue_template = opts[:rescue_template] || '/templates/fixed/default/any' 
    137     # FIXME: @skin_obj   = nil 
    138     # classes : 
    139     klasses = [] 
    140     klass.kpath.split(//).each_index { |i| klasses << klass.kpath[0..i] } 
    141      
    142     best_match = {  
    143       :conditions => ["tkpath IN (?) AND format = ? AND mode #{mode ? '=' : 'IS'} ?", klasses, format, mode], 
    144       :select     => "*, (template_contents.skin_name = '#{skin_name}') AS skin_ok", 
    145       :order      => "length(tkpath) DESC, skin_ok DESC" 
    146     } 
    147      
    148     if skin_name == 'default' 
    149       template = TemplateContent.find(:first, best_match) 
    150       skin_root = "#{SITES_ROOT}/shared" 
    151     else 
    152       best_match[:conditions][0] += " AND template_contents.node_id = nodes.id" 
    153       best_match[:from]   = "nodes, template_contents" 
    154       best_match[:select] = "nodes.*, template_contents.*, (template_contents.skin_name = #{skin_name}) AS skin_ok", 
    155       template = secure(Template) { Template.find(:first, best_match) } 
    156       skin_root = "#{SITES_ROOT}/#{visitor.site.host}" 
    157     end 
    158      
    159     raise ActiveRecord::RecordNotFound unless template 
    160     mode = "_#{mode}" if mode 
    161     skin_path = "/#{template.skin_name}/#{template.klass}#{mode}.#{format}" 
    162     main_path = "/#{visitor.lang}/main.erb" 
    163     url = "#{skin_root}/zafu.compiled#{skin_path}#{main_path}" 
    164      
    165     if File.exists?(url) 
    166       if skin_name == 'default' && (File.stat(url).mtime < File.stat("#{skin_root}/zafu#{skin_path}").mtime || RAILS_ENV == 'development') 
    167         FileUtils.rmtree("#{skin_root}/zafu.compiled#{skin_path}") 
    168       elsif skin_name != 'default' && (File.stat(url).mtime < template.v_updated_at || RAILS_ENV == 'development') 
    169         FileUtils.rmtree("#{skin_root}/zafu.compiled#{skin_path}") 
    170       else 
    171         # we can use the cached version 
    172         return url 
    173       end 
    174     end 
    175      
    176     # render zafu 
    177     response.template.instance_variable_set(:@session, session) 
    178     skin_helper = response.template 
    179     res = ZafuParser.new_with_url(skin_path, :helper => skin_helper).render 
    180     FileUtils::mkpath(File.dirname(url)) unless File.exists?(File.dirname(url)) 
    181     File.open(url, "wb") { |f| f.syswrite(res) }     
    182      
    183     return url 
    184   end 
    185    
    186   # tested in MainControllerTest 
    187   def template_text_for_url(url) 
    188     url = url[1..-1] # strip leading '/' 
    189     url = url.split('/') 
    190     skin_name = url.shift 
    191     if skin_name == 'default' 
    192       path = File.join(SITES_ROOT, 'shared', 'zafu', 'default', *url) 
    193       if File.exists?(path) 
    194         File.read(path) 
     97      @section = @node.section 
     98      @date  ||= params[:date] ? parse_date(params[:date]) : Time.now 
     99      render :file => template_url(opts), :layout=>false 
     100     
     101      cache_page if opts[:cache] 
     102    end 
     103   
     104    # Cache page content into a static file in the current sites directory : SITES_ROOT/test.host/public 
     105    def cache_page(opts={}) 
     106      return unless perform_caching && caching_allowed 
     107      opts = {:expire_after  => nil, 
     108              :path          => (visitor.site.public_path + page_cache_file), 
     109              :content_data  => response.body    
     110              }.merge(opts) 
     111      secure(CachedPage) { CachedPage.create(opts) } 
     112    end 
     113   
     114    # Return true if we can cache the current page 
     115    def caching_allowed 
     116      visitor.is_anon? 
     117    end 
     118   
     119    # Cache file path that reflects the called url 
     120    def page_cache_file 
     121      path = url_for(:only_path => true, :skip_relative_url_root => true) 
     122      path = ((path.empty? || path == "/") ? "/index" : URI.unescape(path)) 
     123      path << ".#{params[:format] || 'html'}" unless path =~ /\.#{params[:format]}$/ 
     124      path 
     125    end 
     126   
     127    # Find the best template for the current node's skin, node's class, format and mode. The template 
     128    # files are searched first into 'sites/shared/views/templates/fixed'. If the templates are not found 
     129    # there, they are searched in the database and compiled into 'app/views/templates/compiled'. 
     130    def template_url(opts={}) 
     131      skin_name = opts[:skin]   || (@node ? @node[:skin] : nil) || 'default' 
     132      skin_name = skin_name.gsub(/[^a-zA-Z]/,'') # security 
     133      mode      = opts[:mode]   || params[:mode] 
     134      format    = opts[:format] || params[:format] || 'html' 
     135      klass     = @node.class 
     136      # FIXME: rescue_template = opts[:rescue_template] || '/templates/fixed/default/any' 
     137      # FIXME: @skin_obj   = nil 
     138      # classes : 
     139      klasses = [] 
     140      klass.kpath.split(//).each_index { |i| klasses << klass.kpath[0..i] } 
     141     
     142      best_match = {  
     143        :conditions => ["tkpath IN (?) AND format = ? AND mode #{mode ? '=' : 'IS'} ?", klasses, format, mode], 
     144        :select     => "*, (template_contents.skin_name = '#{skin_name}') AS skin_ok", 
     145        :order      => "length(tkpath) DESC, skin_ok DESC" 
     146      } 
     147     
     148      if skin_name == 'default' 
     149        template = TemplateContent.find(:first, best_match) 
     150        skin_root = "#{SITES_ROOT}/shared" 
     151      else 
     152        best_match[:conditions][0] += " AND template_contents.node_id = nodes.id" 
     153        best_match[:from]   = "nodes, template_contents" 
     154        best_match[:select] = "nodes.*, template_contents.*, (template_contents.skin_name = #{skin_name}) AS skin_ok", 
     155        template = secure(Template) { Template.find(:first, best_match) } 
     156        skin_root = "#{SITES_ROOT}/#{visitor.site.host}" 
     157      end 
     158     
     159      raise ActiveRecord::RecordNotFound unless template 
     160      mode = "_#{mode}" if mode 
     161      skin_path = "/#{template.skin_name}/#{template.klass}#{mode}.#{format}" 
     162      main_path = "/#{visitor.lang}/main.erb" 
     163      url = "#{skin_root}/zafu.compiled#{skin_path}#{main_path}" 
     164     
     165      if File.exists?(url) 
     166        if skin_name == 'default' && (File.stat(url).mtime < File.stat("#{skin_root}/zafu#{skin_path}").mtime || RAILS_ENV == 'development') 
     167          FileUtils.rmtree("#{skin_root}/zafu.compiled#{skin_path}") 
     168        elsif skin_name != 'default' && (File.stat(url).mtime < template.v_updated_at || RAILS_ENV == 'development') 
     169          FileUtils.rmtree("#{skin_root}/zafu.compiled#{skin_path}") 
     170        else 
     171          # we can use the cached version 
     172          return url 
     173        end 
     174      end 
     175     
     176      # render zafu 
     177      response.template.instance_variable_set(:@session, session) 
     178      skin_helper = response.template 
     179      res = ZafuParser.new_with_url(skin_path, :helper => skin_helper).render 
     180      FileUtils::mkpath(File.dirname(url)) unless File.exists?(File.dirname(url)) 
     181      File.open(url, "wb") { |f| f.syswrite(res) }     
     182     
     183      return url 
     184    end 
     185   
     186    # tested in MainControllerTest 
     187    def template_text_for_url(url) 
     188      url = url[1..-1] # strip leading '/' 
     189      url = url.split('/') 
     190      skin_name = url.shift 
     191      if skin_name == 'default' 
     192        path = File.join(SITES_ROOT, 'shared', 'zafu', 'default', *url) 
     193        if File.exists?(path) 
     194          File.read(path) 
     195        else 
     196          nil 
     197        end 
     198      else 
     199        if @skin_obj[:name] == skin_name 
     200          skin = @skin_obj 
     201        end 
     202        skin ||= secure(Skin) { Skin.find_by_name(skin_name) } 
     203        template = skin.template_for_path(url.join('/')) 
     204        template ? template.version.text : nil 
     205      end 
     206    rescue ActiveRecord::RecordNotFound 
     207      return nil 
     208    end 
     209 
     210    # TODO: implement 
     211    def template_url_for_asset(opts) 
     212     
     213      # 1. find in current skin ? 
     214      url = opts[:current_template][1..-1].split('/') + opts[:src].split('/') 
     215      url.compact! 
     216      skin_name = url.shift 
     217      if @skin_obj && @skin_obj[:name] == skin_name 
     218        skin = @skin_obj 
     219      end 
     220      skin ||= secure(Skin) { Skin.find_by_name(skin_name) } 
     221      asset = skin.asset_for_path(url.join('/'), Document) 
     222      asset ? node_url(asset) : nil 
     223    rescue ActiveRecord::RecordNotFound 
     224      return nil 
     225    end 
     226   
     227    # TODO: test 
     228    def save_erb_to_url(template, template_url) 
     229      path = fullpath_from_template_url(template_url) + ".erb" 
     230      FileUtils.mkpath(File.dirname(path)) unless File.exists?(File.dirname(path)) 
     231      File.open(path, "wb") { |f| f.syswrite(template) } 
     232      "" 
     233    end 
     234   
     235    # TODO: test 
     236    def fullpath_from_template_url(template_url=params[:template_url]) 
     237      if template_url =~ /\.\.|[^\w\._\/]/ 
     238        raise Zena::AccessViolation.new("'template_url' contains illegal characters : #{template_url.inspect}") 
     239      end 
     240     
     241      template_url = template_url[1..-1].split('/') 
     242      path = "/#{template_url[0]}/#{template_url[1]}/#{visitor.lang}/#{template_url[2..-1].join('/')}" 
     243 
     244      if template_url[0] == 'default' 
     245        "#{SITES_ROOT}/shared/zafu.compiled#{path}" 
     246      else 
     247        "#{SITES_ROOT}/#{visitor.site.host}/zafu.compiled#{path}" 
     248      end 
     249    end 
     250   
     251    # Verify that only logged in users access to some protected resources. This can be used to remove public access to an 
     252    # entire site. +authorize+ is called before any action in any controller. 
     253    def authorize 
     254      if (visitor.site[:authorize] || params[:prefix] == AUTHENTICATED_PREFIX) && ! session[:user] 
     255        flash[:notice] = trans "Please log in" 
     256        session[:after_login_url] = request.parameters 
     257        redirect_to :controller =>'login', :action=>'login' and return false 
     258      end 
     259    end 
     260   
     261    # Make sure everything is in sync, change current language, set @su warning color (tested in MainControllerTest) 
     262    def check_env 
     263      # Set connection charset. MySQL 4.0 doesn't support this so it 
     264      # will throw an error, MySQL 4.1 needs this 
     265      suppress(ActiveRecord::StatementInvalid) do 
     266        ActiveRecord::Base.connection.execute 'SET NAMES UTF8' 
     267      end 
     268     
     269      redirect_to not_found_path if params[:id] && params[:path] # make sure we do not mix 'pretty urls' with resources. 
     270     
     271      new_lang = nil 
     272      if params[:lang] 
     273        if visitor.site.lang_list.include?(params[:lang]) 
     274          new_lang = params[:lang] 
     275        else 
     276          new_lang = :bad_language 
     277        end 
     278      elsif params[:prefix] && params[:prefix] != AUTHENTICATED_PREFIX 
     279        if visitor.site.lang_list.include?(params[:prefix]) 
     280          session[:lang] = params[:prefix] 
     281        else 
     282          new_lang = :bad_language 
     283        end 
     284      end 
     285     
     286      if new_lang 
     287        if new_lang == :bad_language 
     288          flash[:notice] = trans "The requested language is not available." 
     289          session[:lang] ||= visitor.site[:default_lang] 
     290        else 
     291          session[:lang] = new_lang 
     292        end 
     293        req = request.parameters 
     294        req.delete(:lang) 
     295        req[:prefix] = session[:user] ? AUTHENTICATED_PREFIX : session[:lang] 
     296        redirect_to req and return false 
     297      end 
     298      # If the current user is su, make the CSS ugly so the user does not stay logged in as su. 
     299      if visitor.is_su? 
     300        @su=' style="background:#060;" ' 
     301      else 
     302        @su='' 
     303      end 
     304     
     305      # turn translation on/off 
     306      if params[:translate]  
     307        if visitor.group_ids.include?(visitor.site[:trans_group_id]) 
     308          if params[:translate] == 'on' 
     309            session[:translate] = true 
     310          else 
     311            session[:translate] = nil 
     312          end 
     313        end 
     314        req = request.parameters 
     315        req.delete(:translate) 
     316        redirect_to req and return false   
     317      end 
     318      visitor.lang = session[:lang] ||= (visitor.lang || visitor.site[:default_lang]) 
     319      true 
     320    end 
     321   
     322    # "Translate" static text into the current lang 
     323    def trans(keyword, edit=true) 
     324      TransPhrase[keyword][lang] 
     325    end 
     326   
     327    def set_encoding 
     328      headers['Content-Type'] ||= 'text/html' 
     329      if headers['Content-Type'].starts_with?('text/') and !headers['Content-Type'].include?('charset=') 
     330        headers['Content-Type'] += '; charset=utf-8' 
     331      end 
     332    end 
     333   
     334    # Parse date : return a date from a string 
     335    # TODO: test time_zone.. 
     336    def parse_date(datestr, fmt=trans('datetime')) 
     337      elements = datestr.split(/(\.|\-|\/|\s|:)+/) 
     338      format = fmt.split(/(\.|\-|\/|\s|:)+/) 
     339      if elements 
     340        hash = {} 
     341        elements.each_index do |i| 
     342          hash[format[i]] = elements[i] 
     343        end 
     344        hash['%Y'] ||= hash['%y'] ? (hash['%y'].to_i + 2000) : Time.now.year 
     345        hash['%H'] ||= 0 
     346        hash['%M'] ||= 0 
     347        hash['%S'] ||= 0 
     348        if hash['%Y'] && hash['%m'] && hash['%d'] 
     349          visitor.tz.unadjust(Time.gm(hash['%Y'], hash['%m'], hash['%d'], hash['%H'], hash['%M'], hash['%S'])) 
     350        else 
     351          nil 
     352        end 
    195353      else 
    196354        nil 
    197355      end 
    198     else 
    199       if @skin_obj[:name] == skin_name 
    200         skin = @skin_obj 
    201       end 
    202       skin ||= secure(Skin) { Skin.find_by_name(skin_name) } 
    203       template = skin.template_for_path(url.join('/')) 
    204       template ? template.version.text : nil 
    205     end 
    206   rescue ActiveRecord::RecordNotFound 
    207     return nil 
    208   end 
     356    end 
     357   
     358    def parse_dates(hash, fmt=trans('datetime')) 
     359      [:v_publish_from, :log_at, :event_at].each do |sym| 
     360        hash[sym] = parse_date(hash[sym], fmt) if hash[sym] && hash[sym].kind_of?(String) 
     361      end 
     362    end 
     363   
     364    # /////// The following methods are common to controllers and views //////////// # 
     365   
     366    def data_path(obj) 
     367      zen_path(obj, :format => obj.c_ext) 
     368    end 
     369   
     370    # Path for the node (as string). Options can be :format and :mode. 
     371    def zen_path(obj, opts={}) 
     372      opts = {:format => params[:format], :prefix => prefix}.merge(opts) 
     373      opts[:format] = 'html' if opts[:format].nil? || opts[:format] == '' 
     374      if obj[:id] == visitor.site[:root_id] && opts[:mode].nil? 
     375        "/#{opts[:prefix]}" # index page 
     376      elsif obj[:custom_base] 
     377        "/#{opts[:prefix]}/" + 
     378        obj.basepath + 
     379        (opts[:mode]    ? "_#{opts[:mode]}" : '') + 
     380        ".#{opts[:format]}" 
     381      else 
     382        "/#{opts[:prefix]}/" + 
     383        (obj.basepath != '' ? "#{obj.basepath}/"    : '') + 
     384        (obj.class.to_s.downcase               ) + 
     385        (obj[:zip].to_s                        ) + 
     386        (opts[:mode]    ? "_#{opts[:mode]}" : '') + 
     387        ".#{opts[:format]}" 
     388      end 
     389    end 
     390   
     391    def zen_url(obj, opts={}) 
     392      path = zen_path(obj,opts) 
     393      url_for(:url => path) 
     394    end 
    209395 
    210   # TODO: implement 
    211   def template_url_for_asset(opts) 
    212      
    213     # 1. find in current skin ? 
    214     url = opts[:current_template][1..-1].split('/') + opts[:src].split('/') 
    215     url.compact! 
    216     skin_name = url.shift 
    217     if @skin_obj && @skin_obj[:name] == skin_name 
    218       skin = @skin_obj 
    219     end 
    220     skin ||= secure(Skin) { Skin.find_by_name(skin_name) } 
    221     asset = skin.asset_for_path(url.join('/'), Document) 
    222     asset ? node_url(asset) : nil 
    223   rescue ActiveRecord::RecordNotFound 
    224     return nil 
    225   end 
    226    
    227   # TODO: test 
    228   def save_erb_to_url(template, template_url) 
    229     path = fullpath_from_template_url(template_url) + ".erb" 
    230     FileUtils.mkpath(File.dirname(path)) unless File.exists?(File.dirname(path)) 
    231     File.open(path, "wb") { |f| f.syswrite(template) } 
    232     "" 
    233   end 
    234    
    235   # TODO: test 
    236   def fullpath_from_template_url(template_url=params[:template_url]) 
    237     if template_url =~ /\.\.|[^\w\._\/]/ 
    238       raise Zena::AccessViolation.new("'template_url' contains illegal characters : #{template_url.inspect}") 
    239     end 
    240      
    241     template_url = template_url[1..-1].split('/') 
    242     path = "/#{template_url[0]}/#{template_url[1]}/#{visitor.lang}/#{template_url[2..-1].join('/')}" 
    243  
    244     if template_url[0] == 'default' 
    245       "#{SITES_ROOT}/shared/zafu.compiled#{path}" 
    246     else 
    247       "#{SITES_ROOT}/#{visitor.site.host}/zafu.compiled#{path}" 
    248     end 
    249   end 
    250    
    251   # Verify that only logged in users access to some protected resources. This can be used to remove public access to an 
    252   # entire site. +authorize+ is called before any action in any controller. 
    253   def authorize 
    254     if (visitor.site[:authorize] || params[:prefix] == AUTHENTICATED_PREFIX) && ! session[:user] 
    255       flash[:notice] = trans "Please log in" 
    256       session[:after_login_url] = request.parameters 
    257       redirect_to :controller =>'login', :action=>'login' and return false 
    258     end 
    259   end 
    260    
    261   # Make sure everything is in sync, change current language, set @su warning color (tested in MainControllerTest) 
    262   def check_env 
    263     # Set connection charset. MySQL 4.0 doesn't support this so it 
    264     # will throw an error, MySQL 4.1 needs this 
    265     suppress(ActiveRecord::StatementInvalid) do 
    266       ActiveRecord::Base.connection.execute 'SET NAMES UTF8' 
    267     end 
    268      
    269     redirect_to not_found_path if params[:id] && params[:path] # make sure we do not mix 'pretty urls' with resources. 
    270      
    271     new_lang = nil 
    272     if params[:lang] 
    273       if visitor.site.lang_list.include?(params[:lang]) 
    274         new_lang = params[:lang] 
    275       else 
    276         new_lang = :bad_language 
    277       end 
    278     elsif params[:prefix] && params[:prefix] != AUTHENTICATED_PREFIX 
    279       if visitor.site.lang_list.include?(params[:prefix]) 
    280         session[:lang] = params[:prefix] 
    281       else 
    282         new_lang = :bad_language 
    283       end 
    284     end 
    285      
    286     if new_lang 
    287       if new_lang == :bad_language 
    288         flash[:notice] = trans "The requested language is not available." 
    289         session[:lang] ||= visitor.site[:default_lang] 
    290       else 
    291         session[:lang] = new_lang 
    292       end 
    293       req = request.parameters 
    294       req.delete(:lang) 
    295       req[:prefix] = session[:user] ? AUTHENTICATED_PREFIX : session[:lang] 
    296       redirect_to req and return false 
    297     end 
    298     # If the current user is su, make the CSS ugly so the user does not stay logged in as su. 
    299     if visitor.is_su? 
    300       @su=' style="background:#060;" ' 
    301     else 
    302       @su='' 
    303     end 
    304      
    305     # turn translation on/off 
    306     if params[:translate]  
    307       if visitor.group_ids.include?(visitor.site[:trans_group_id]) 
    308         if params[:translate] == 'on' 
    309           session[:translate] = true 
    310         else 
    311           session[:translate] = nil 
    312         end 
    313       end 
    314       req = request.parameters 
    315       req.delete(:translate) 
    316       redirect_to req and return false   
    317     end 
    318     visitor.lang = session[:lang] ||= (visitor.lang || visitor.site[:default_lang]) 
    319     true 
    320   end 
    321    
    322   # "Translate" static text into the current lang 
    323   def trans(keyword, edit=true) 
    324     TransPhrase[keyword][lang] 
    325   end 
    326    
    327   def set_encoding 
    328     headers['Content-Type'] ||= 'text/html' 
    329     if headers['Content-Type'].starts_with?('text/') and !headers['Content-Type'].include?('charset=') 
    330       headers['Content-Type'] += '; charset=utf-8' 
    331     end 
    332   end 
    333    
    334   # Parse date : return a date from a string 
    335   # TODO: test time_zone.. 
    336   def parse_date(datestr, fmt=trans('datetime')) 
    337     elements = datestr.split(/(\.|\-|\/|\s|:)+/) 
    338     format = fmt.split(/(\.|\-|\/|\s|:)+/) 
    339     if elements 
    340       hash = {} 
    341       elements.each_index do |i| 
    342         hash[format[i]] = elements[i] 
    343       end 
    344       hash['%Y'] ||= hash['%y'] ? (hash['%y'].to_i + 2000) : Time.now.year 
    345       hash['%H'] ||= 0 
    346       hash['%M'] ||= 0 
    347       hash['%S'] ||= 0 
    348       if hash['%Y'] && hash['%m'] && hash['%d'] 
    349         visitor.tz.unadjust(Time.gm(hash['%Y'], hash['%m'], hash['%d'], hash['%H'], hash['%M'], hash['%S'])) 
    350       else 
    351         nil 
    352       end 
    353     else 
    354       nil 
    355     end 
    356   end 
    357    
    358   def parse_dates(hash, fmt=trans('datetime')) 
    359     [:v_publish_from, :log_at, :event_at].each do |sym| 
    360       hash[sym] = parse_date(hash[sym], fmt) if hash[sym] && hash[sym].kind_of?(String) 
    361     end 
    362   end 
    363    
    364   # /////// The following methods are common to controllers and views //////////// # 
    365    
    366   def data_path(obj) 
    367     zen_path(obj, :format => obj.c_ext) 
    368   end 
    369    
    370   # Path for the node (as string). Options can be :format and :mode. 
    371   def zen_path(obj, opts={}) 
    372     opts = {:format => params[:format], :prefix => prefix}.merge(opts) 
    373     opts[:format] = 'html' if opts[:format].nil? || opts[:format] == '' 
    374     if obj[:id] == visitor.site[:root_id] && opts[:mode].nil? 
    375       "/#{opts[:prefix]}" # index page 
    376     elsif obj[:custom_base] 
    377       "/#{opts[:prefix]}/" + 
    378       obj.basepath + 
    379       (opts[:mode]    ? "_#{opts[:mode]}" : '') + 
    380       ".#{opts[:format]}" 
    381     else 
    382       "/#{opts[:prefix]}/" + 
    383       (obj.basepath != '' ? "#{obj.basepath}/"    : '') + 
    384       (obj.class.to_s.downcase               ) + 
    385       (obj[:zip].to_s                        ) + 
    386       (opts[:mode]    ? "_#{opts[:mode]}" : '') + 
    387       ".#{opts[:format]}" 
    388     end 
    389   end 
    390    
    391   def zen_url(obj, opts={}) 
    392     path = zen_path(obj,opts) 
    393     url_for(:url => path) 
    394   end 
    395  
    396   def prefix 
    397     if visitor.is_anon? 
    398       if visitor.site[:monolingual] 
    399         '' 
    400       else 
    401         lang 
    402       end 
    403     else 
    404       AUTHENTICATED_PREFIX 
    405     end 
    406   end 
    407    
    408   # Restrict access some actions to administrators (used as a before_filter) 
    409   def check_is_admin 
    410     render_404(ActiveRecord::RecordNotFound) unless visitor.is_admin? 
    411     @admin = true 
    412   end 
    413    
    414   # Notes finder options are 
    415   # [from] node providing the notes. If omitted, <code>@project</code> or <code>@node.project</code> is used. 
    416   # [find] method called on the source. Default is 'notes'. For example, <code>:from=>@node.project, :find=>:notes</code> finds all notes from the project of the current node. 
    417   # [date] only find notes for the given date 
    418   # [using] specify the field used to sort and filter by date. By default, 'log_at' is used 
    419   # [order] sort order. By default "#{using} ASC" is used. 
    420   # [] 
    421   def notes(options={}) 
    422     source = options[:from] || (@project ||= (@node ? @node.project : nil)) 
    423     return [] unless source 
    424      
    425     options.delete(:from) 
     396    def prefix 
     397      if visitor.is_anon? 
     398        if visitor.site[:monolingual] 
     399          '' 
     400        else 
     401          lang 
     402        end 
     403      else 
     404        AUTHENTICATED_PREFIX 
     405      end 
     406    end 
     407   
     408    # Restrict access some actions to administrators (used as a before_filter) 
     409    def check_is_admin 
     410      render_404(ActiveRecord::RecordNotFound) unless visitor.is_admin? 
     411      @admin = true 
     412    end 
     413   
     414    # Notes finder options are 
     415    # [from] node providing the notes. If omitted, <code>@project</code> or <code>@node.project</code> is used. 
     416    # [find] method called on the source. Default is 'notes'. For example, <code>:from=>@node.project, :find=>:notes</code> finds all notes from the project of the current node. 
     417    # [date] only find notes for the given date 
     418    # [using] specify the field used to sort and filter by date. By default, 'log_at' is used 
     419    # [order] sort order. By default "#{using} ASC" is used. 
     420    # [] 
     421    def notes(options={}) 
     422      source = options[:from] || (@project ||= (@node ? @node.project : nil)) 
     423      return [] unless source 
     424     
     425      options.delete(:from) 
    426426       
    427     method = options[:find] || :notes 
    428     options.delete(:find) 
    429      
    430     field = options[:using] || :log_at 
    431     options.delete(:using) 
    432      
    433     options[:order] ||= "#{field} ASC" 
    434     options.delete(:using) 
    435      
    436     if date = options[:date] 
    437       options.delete(:date) 
    438       options.merge!(:conditions=>["date(#{field}) = ?", date]) 
    439     end 
    440      
    441     source.send(method, options) 
    442   end 
    443    
    444   #TODO: test 
    445   def error_messages_for(obj_name) 
    446     obj = instance_variable_get("@#{obj_name}") 
    447     return '' unless obj && !obj.errors.empty? 
    448     res = ["<ul>"] 
    449     obj.errors.each do |er,msg| 
    450       res << "<li><b>#{er}</b> #{trans(msg)}</li>" 
    451     end 
    452     res << '</ul>' 
    453     res.join("\n") 
    454   end 
    455    
    456   # TODO: test 
    457   def add_error(msg) 
    458     @errors ||= [] 
    459     @errors << trans(msg) 
    460   end 
    461    
    462   # TODO: test 
    463   def render_errors(errs=@errors) 
    464     if !errs || errs.empty? 
    465       "" 
    466     else 
    467       "<ul><li>#{errs.join("</li>\n<li>")}</li></ul>" 
    468     end 
    469   end 
    470    
    471   # Find the proper layout to render 'admin' actions. The layout is searched into the visitor's contact's skin first 
    472   # and then into default. This action is also responsible for setting a default @title_for_layout. 
    473   def admin_layout 
    474     puts "ADMIN_LAYOUT #{@node[:zip]}" 
    475     @title_for_layout ||= "#{params[:controller]}/#{params[:action]}" 
    476     template_url(:mode=>'admin_layout') 
    477   end 
    478    
    479   # TODO: test 
    480   def popup_layout 
    481     template_url(:mode=>'popup_layout') 
    482   end 
     427      method = options[:find] || :notes 
     428      options.delete(:find) 
     429     
     430      field = options[:using] || :log_at 
     431      options.delete(:using) 
     432     
     433      options[:order] ||= "#{field} ASC" 
     434      options.delete(:using) 
     435     
     436      if date = options[:date] 
     437        options.delete(:date) 
     438        options.merge!(:conditions=>["date(#{field}) = ?", date]) 
     439      end 
     440     
     441      source.send(method, options) 
     442    end 
     443   
     444    #TODO: test 
     445    def error_messages_for(obj_name) 
     446      obj = instance_variable_get("@#{obj_name}") 
     447      return '' unless obj && !obj.errors.empty? 
     448      res = ["<ul>"] 
     449      obj.errors.each do |er,msg| 
     450        res << "<li><b>#{er}</b> #{trans(msg)}</li>" 
     451      end 
     452      res << '</ul>' 
     453      res.join("\n") 
     454    end 
     455   
     456    # TODO: test 
     457    def add_error(msg) 
     458      @errors ||= [] 
     459      @errors << trans(msg) 
     460    end 
     461   
     462    # TODO: test 
     463    def render_errors(errs=@errors) 
     464      if !errs || errs.empty? 
     465        "" 
     466      else 
     467        "<ul><li>#{errs.join("</li>\n<li>")}</li></ul>" 
     468      end 
     469    end 
     470   
     471    # Find the proper layout to render 'admin' actions. The layout is searched into the visitor's contact's skin first 
     472    # and then into default. This action is also responsible for setting a default @title_for_layout. 
     473    def admin_layout 
     474      puts "ADMIN_LAYOUT #{@node[:zip]}" 
     475      @title_for_layout ||= "#{params[:controller]}/#{params[:action]}" 
     476      template_url(:mode=>'admin_layout') 
     477    end 
     478   
     479    # TODO: test 
     480    def popup_layout 
     481      template_url(:mode=>'popup_layout') 
     482    end 
    483483end 
  • branches/rest/app/controllers/nodes_controller.rb

    r423 r425  
    185185     
    186186    @node.update_attributes(attrs) 
    187     if @node.save 
     187    if @node.errors.empty? 
    188188      flash.now[:notice] = trans('node updated') 
    189189    else 
  • branches/rest/app/controllers/versions_controller.rb

    r423 r425  
    4646  def preview 
    4747    @preview_id = session[:preview_id] 
    48     if params[:node] 
     48    if @key = (params['key'] || params['amp;key']) 
     49      @value = params[:content] 
    4950      # redaction 
    50       @node.attributes = params[:node] 
    5151    else 
    5252      # drive view 
  • branches/rest/app/models/image.rb

    r392 r425  
    2121An image can be croped by changing the 'crop' pseudo attribute (see Image#crop= ) : 
    2222 
    23   @node.update_attributes(:crop=>{:x=>10, :y=>10, :width=>30, :height=>60}) 
     23  @node.update_attributes(:c_crop=>{:x=>10, :y=>10, :width=>30, :height=>60}) 
    2424 
    2525=== Version 
     
    6464  #   @node.crop = {:x=>10, :y=>10, :width=>30, :height=>60} 
    6565  # Be carefull as this method changes the current file. So you should make a backup version before croping the image (the popup editor displays a warning). 
    66   def c_crop=(crop
    67     x, y, w, h = crop[:x].to_i, crop[:y].to_i, crop[:w].to_i, crop[:h].to_i 
     66  def c_crop=(format
     67    x, y, w, h = format[:x].to_i, format[:y].to_i, format[:w].to_i, format[:h].to_i 
    6868    if (x >= 0 && y >= 0 && w <= c_width && h <= c_height) && !(x==0 && y==0 && w == c_width && h == c_height) 
    6969      # do crop 
    70       self.c_file = version.content.crop(crop
     70      self.c_file = version.content.crop(format
    7171    else 
    7272      # nothing to do: ignore this operation. 
  • branches/rest/app/models/image_content.rb

    r392 r425  
    1717   
    1818  # Return a cropped image using the 'crop' hash with the top left corner position (:x, :y) and the width and height (:width, :heigt). 
    19   def crop(crop
     19  def crop(format
    2020    return if @file # we do not want to crop on file upload in case the crop params lie around in the user's form 
    21     x, y, w, h = crop[:x].to_i, crop[:y].to_i, crop[:w].to_i, crop[:h].to_i 
     21    x, y, w, h = format[:x].to_i, format[:y].to_i, format[:w].to_i, format[:h].to_i 
    2222 
    2323    # crop image