require 'parameter_keeper' class PatchesController < ApplicationController PATCHES_PER_PAGE = 20 user_actions :add_item, :new_patch_version, :update, :destroy ajax_updater_for :patch, :title ajax_updater_for :patch, :description, :render => lambda {|p| {:inline => "<%= textilize_without_paragraph(@patch.description) %>"}} ajax_updater_for :patch, :program_id, :render => lambda {|p| {:inline => "<%= link_to(@patch.program.name, @patch.program.url) %>"}} def update_patch_applied_since_version @patch = Patch.find(params[:id]) @patch.applied_since_version = params[:patch][:applied_since_version] if @patch.applied_since_version @patch.applied_at = Time.now @patch.applied_by = current_user.id else @patch.applied_at = @patch.applied_by = nil end @patch.save render :text => "" end # Mark as (logged-in) user actions all the ajax_updater_for related ones user_actions :update_patch_title, :update_patch_description, :update_patch_program_id, :update_patch_applied_since_version def index @n_patches = Patch.count @n_unapplied_patches = Patch.count(Patch::UNAPPLIED_CONDITION) @n_programs = Program.count @latest_unapplied_patches = Patch.find(:all, :conditions => Patch::UNAPPLIED_CONDITION, :order => "created_at DESC", :limit => 5) end # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) verify :method => :post, :only => [ :destroy, :create, :update ], :redirect_to => { :action => :list } def list @paginator, @patches = paginate_patches end def my_patches @paginator, @my_patches = paginate_my_patches end def unapplied @paginator, @unapplied_patches = paginate_unapplied_patches end def show edit render :action => 'edit' end def edit @patch = Patch.find(params[:id]) if params[:patch_extra] and params[:patch_extra][:version] @patch_version = PatchVersion.find(params[:patch_extra][:version]) else @patch_version = @patch.latest_version end end def update @patch = Patch.find(params[:id]) if @patch.update_attributes(params[:patch].merge(update_info)) flash[:notice] = 'Patch was successfully updated.' redirect_to :action => 'show', :id => @patch else render :action => 'edit' end end def destroy item = Patch.find(params[:id]) patch_versions = item.patch_versions # Save before destroying item if item.destroy patch_versions.each do |pv| begin File.unlink(File.join(PATCH_REPOSITORY, pv.id.to_s)) rescue Errno::ENOENT # Ignore non-existent files end pv.destroy end redirect_to(:action => "list") else render_text "Couldn't destroy item" end end # Non-scaffold methods def add_item # Uploaded file contents patch_contents = params[:patch_file].readlines.join('') if params[:new_item][:title] == '' or patch_contents == '' flash[:error] = "Can't add patch without title or contents" redirect_to(:action => "list") return end # First, insert the application if it's a new one if params[:new_item][:program_id].to_i == NEW_APPLICATION_ID @new_item = Patch.new(params[:new_item]) @new_item_version = ParameterKeeper.new(params[:new_item_version]) # If there is an application with the same name, abort if Program.find(:first, :conditions => ["name ILIKE :new_app_name", {:new_app_name => params[:new_app][:name]}]) flash[:error] = "Can't create two applications with the same name" @new_item.program_id = NEW_APPLICATION_ID @new_app = ParameterKeeper.new(params[:new_app]) @patch_file = ParameterKeeper.new(params[:new_app]) index render :action => 'index' return end app = Program.new(params[:new_app]) if app.save params[:new_item][:program_id] = app.id else flash[:error] = "Couldn't insert new application" redirect_to(:action => "list") return end end # Is it an update for an existent patch? version = nil if params[:patch] and params[:patch][:id] and Patch.exists? params[:patch][:id] item = Patch.find(params[:patch][:id]) # Use patch title as the patch version comment basic_version_attrs = {:comment => params[:new_item][:title]}.merge(params[:new_item_version]) version = item.patch_versions.build(basic_version_attrs) else item = Patch.new({:description => params[:new_item][:title], :filename => params[:patch_file].original_filename}.merge(params[:new_item]).merge(create_info)) if item.save version = item.patch_versions.build({:comment => 'Initial version'}.merge(params[:new_item_version])) else flash[:error] = "Couldn't add new patch" redirect_to(:action => "list") return end end # Take care of the new patch version, in either case version.text = patch_contents unless version.save item.destroy if item.patch_versions.empty? flash[:error] = "Couldn't create new patch version" redirect_to(:action => "list") return end Notifier::deliver_new_patch(item) redirect_to(:action => 'show', :id => item.id) end def new_patch_version patch = Patch.find(params[:id]) patch_contents = params[:patch_file].readlines.join('') if patch_contents == '' flash[:error] = "Can't update patch without file" redirect_to(:action => "list") return end if patch version = patch.patch_versions.build(params[:version].merge(create_info)) version.text = patch_contents unless version.save flash[:error] = "Can't create new patch version" redirect_to(:action => "show", :id => patch) return end end redirect_to(:action => "show", :id => patch) end def view_patch begin @patch_version = PatchVersion.find(params[:id]) if params[:download].to_s != '' @headers["Content-Type"] = "text/plain" @headers["Content-Disposition"] = "attachment; filename=#{@patch_version.patch.filename}" render :text => @patch_version.text end rescue Errno::ENOENT render :text => "Sorry, couldn't retrieve patch number #{params[:id]}" end # Don't use the standard HTML menus and stuff @only_basic_html = true end def view_versions @item = Patch.find(params[:id]) if @item.has_patch_versions? @versions = @item.patch_versions else render_text 'No versions available' end end def search if request.get? @patches = [] @n_results = 0 else conditions, vars = [], {} # Sanitize parameters params[:patch] ||= {} params[:patch_extra] ||= {} params[:patch_version] ||= {} # Program and for_upstream_version if params[:patch][:program_id].to_s != '' conditions << "program_id = :program_id" vars[:program_id] = params[:patch][:program_id] if params[:patch_version][:for_upstream_version].to_s != '' conditions << "patches.id IN (SELECT patch_id FROM patch_versions WHERE for_upstream_version = :f_u_v)" vars[:f_u_v] = params[:patch_version][:for_upstream_version] end end # Title/description if params[:patch_extra][:description].to_s != '' idx = 0 conditions += params[:patch_extra][:description].split(/\s+/).map do |word| idx += 1 vars[:"w#{idx}"] = "%#{word}%" "(title ILIKE :w#{idx} OR description ILIKE :w#{idx})" end end # User/author if params[:patch_extra][:author].to_s != '' conditions << "created_by = :author_id" vars[:author_id] = params[:patch_extra][:author] end # Applied? case params[:patch_extra][:applied] when '1' conditions << Patch::APPLIED_CONDITION when '0' conditions << Patch::UNAPPLIED_CONDITION end # Grrrr.... if conditions.empty? conditions << "1 = 1" end session[:search_opts] = {:conditions => [conditions.join(" AND "), vars], :order => "program_name"} @paginator, @patches = paginate_patch_search(session[:search_opts]) @n_results = PatchView.count(session[:search_opts][:conditions]) @patch_upstream_versions = [['Any', '']] + upstream_versions_for_program(params[:patch][:program_id].to_i) # Small hacks to keep search options params[:patch][:program_id] = params[:patch][:program_id].to_i @patch_extra = ParameterKeeper.new(params[:patch_extra]) @patch_version = ParameterKeeper.new(params[:patch_version]) @patch = ParameterKeeper.new(params[:patch]) end end def search_results_archive # Use the search options saved in the session object send_patches Patch.find(:all, :conditions => session[:search_opts][:conditions]) end def patches_rss @headers["Content-Type"] = "application/xml" @patches = PatchView.find(:all, :order => 'created_at DESC') @title = 'PatchServer patches' @description = 'New patches uploaded to PatchServer' render :template => 'shared/patches_rss', :layout => false end def ajax_get_patches @paginator, @patches = paginate_patches render :inline => < :ajax_get_patches, :paginator => @paginator, :columns => 6) %> EOD end def ajax_get_my_patches @paginator, @patches = paginate_my_patches render :inline => < :ajax_get_my_patches, :paginator => @paginator, :columns => 6) %> EOD end def ajax_get_unapplied_patches @paginator, @patches = paginate_unapplied_patches render :inline => < :ajax_get_unapplied_patches, :paginator => @paginator, :columns => 5) %> EOD end def ajax_get_patch_info if Patch.exists? params[:patch][:id] p = Patch.find(params[:patch][:id]) render :text => "Patch \##{p.id} (#{p.title})" else render :text => "No such patch '#{params[:patch][:id]}'" end end def ajax_get_upstream_versions @versions = upstream_versions_for_program(params[:program][:id].to_i) render :inline => < EOD end def ajax_get_patch_search @paginator, @patches = paginate_patch_search(session[:search_opts]) render :inline => < :ajax_get_patch_search, :paginator => @paginator, :columns => 6) %> EOD end def preferences redirect_to :controller => '/' if current_user.nil? @prefs = current_user.prefs_hash end def save_preferences params[:pref].each_pair do |k,v| u = UserPref.find_or_create_by_user_id_and_name(current_user.id, k) u.value = v u.save end flash[:notice] = 'Preferences saved' redirect_to :action => 'preferences' end def add_comment @patch = Patch.find(params[:id]) @patch.patch_comments.build(params[:comment]) unless @patch.save flash[:error] = "Couldn't store comment" end redirect_to :action => 'edit', :id => @patch.id end protected def paginate_patches(user_options={}) options = {:per_page => PATCHES_PER_PAGE, :order => 'program_name, applied_since_version DESC, for_upstream_version DESC'}.merge(user_options) @paginator, @patches = paginate :patch_views, options end def paginate_my_patches(user_options={}) options = {:per_page => PATCHES_PER_PAGE, :conditions => ["created_by = :author_id", {:author_id => current_user.id}], :order => '(applied_since_version IS NULL) DESC, program_name, applied_at, for_upstream_version DESC'}.merge(user_options) @paginator, @patches = paginate :patch_views, options end def paginate_unapplied_patches(user_options={}) options = {:per_page => PATCHES_PER_PAGE, :conditions => PatchView::UNAPPLIED_CONDITION, :order => 'program_name, applied_since_version DESC, for_upstream_version DESC'}.merge(user_options) @paginator, @patches = paginate :patch_views, options end def paginate_patch_search(user_options) options = {:per_page => PATCHES_PER_PAGE, :order => 'program_name, applied_since_version DESC, for_upstream_version DESC'}.merge(user_options) @paginator, @patches = paginate :patch_views, options end def upstream_versions_for_program(id) con = ActiveRecord::Base.connection con.select_values("SELECT DISTINCT pv.for_upstream_version FROM patches p JOIN patch_versions pv ON pv.patch_id = p.id WHERE p.program_id = #{con.quote(id)} ORDER BY pv.for_upstream_version DESC") end def create_info {:created_by => current_user.nil? ? 0 : current_user.id} end def update_info {:updated_by => current_user.nil? ? 0 : current_user.id} end end