Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5ac5000e5 |
@@ -86,7 +86,6 @@ class AdminController < ApplicationController
|
||||
@flags = {
|
||||
:default_admin_changed => User.find(:first, :conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?,
|
||||
:file_repository_writable => File.writable?(Attachment.storage_path),
|
||||
:plugin_assets_writable => File.writable?(Engines.public_directory),
|
||||
:rmagick_available => Object.const_defined?(:Magick)
|
||||
}
|
||||
end
|
||||
|
||||
@@ -175,7 +175,6 @@ class ApplicationController < ActionController::Base
|
||||
# TODO: move to model
|
||||
def attach_files(obj, attachments)
|
||||
attached = []
|
||||
unsaved = []
|
||||
if attachments && attachments.is_a?(Hash)
|
||||
attachments.each_value do |attachment|
|
||||
file = attachment['file']
|
||||
@@ -184,10 +183,7 @@ class ApplicationController < ActionController::Base
|
||||
:file => file,
|
||||
:description => attachment['description'].to_s.strip,
|
||||
:author => User.current)
|
||||
a.new_record? ? (unsaved << a) : (attached << a)
|
||||
end
|
||||
if unsaved.any?
|
||||
flash[:warning] = l(:warning_attachments_not_saved, unsaved.size)
|
||||
attached << a unless a.new_record?
|
||||
end
|
||||
end
|
||||
attached
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2008 Jean-Philippe Lang
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -17,11 +17,7 @@
|
||||
|
||||
class AttachmentsController < ApplicationController
|
||||
before_filter :find_project
|
||||
before_filter :read_authorize, :except => :destroy
|
||||
before_filter :delete_authorize, :only => :destroy
|
||||
|
||||
verify :method => :post, :only => :destroy
|
||||
|
||||
|
||||
def show
|
||||
if @attachment.is_diff?
|
||||
@diff = File.new(@attachment.diskfile, "rb").read
|
||||
@@ -35,40 +31,25 @@ class AttachmentsController < ApplicationController
|
||||
end
|
||||
|
||||
def download
|
||||
if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
|
||||
@attachment.increment_download
|
||||
end
|
||||
@attachment.increment_download if @attachment.container.is_a?(Version)
|
||||
|
||||
# images are sent inline
|
||||
send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
|
||||
:type => @attachment.content_type,
|
||||
:disposition => (@attachment.image? ? 'inline' : 'attachment')
|
||||
|
||||
end
|
||||
|
||||
def destroy
|
||||
# Make sure association callbacks are called
|
||||
@attachment.container.attachments.delete(@attachment)
|
||||
redirect_to :back
|
||||
rescue ::ActionController::RedirectBackError
|
||||
redirect_to :controller => 'projects', :action => 'show', :id => @project
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def find_project
|
||||
@attachment = Attachment.find(params[:id])
|
||||
# Show 404 if the filename in the url is wrong
|
||||
raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename
|
||||
|
||||
@project = @attachment.project
|
||||
permission = @attachment.container.is_a?(Version) ? :view_files : "view_#{@attachment.container.class.name.underscore.pluralize}".to_sym
|
||||
allowed = User.current.allowed_to?(permission, @project)
|
||||
allowed ? true : (User.current.logged? ? render_403 : require_login)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
|
||||
def read_authorize
|
||||
@attachment.visible? ? true : deny_access
|
||||
end
|
||||
|
||||
def delete_authorize
|
||||
@attachment.deletable? ? true : deny_access
|
||||
end
|
||||
end
|
||||
|
||||
@@ -71,6 +71,11 @@ class DocumentsController < ApplicationController
|
||||
Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('document_added')
|
||||
redirect_to :action => 'show', :id => @document
|
||||
end
|
||||
|
||||
def destroy_attachment
|
||||
@document.attachments.find(params[:attachment_id]).destroy
|
||||
redirect_to :action => 'show', :id => @document
|
||||
end
|
||||
|
||||
private
|
||||
def find_project
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
class IssuesController < ApplicationController
|
||||
menu_item :new_issue, :only => :new
|
||||
|
||||
before_filter :find_issue, :only => [:show, :edit, :reply]
|
||||
before_filter :find_issue, :only => [:show, :edit, :reply, :destroy_attachment]
|
||||
before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
|
||||
before_filter :find_project, :only => [:new, :update_form, :preview]
|
||||
before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :update_form, :context_menu]
|
||||
@@ -30,6 +30,8 @@ class IssuesController < ApplicationController
|
||||
include ProjectsHelper
|
||||
helper :custom_fields
|
||||
include CustomFieldsHelper
|
||||
helper :ifpdf
|
||||
include IfpdfHelper
|
||||
helper :issue_relations
|
||||
include IssueRelationsHelper
|
||||
helper :watchers
|
||||
@@ -41,7 +43,6 @@ class IssuesController < ApplicationController
|
||||
include SortHelper
|
||||
include IssuesHelper
|
||||
helper :timelog
|
||||
include Redmine::Export::PDF
|
||||
|
||||
def index
|
||||
retrieve_query
|
||||
@@ -67,7 +68,7 @@ class IssuesController < ApplicationController
|
||||
format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
|
||||
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
|
||||
format.csv { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
|
||||
format.pdf { send_data(issues_to_pdf(@issues, @project), :type => 'application/pdf', :filename => 'export.pdf') }
|
||||
format.pdf { send_data(render(:template => 'issues/index.rfpdf', :layout => false), :type => 'application/pdf', :filename => 'export.pdf') }
|
||||
end
|
||||
else
|
||||
# Send html if the query is not valid
|
||||
@@ -105,7 +106,7 @@ class IssuesController < ApplicationController
|
||||
respond_to do |format|
|
||||
format.html { render :template => 'issues/show.rhtml' }
|
||||
format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
|
||||
format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
|
||||
format.pdf { send_data(render(:template => 'issues/show.rfpdf', :layout => false), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -122,10 +123,7 @@ class IssuesController < ApplicationController
|
||||
render :nothing => true, :layout => true
|
||||
return
|
||||
end
|
||||
if params[:issue].is_a?(Hash)
|
||||
@issue.attributes = params[:issue]
|
||||
@issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
|
||||
end
|
||||
@issue.attributes = params[:issue]
|
||||
@issue.author = User.current
|
||||
|
||||
default_status = IssueStatus.default
|
||||
@@ -147,8 +145,7 @@ class IssuesController < ApplicationController
|
||||
attach_files(@issue, params[:attachments])
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
|
||||
redirect_to(params[:continue] ? { :action => 'new', :tracker_id => @issue.tracker } :
|
||||
{ :action => 'show', :id => @issue })
|
||||
redirect_to :controller => 'issues', :action => 'show', :id => @issue
|
||||
return
|
||||
end
|
||||
end
|
||||
@@ -186,7 +183,7 @@ class IssuesController < ApplicationController
|
||||
|
||||
if (@time_entry.hours.nil? || @time_entry.valid?) && @issue.save
|
||||
# Log spend time
|
||||
if User.current.allowed_to?(:log_time, @project)
|
||||
if current_role.allowed_to?(:log_time)
|
||||
@time_entry.save
|
||||
end
|
||||
if !journal.new_record?
|
||||
@@ -318,6 +315,17 @@ class IssuesController < ApplicationController
|
||||
@issues.each(&:destroy)
|
||||
redirect_to :action => 'index', :project_id => @project
|
||||
end
|
||||
|
||||
def destroy_attachment
|
||||
a = @issue.attachments.find(params[:attachment_id])
|
||||
a.destroy
|
||||
journal = @issue.init_journal(User.current)
|
||||
journal.details << JournalDetail.new(:property => 'attachment',
|
||||
:prop_key => a.id,
|
||||
:old_value => a.filename)
|
||||
journal.save
|
||||
redirect_to :action => 'show', :id => @issue
|
||||
end
|
||||
|
||||
def gantt
|
||||
@gantt = Redmine::Helpers::Gantt.new(params)
|
||||
@@ -346,7 +354,7 @@ class IssuesController < ApplicationController
|
||||
respond_to do |format|
|
||||
format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
|
||||
format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.identifier}-gantt.png") } if @gantt.respond_to?('to_image')
|
||||
format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{@project.nil? ? '' : "#{@project.identifier}-" }gantt.pdf") }
|
||||
format.pdf { send_data(render(:template => "issues/gantt.rfpdf", :layout => false), :type => 'application/pdf', :filename => "#{@project.nil? ? '' : "#{@project.identifier}-" }gantt.pdf") }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ class ProjectsController < ApplicationController
|
||||
include SortHelper
|
||||
helper :custom_fields
|
||||
include CustomFieldsHelper
|
||||
helper :ifpdf
|
||||
include IfpdfHelper
|
||||
helper :issues
|
||||
helper IssuesHelper
|
||||
helper :queries
|
||||
@@ -82,11 +84,6 @@ class ProjectsController < ApplicationController
|
||||
|
||||
# Show @project
|
||||
def show
|
||||
if params[:jump]
|
||||
# try to redirect to the requested menu item
|
||||
redirect_to_project_menu_item(@project, params[:jump]) && return
|
||||
end
|
||||
|
||||
@members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
|
||||
@subprojects = @project.children.find(:all, :conditions => Project.visible_by(User.current))
|
||||
@news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
|
||||
@@ -191,13 +188,10 @@ class ProjectsController < ApplicationController
|
||||
|
||||
def add_file
|
||||
if request.post?
|
||||
container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
|
||||
attachments = attach_files(container, params[:attachments])
|
||||
if !attachments.empty? && Setting.notified_events.include?('file_added')
|
||||
Mailer.deliver_attachments_added(attachments)
|
||||
end
|
||||
@version = @project.versions.find_by_id(params[:version_id])
|
||||
attachments = attach_files(@version, params[:attachments])
|
||||
Mailer.deliver_attachments_added(attachments) if !attachments.empty? && Setting.notified_events.include?('file_added')
|
||||
redirect_to :controller => 'projects', :action => 'list_files', :id => @project
|
||||
return
|
||||
end
|
||||
@versions = @project.versions.sort
|
||||
end
|
||||
@@ -209,8 +203,7 @@ class ProjectsController < ApplicationController
|
||||
'size' => "#{Attachment.table_name}.filesize",
|
||||
'downloads' => "#{Attachment.table_name}.downloads"
|
||||
|
||||
@containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)]
|
||||
@containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
|
||||
@versions = @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
|
||||
render :layout => !request.xhr?
|
||||
end
|
||||
|
||||
|
||||
@@ -127,9 +127,6 @@ class RepositoriesController < ApplicationController
|
||||
end
|
||||
|
||||
def annotate
|
||||
@entry = @repository.entry(@path, @rev)
|
||||
show_error_not_found and return unless @entry
|
||||
|
||||
@annotate = @repository.scm.annotate(@path, @rev)
|
||||
render_error l(:error_scm_annotate) and return if @annotate.nil? || @annotate.empty?
|
||||
end
|
||||
|
||||
@@ -40,7 +40,7 @@ class SettingsController < ApplicationController
|
||||
@options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
|
||||
@deliveries = ActionMailer::Base.perform_deliveries
|
||||
|
||||
@guessed_host_and_path = request.host_with_port.dup
|
||||
@guessed_host_and_path = request.host_with_port
|
||||
@guessed_host_and_path << ('/'+ request.relative_url_root.gsub(%r{^\/}, '')) unless request.relative_url_root.blank?
|
||||
end
|
||||
|
||||
|
||||
@@ -75,11 +75,7 @@ class UsersController < ApplicationController
|
||||
@user.admin = params[:user][:admin] if params[:user][:admin]
|
||||
@user.login = params[:user][:login] if params[:user][:login]
|
||||
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id
|
||||
@user.attributes = params[:user]
|
||||
# Was the account actived ? (do it before User#save clears the change)
|
||||
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
|
||||
if @user.save
|
||||
Mailer.deliver_account_activated(@user) if was_activated
|
||||
if @user.update_attributes(params[:user])
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
# Give a string to redirect_to otherwise it would use status param as the response code
|
||||
redirect_to(url_for(:action => 'list', :status => params[:status], :page => params[:page]))
|
||||
|
||||
@@ -37,6 +37,12 @@ class VersionsController < ApplicationController
|
||||
redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
|
||||
end
|
||||
|
||||
def destroy_file
|
||||
@version.attachments.find(params[:attachment_id]).destroy
|
||||
flash[:notice] = l(:notice_successful_delete)
|
||||
redirect_to :controller => 'projects', :action => 'list_files', :id => @project
|
||||
end
|
||||
|
||||
def status_by
|
||||
respond_to do |format|
|
||||
format.html { render :action => 'show' }
|
||||
|
||||
@@ -21,7 +21,7 @@ class WikiController < ApplicationController
|
||||
before_filter :find_wiki, :authorize
|
||||
before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
|
||||
|
||||
verify :method => :post, :only => [:destroy, :protect], :redirect_to => { :action => :index }
|
||||
verify :method => :post, :only => [:destroy, :destroy_attachment, :protect], :redirect_to => { :action => :index }
|
||||
|
||||
helper :attachments
|
||||
include AttachmentsHelper
|
||||
@@ -181,6 +181,13 @@ class WikiController < ApplicationController
|
||||
redirect_to :action => 'index', :page => @page.title
|
||||
end
|
||||
|
||||
def destroy_attachment
|
||||
@page = @wiki.find_page(params[:page])
|
||||
return render_403 unless editable?
|
||||
@page.attachments.find(params[:attachment_id]).destroy
|
||||
redirect_to :action => 'index', :page => @page.title
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_wiki
|
||||
|
||||
@@ -147,15 +147,6 @@ module ApplicationHelper
|
||||
end
|
||||
content
|
||||
end
|
||||
|
||||
# Renders flash messages
|
||||
def render_flash_messages
|
||||
s = ''
|
||||
flash.each do |k,v|
|
||||
s << content_tag('div', v, :class => "flash #{k}")
|
||||
end
|
||||
s
|
||||
end
|
||||
|
||||
# Truncates and returns the string as a single line
|
||||
def truncate_single_line(string, *args)
|
||||
@@ -291,15 +282,16 @@ module ApplicationHelper
|
||||
attachments = attachments.sort_by(&:created_on).reverse
|
||||
text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
|
||||
style = $1
|
||||
filename = $6.downcase
|
||||
filename = $6
|
||||
rf = Regexp.new(Regexp.escape(filename), Regexp::IGNORECASE)
|
||||
# search for the picture in attachments
|
||||
if found = attachments.detect { |att| att.filename.downcase == filename }
|
||||
if found = attachments.detect { |att| att.filename =~ rf }
|
||||
image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
|
||||
desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
|
||||
alt = desc.blank? ? nil : "(#{desc})"
|
||||
"!#{style}#{image_url}#{alt}!"
|
||||
else
|
||||
m
|
||||
"!#{style}#{filename}!"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -487,11 +479,11 @@ module ApplicationHelper
|
||||
full_messages = []
|
||||
object.errors.each do |attr, msg|
|
||||
next if msg.nil?
|
||||
msg = [msg] unless msg.is_a?(Array)
|
||||
msg = msg.first if msg.is_a? Array
|
||||
if attr == "base"
|
||||
full_messages << l(*msg)
|
||||
full_messages << l(msg)
|
||||
else
|
||||
full_messages << "« " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " » " + l(*msg) unless attr == "custom_values"
|
||||
full_messages << "« " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " » " + l(msg) unless attr == "custom_values"
|
||||
end
|
||||
end
|
||||
# retrieve custom values error messages
|
||||
@@ -499,8 +491,8 @@ module ApplicationHelper
|
||||
object.custom_values.each do |v|
|
||||
v.errors.each do |attr, msg|
|
||||
next if msg.nil?
|
||||
msg = [msg] unless msg.is_a?(Array)
|
||||
full_messages << "« " + v.custom_field.name + " » " + l(*msg)
|
||||
msg = msg.first if msg.is_a? Array
|
||||
full_messages << "« " + v.custom_field.name + " » " + l(msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -534,7 +526,6 @@ module ApplicationHelper
|
||||
|
||||
def back_url_hidden_field_tag
|
||||
back_url = params[:back_url] || request.env['HTTP_REFERER']
|
||||
back_url = CGI.unescape(back_url.to_s)
|
||||
hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
|
||||
end
|
||||
|
||||
|
||||
@@ -16,15 +16,10 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module AttachmentsHelper
|
||||
# Displays view/delete links to the attachments of the given object
|
||||
# Options:
|
||||
# :author -- author names are not displayed if set to false
|
||||
def link_to_attachments(container, options = {})
|
||||
options.assert_valid_keys(:author)
|
||||
|
||||
if container.attachments.any?
|
||||
options = {:deletable => container.attachments_deletable?, :author => true}.merge(options)
|
||||
render :partial => 'attachments/links', :locals => {:attachments => container.attachments, :options => options}
|
||||
# displays the links to a collection of attachments
|
||||
def link_to_attachments(attachments, options = {})
|
||||
if attachments.any?
|
||||
render :partial => 'attachments/links', :locals => {:attachments => attachments, :options => options}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
85
app/helpers/ifpdf_helper.rb
Normal file
85
app/helpers/ifpdf_helper.rb
Normal file
@@ -0,0 +1,85 @@
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
require 'iconv'
|
||||
require 'rfpdf/chinese'
|
||||
|
||||
module IfpdfHelper
|
||||
|
||||
class IFPDF < FPDF
|
||||
include GLoc
|
||||
attr_accessor :footer_date
|
||||
|
||||
def initialize(lang)
|
||||
super()
|
||||
set_language_if_valid lang
|
||||
case current_language.to_s
|
||||
when 'ja'
|
||||
extend(PDF_Japanese)
|
||||
AddSJISFont()
|
||||
@font_for_content = 'SJIS'
|
||||
@font_for_footer = 'SJIS'
|
||||
when 'zh'
|
||||
extend(PDF_Chinese)
|
||||
AddGBFont()
|
||||
@font_for_content = 'GB'
|
||||
@font_for_footer = 'GB'
|
||||
when 'zh-tw'
|
||||
extend(PDF_Chinese)
|
||||
AddBig5Font()
|
||||
@font_for_content = 'Big5'
|
||||
@font_for_footer = 'Big5'
|
||||
else
|
||||
@font_for_content = 'Arial'
|
||||
@font_for_footer = 'Helvetica'
|
||||
end
|
||||
SetCreator(Redmine::Info.app_name)
|
||||
SetFont(@font_for_content)
|
||||
end
|
||||
|
||||
def SetFontStyle(style, size)
|
||||
SetFont(@font_for_content, style, size)
|
||||
end
|
||||
|
||||
def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
|
||||
@ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
|
||||
# these quotation marks are not correctly rendered in the pdf
|
||||
txt = txt.gsub(/[“”]/, '"') if txt
|
||||
txt = begin
|
||||
# 0x5c char handling
|
||||
txtar = txt.split('\\')
|
||||
txtar << '' if txt[-1] == ?\\
|
||||
txtar.collect {|x| @ic.iconv(x)}.join('\\').gsub(/\\/, "\\\\\\\\")
|
||||
rescue
|
||||
txt
|
||||
end || ''
|
||||
super w,h,txt,border,ln,align,fill,link
|
||||
end
|
||||
|
||||
def Footer
|
||||
SetFont(@font_for_footer, 'I', 8)
|
||||
SetY(-15)
|
||||
SetX(15)
|
||||
Cell(0, 5, @footer_date, 0, 0, 'L')
|
||||
SetY(-15)
|
||||
SetX(-30)
|
||||
Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -36,7 +36,6 @@ module IssuesHelper
|
||||
# Returns a string of css classes that apply to the given issue
|
||||
def css_issue_classes(issue)
|
||||
s = "issue status-#{issue.status.position} priority-#{issue.priority.position}"
|
||||
s << ' closed' if issue.closed?
|
||||
s << ' overdue' if issue.overdue?
|
||||
s
|
||||
end
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
module TimelogHelper
|
||||
include ApplicationHelper
|
||||
|
||||
def render_timelog_breadcrumb
|
||||
links = []
|
||||
links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
|
||||
@@ -83,7 +81,7 @@ module TimelogHelper
|
||||
csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
|
||||
# csv lines
|
||||
entries.each do |entry|
|
||||
fields = [format_date(entry.spent_on),
|
||||
fields = [l_date(entry.spent_on),
|
||||
entry.user,
|
||||
entry.activity,
|
||||
entry.project,
|
||||
|
||||
@@ -98,14 +98,6 @@ class Attachment < ActiveRecord::Base
|
||||
container.project
|
||||
end
|
||||
|
||||
def visible?(user=User.current)
|
||||
container.attachments_visible?(user)
|
||||
end
|
||||
|
||||
def deletable?(user=User.current)
|
||||
container.attachments_deletable?(user)
|
||||
end
|
||||
|
||||
def image?
|
||||
self.filename =~ /\.(jpe?g|gif|png)$/i
|
||||
end
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
class Document < ActiveRecord::Base
|
||||
belongs_to :project
|
||||
belongs_to :category, :class_name => "Enumeration", :foreign_key => "category_id"
|
||||
acts_as_attachable :delete_permission => :manage_documents
|
||||
has_many :attachments, :as => :container, :dependent => :destroy
|
||||
|
||||
acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
|
||||
acts_as_event :title => Proc.new {|o| "#{l(:label_document)}: #{o.title}"},
|
||||
|
||||
@@ -26,13 +26,13 @@ class Issue < ActiveRecord::Base
|
||||
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
|
||||
|
||||
has_many :journals, :as => :journalized, :dependent => :destroy
|
||||
has_many :attachments, :as => :container, :dependent => :destroy
|
||||
has_many :time_entries, :dependent => :delete_all
|
||||
has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
|
||||
|
||||
has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
|
||||
has_many :relations_to, :class_name => 'IssueRelation', :foreign_key => 'issue_to_id', :dependent => :delete_all
|
||||
|
||||
acts_as_attachable :after_remove => :attachment_removed
|
||||
acts_as_customizable
|
||||
acts_as_watchable
|
||||
acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
|
||||
@@ -45,7 +45,7 @@ class Issue < ActiveRecord::Base
|
||||
acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
|
||||
:author_key => :author_id
|
||||
|
||||
validates_presence_of :subject, :priority, :project, :tracker, :author, :status
|
||||
validates_presence_of :subject, :description, :priority, :project, :tracker, :author, :status
|
||||
validates_length_of :subject, :maximum => 255
|
||||
validates_inclusion_of :done_ratio, :in => 0..100
|
||||
validates_numericality_of :estimated_hours, :allow_nil => true
|
||||
@@ -266,15 +266,4 @@ class Issue < ActiveRecord::Base
|
||||
def to_s
|
||||
"#{tracker} ##{id}: #{subject}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Callback on attachment deletion
|
||||
def attachment_removed(obj)
|
||||
journal = init_journal(User.current)
|
||||
journal.details << JournalDetail.new(:property => 'attachment',
|
||||
:prop_key => obj.id,
|
||||
:old_value => obj.filename)
|
||||
journal.save
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,7 +40,7 @@ class MailHandler < ActionMailer::Base
|
||||
# Processes incoming emails
|
||||
def receive(email)
|
||||
@email = email
|
||||
@user = User.active.find(:first, :conditions => ["LOWER(mail) = ?", email.from.to_a.first.to_s.strip.downcase])
|
||||
@user = User.active.find(:first, :conditions => ["LOWER(mail) = ?", email.from.first.to_s.strip.downcase])
|
||||
unless @user
|
||||
# Unknown user => the email is ignored
|
||||
# TODO: ability to create the user's account
|
||||
@@ -90,13 +90,6 @@ class MailHandler < ActionMailer::Base
|
||||
end
|
||||
issue.subject = email.subject.chomp.toutf8
|
||||
issue.description = plain_text_body
|
||||
# custom fields
|
||||
issue.custom_field_values = issue.available_custom_fields.inject({}) do |h, c|
|
||||
if value = get_keyword(c.name, :override => true)
|
||||
h[c.id] = value
|
||||
end
|
||||
h
|
||||
end
|
||||
issue.save!
|
||||
add_attachments(issue)
|
||||
logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
|
||||
@@ -162,18 +155,11 @@ class MailHandler < ActionMailer::Base
|
||||
end
|
||||
end
|
||||
|
||||
def get_keyword(attr, options={})
|
||||
@keywords ||= {}
|
||||
if @keywords.has_key?(attr)
|
||||
@keywords[attr]
|
||||
else
|
||||
@keywords[attr] = begin
|
||||
if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && plain_text_body.gsub!(/^#{attr}:[ \t]*(.+)\s*$/i, '')
|
||||
$1.strip
|
||||
elsif !@@handler_options[:issue][attr].blank?
|
||||
@@handler_options[:issue][attr]
|
||||
end
|
||||
end
|
||||
def get_keyword(attr)
|
||||
if @@handler_options[:allow_override].include?(attr.to_s) && plain_text_body =~ /^#{attr}:[ \t]*(.+)$/i
|
||||
$1.strip
|
||||
elsif !@@handler_options[:issue][attr].blank?
|
||||
@@handler_options[:issue][attr]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -195,6 +181,5 @@ class MailHandler < ActionMailer::Base
|
||||
@plain_text_body = plain_text_part.body.to_s
|
||||
end
|
||||
@plain_text_body.strip!
|
||||
@plain_text_body
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,19 +22,12 @@ class Mailer < ActionMailer::Base
|
||||
|
||||
include ActionController::UrlWriter
|
||||
|
||||
def self.default_url_options
|
||||
h = Setting.host_name
|
||||
h = h.to_s.gsub(%r{\/.*$}, '') unless Redmine::Utils.relative_url_root.blank?
|
||||
{ :host => h, :protocol => Setting.protocol }
|
||||
end
|
||||
|
||||
def issue_add(issue)
|
||||
redmine_headers 'Project' => issue.project.identifier,
|
||||
'Issue-Id' => issue.id,
|
||||
'Issue-Author' => issue.author.login
|
||||
redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
|
||||
recipients issue.recipients
|
||||
cc(issue.watcher_recipients - @recipients)
|
||||
subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
|
||||
body :issue => issue,
|
||||
:issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
|
||||
@@ -46,7 +39,6 @@ class Mailer < ActionMailer::Base
|
||||
'Issue-Id' => issue.id,
|
||||
'Issue-Author' => issue.author.login
|
||||
redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
|
||||
@author = journal.user
|
||||
recipients issue.recipients
|
||||
# Watchers in cc
|
||||
cc(issue.watcher_recipients - @recipients)
|
||||
@@ -81,9 +73,6 @@ class Mailer < ActionMailer::Base
|
||||
added_to = ''
|
||||
added_to_url = ''
|
||||
case container.class.name
|
||||
when 'Project'
|
||||
added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container)
|
||||
added_to = "#{l(:label_project)}: #{container}"
|
||||
when 'Version'
|
||||
added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container.project_id)
|
||||
added_to = "#{l(:label_version)}: #{container.name}"
|
||||
@@ -133,15 +122,6 @@ class Mailer < ActionMailer::Base
|
||||
:url => url_for(:controller => 'users', :action => 'index', :status => User::STATUS_REGISTERED, :sort_key => 'created_on', :sort_order => 'desc')
|
||||
end
|
||||
|
||||
# A registered user's account was activated by an administrator
|
||||
def account_activated(user)
|
||||
set_language_if_valid user.language
|
||||
recipients user.mail
|
||||
subject l(:mail_subject_register, Setting.app_title)
|
||||
body :user => user,
|
||||
:login_url => url_for(:controller => 'account', :action => 'login')
|
||||
end
|
||||
|
||||
def lost_password(token)
|
||||
set_language_if_valid(token.user.language)
|
||||
recipients token.user.mail
|
||||
@@ -204,6 +184,12 @@ class Mailer < ActionMailer::Base
|
||||
set_language_if_valid Setting.default_language
|
||||
from Setting.mail_from
|
||||
|
||||
# URL options
|
||||
h = Setting.host_name
|
||||
h = h.to_s.gsub(%r{\/.*$}, '') unless ActionController::AbstractRequest.relative_url_root.blank?
|
||||
default_url_options[:host] = h
|
||||
default_url_options[:protocol] = Setting.protocol
|
||||
|
||||
# Common headers
|
||||
headers 'X-Mailer' => 'Redmine',
|
||||
'X-Redmine-Host' => Setting.host_name,
|
||||
@@ -219,10 +205,9 @@ class Mailer < ActionMailer::Base
|
||||
def create_mail
|
||||
# Removes the current user from the recipients and cc
|
||||
# if he doesn't want to receive notifications about what he does
|
||||
@author ||= User.current
|
||||
if @author.pref[:no_self_notified]
|
||||
recipients.delete(@author.mail) if recipients
|
||||
cc.delete(@author.mail) if cc
|
||||
if User.current.pref[:no_self_notified]
|
||||
recipients.delete(User.current.mail) if recipients
|
||||
cc.delete(User.current.mail) if cc
|
||||
end
|
||||
# Blind carbon copy recipients
|
||||
if Setting.bcc_recipients?
|
||||
|
||||
@@ -19,11 +19,11 @@ class Message < ActiveRecord::Base
|
||||
belongs_to :board
|
||||
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
|
||||
acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
|
||||
acts_as_attachable
|
||||
has_many :attachments, :as => :container, :dependent => :destroy
|
||||
belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
|
||||
|
||||
acts_as_searchable :columns => ['subject', 'content'],
|
||||
:include => {:board => :project},
|
||||
:include => {:board, :project},
|
||||
:project_key => 'project_id',
|
||||
:date_column => "#{table_name}.created_on"
|
||||
acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},
|
||||
|
||||
@@ -44,8 +44,6 @@ class Project < ActiveRecord::Base
|
||||
:association_foreign_key => 'custom_field_id'
|
||||
|
||||
acts_as_tree :order => "name", :counter_cache => true
|
||||
acts_as_attachable :view_permission => :view_files,
|
||||
:delete_permission => :manage_files
|
||||
|
||||
acts_as_customizable
|
||||
acts_as_searchable :columns => ['name', 'description'], :project_key => 'id', :permission => nil
|
||||
@@ -60,7 +58,7 @@ class Project < ActiveRecord::Base
|
||||
validates_associated :repository, :wiki
|
||||
validates_length_of :name, :maximum => 30
|
||||
validates_length_of :homepage, :maximum => 255
|
||||
validates_length_of :identifier, :in => 2..20
|
||||
validates_length_of :identifier, :in => 3..20
|
||||
validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
|
||||
|
||||
before_destroy :delete_all_members
|
||||
|
||||
@@ -31,9 +31,9 @@ class Role < ActiveRecord::Base
|
||||
raise "Can not copy workflow from a #{role.class}" unless role.is_a?(Role)
|
||||
raise "Can not copy workflow from/to an unsaved role" if proxy_owner.new_record? || role.new_record?
|
||||
clear
|
||||
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, old_status_id, new_status_id, role_id)" +
|
||||
connection.insert "INSERT INTO workflows (tracker_id, old_status_id, new_status_id, role_id)" +
|
||||
" SELECT tracker_id, old_status_id, new_status_id, #{proxy_owner.id}" +
|
||||
" FROM #{Workflow.table_name}" +
|
||||
" FROM workflows" +
|
||||
" WHERE role_id = #{role.id}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,7 +32,7 @@ class TimeEntry < ActiveRecord::Base
|
||||
:description => :comments
|
||||
|
||||
validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
|
||||
validates_numericality_of :hours, :allow_nil => true, :message => :activerecord_error_invalid
|
||||
validates_numericality_of :hours, :allow_nil => true
|
||||
validates_length_of :comments, :maximum => 255, :allow_nil => true
|
||||
|
||||
def after_initialize
|
||||
@@ -54,7 +54,7 @@ class TimeEntry < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def hours=(h)
|
||||
write_attribute :hours, (h.is_a?(String) ? (h.to_hours || h) : h)
|
||||
write_attribute :hours, (h.is_a?(String) ? h.to_hours : h)
|
||||
end
|
||||
|
||||
# tyear, tmonth, tweek assigned where setting spent_on attributes
|
||||
|
||||
@@ -23,9 +23,9 @@ class Tracker < ActiveRecord::Base
|
||||
raise "Can not copy workflow from a #{tracker.class}" unless tracker.is_a?(Tracker)
|
||||
raise "Can not copy workflow from/to an unsaved tracker" if proxy_owner.new_record? || tracker.new_record?
|
||||
clear
|
||||
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, old_status_id, new_status_id, role_id)" +
|
||||
connection.insert "INSERT INTO workflows (tracker_id, old_status_id, new_status_id, role_id)" +
|
||||
" SELECT #{proxy_owner.id}, old_status_id, new_status_id, role_id" +
|
||||
" FROM #{Workflow.table_name}" +
|
||||
" FROM workflows" +
|
||||
" WHERE tracker_id = #{tracker.id}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,8 +19,7 @@ class Version < ActiveRecord::Base
|
||||
before_destroy :check_integrity
|
||||
belongs_to :project
|
||||
has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id'
|
||||
acts_as_attachable :view_permission => :view_files,
|
||||
:delete_permission => :manage_files
|
||||
has_many :attachments, :as => :container, :dependent => :destroy
|
||||
|
||||
validates_presence_of :name
|
||||
validates_uniqueness_of :name, :scope => [:project_id]
|
||||
|
||||
@@ -21,7 +21,7 @@ require 'enumerator'
|
||||
class WikiPage < ActiveRecord::Base
|
||||
belongs_to :wiki
|
||||
has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy
|
||||
acts_as_attachable :delete_permission => :delete_wiki_pages_attachments
|
||||
has_many :attachments, :as => :container, :dependent => :destroy
|
||||
acts_as_tree :order => 'title'
|
||||
|
||||
acts_as_event :title => Proc.new {|o| "#{l(:label_wiki)}: #{o.title}"},
|
||||
@@ -111,10 +111,6 @@ class WikiPage < ActiveRecord::Base
|
||||
def editable_by?(usr)
|
||||
!protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
|
||||
end
|
||||
|
||||
def attachments_deletable?(usr=User.current)
|
||||
editable_by?(usr) && super(usr)
|
||||
end
|
||||
|
||||
def parent_title
|
||||
@parent_title || (self.parent && self.parent.pretty_title)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
<table class="list">
|
||||
<tr class="odd"><td><%= l(:text_default_administrator_account_changed) %></td><td><%= image_tag (@flags[:default_admin_changed] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
|
||||
<tr class="even"><td><%= l(:text_file_repository_writable) %> (<%= Attachment.storage_path %>)</td><td><%= image_tag (@flags[:file_repository_writable] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
|
||||
<tr class="even"><td><%= l(:text_plugin_assets_writable) %> (<%= Engines.public_directory %>)</td><td><%= image_tag (@flags[:plugin_assets_writable] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
|
||||
<tr class="even"><td><%= l(:text_file_repository_writable) %></td><td><%= image_tag (@flags[:file_repository_writable] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
|
||||
<tr class="odd"><td><%= l(:text_rmagick_available) %></td><td><%= image_tag (@flags[:rmagick_available] ? 'true.png' : 'false.png'), :style => "vertical-align:bottom;" %></td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<p><%= link_to_attachment attachment, :class => 'icon icon-attachment' -%>
|
||||
<%= h(" - #{attachment.description}") unless attachment.description.blank? %>
|
||||
<span class="size">(<%= number_to_human_size attachment.filesize %>)</span>
|
||||
<% if options[:deletable] %>
|
||||
<%= link_to image_tag('delete.png'), {:controller => 'attachments', :action => 'destroy', :id => attachment},
|
||||
<% if options[:delete_url] %>
|
||||
<%= link_to image_tag('delete.png'), options[:delete_url].update({:attachment_id => attachment}),
|
||||
:confirm => l(:text_are_you_sure),
|
||||
:method => :post,
|
||||
:class => 'delete',
|
||||
:title => l(:button_delete) %>
|
||||
<% end %>
|
||||
<% if options[:author] %>
|
||||
<% unless options[:no_author] %>
|
||||
<span class="author"><%= attachment.author %>, <%= format_time(attachment.created_on) %></span>
|
||||
<% end %>
|
||||
</p>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<tbody>
|
||||
<% line_num = 1 %>
|
||||
<% syntax_highlight(filename, to_utf8(content)).each_line do |line| %>
|
||||
<tr><th class="line-num" id="L<%= line_num %>"><a href="#L<%= line_num %>"><%= line_num %></a></th><td class="line-code"><pre><%= line %></pre></td></tr>
|
||||
<tr><th class="line-num" id="L<%= line_num %>"><%= line_num %></th><td class="line-code"><pre><%= line %></pre></td></tr>
|
||||
<% line_num += 1 %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</div>
|
||||
|
||||
<h3><%= l(:label_attachment_plural) %></h3>
|
||||
<%= link_to_attachments @document %>
|
||||
<%= link_to_attachments @attachments, :delete_url => (authorize_for('documents', 'destroy_attachment') ? {:controller => 'documents', :action => 'destroy_attachment', :id => @document} : nil) %>
|
||||
|
||||
<% if authorize_for('documents', 'add_attachment') %>
|
||||
<p><%= link_to l(:label_attachment_new), {}, :onclick => "Element.show('add_attachment_form'); Element.hide(this); Element.scrollTo('add_attachment_form'); return false;",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<div id="issue_descr_fields" <%= 'style="display:none"' unless @issue.new_record? || @issue.errors.any? %>>
|
||||
<p><%= f.text_field :subject, :size => 80, :required => true %></p>
|
||||
<p><%= f.text_area :description,
|
||||
<p><%= f.text_area :description, :required => true,
|
||||
:cols => 60,
|
||||
:rows => (@issue.description.blank? ? 10 : [[10, @issue.description.length / 50].max, 100].min),
|
||||
:accesskey => accesskey(:edit),
|
||||
@@ -24,13 +24,11 @@
|
||||
|
||||
<p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
|
||||
<p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
|
||||
<% unless @project.issue_categories.empty? %>
|
||||
<p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
|
||||
<%= prompt_to_remote(l(:label_issue_category_new),
|
||||
l(:label_issue_category_new), 'category[name]',
|
||||
{:controller => 'projects', :action => 'add_issue_category', :id => @project},
|
||||
:class => 'small', :tabindex => 199) if authorize_for('projects', 'add_issue_category') %></p>
|
||||
<% end %>
|
||||
<%= content_tag('p', f.select(:fixed_version_id,
|
||||
(@project.versions.sort.collect {|v| [v.name, v.id]}),
|
||||
{ :include_blank => true })) unless @project.versions.empty? %>
|
||||
@@ -50,14 +48,6 @@
|
||||
<p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
|
||||
<% end %>
|
||||
|
||||
<% if @issue.new_record? && User.current.allowed_to?(:add_issue_watchers, @project) -%>
|
||||
<p><label><%= l(:label_issue_watchers) %></label>
|
||||
<% @issue.project.users.sort.each do |user| -%>
|
||||
<label class="floating"><%= check_box_tag 'issue[watcher_user_ids][]', user.id, @issue.watcher_user_ids.include?(user.id) %> <%=h user %></label>
|
||||
<% end -%>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%= call_hook(:view_issues_form_details_bottom, { :issue => @issue, :form => f }) %>
|
||||
|
||||
<%= wikitoolbar_for 'issue_description' %>
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
<fieldset><legend><%= l(:field_notes) %></legend>
|
||||
<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
|
||||
<%= wikitoolbar_for 'notes' %>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<p><%= submit_tag l(:button_submit) %>
|
||||
|
||||
188
app/views/issues/gantt.rfpdf
Normal file
188
app/views/issues/gantt.rfpdf
Normal file
@@ -0,0 +1,188 @@
|
||||
<%
|
||||
pdf=IfpdfHelper::IFPDF.new(current_language)
|
||||
pdf.SetTitle("#{l(:label_gantt)} #{@project}")
|
||||
pdf.AliasNbPages
|
||||
pdf.footer_date = format_date(Date.today)
|
||||
pdf.AddPage("L")
|
||||
pdf.SetFontStyle('B',12)
|
||||
pdf.SetX(15)
|
||||
pdf.Cell(70, 20, @project.to_s)
|
||||
pdf.Ln
|
||||
pdf.SetFontStyle('B',9)
|
||||
|
||||
subject_width = 70
|
||||
header_heigth = 5
|
||||
|
||||
headers_heigth = header_heigth
|
||||
show_weeks = false
|
||||
show_days = false
|
||||
|
||||
if @gantt.months < 7
|
||||
show_weeks = true
|
||||
headers_heigth = 2*header_heigth
|
||||
if @gantt.months < 3
|
||||
show_days = true
|
||||
headers_heigth = 3*header_heigth
|
||||
end
|
||||
end
|
||||
|
||||
g_width = 210
|
||||
zoom = (g_width) / (@gantt.date_to - @gantt.date_from + 1)
|
||||
g_height = 120
|
||||
t_height = g_height + headers_heigth
|
||||
|
||||
y_start = pdf.GetY
|
||||
|
||||
|
||||
#
|
||||
# Months headers
|
||||
#
|
||||
month_f = @gantt.date_from
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
@gantt.months.times do
|
||||
width = ((month_f >> 1) - month_f) * zoom
|
||||
pdf.SetY(y_start)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
|
||||
left = left + width
|
||||
month_f = month_f >> 1
|
||||
end
|
||||
|
||||
#
|
||||
# Weeks headers
|
||||
#
|
||||
if show_weeks
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
if @gantt.date_from.cwday == 1
|
||||
# @gantt.date_from is monday
|
||||
week_f = @gantt.date_from
|
||||
else
|
||||
# find next monday after @gantt.date_from
|
||||
week_f = @gantt.date_from + (7 - @gantt.date_from.cwday + 1)
|
||||
width = (7 - @gantt.date_from.cwday + 1) * zoom-1
|
||||
pdf.SetY(y_start + header_heigth)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width + 1, height, "", "LTR")
|
||||
left = left + width+1
|
||||
end
|
||||
while week_f <= @gantt.date_to
|
||||
width = (week_f + 6 <= @gantt.date_to) ? 7 * zoom : (@gantt.date_to - week_f + 1) * zoom
|
||||
pdf.SetY(y_start + header_heigth)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
|
||||
left = left + width
|
||||
week_f = week_f+7
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Days headers
|
||||
#
|
||||
if show_days
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
wday = @gantt.date_from.cwday
|
||||
pdf.SetFontStyle('B',7)
|
||||
(@gantt.date_to - @gantt.date_from + 1).to_i.times do
|
||||
width = zoom
|
||||
pdf.SetY(y_start + 2 * header_heigth)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C")
|
||||
left = left + width
|
||||
wday = wday + 1
|
||||
wday = 1 if wday > 7
|
||||
end
|
||||
end
|
||||
|
||||
pdf.SetY(y_start)
|
||||
pdf.SetX(15)
|
||||
pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
|
||||
|
||||
|
||||
#
|
||||
# Tasks
|
||||
#
|
||||
top = headers_heigth + y_start
|
||||
pdf.SetFontStyle('B',7)
|
||||
@gantt.events.each do |i|
|
||||
pdf.SetY(top)
|
||||
pdf.SetX(15)
|
||||
|
||||
if i.is_a? Issue
|
||||
pdf.Cell(subject_width-15, 5, "#{i.tracker.name} #{i.id}: #{i.subject}".sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)'), "LR")
|
||||
else
|
||||
pdf.Cell(subject_width-15, 5, "#{l(:label_version)}: #{i.name}", "LR")
|
||||
end
|
||||
|
||||
pdf.SetY(top)
|
||||
pdf.SetX(subject_width)
|
||||
pdf.Cell(g_width, 5, "", "LR")
|
||||
|
||||
pdf.SetY(top+1.5)
|
||||
|
||||
if i.is_a? Issue
|
||||
i_start_date = (i.start_date >= @gantt.date_from ? i.start_date : @gantt.date_from )
|
||||
i_end_date = (i.due_before <= @gantt.date_to ? i.due_before : @gantt.date_to )
|
||||
|
||||
i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor
|
||||
i_done_date = (i_done_date <= @gantt.date_from ? @gantt.date_from : i_done_date )
|
||||
i_done_date = (i_done_date >= @gantt.date_to ? @gantt.date_to : i_done_date )
|
||||
|
||||
i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
|
||||
|
||||
i_left = ((i_start_date - @gantt.date_from)*zoom)
|
||||
i_width = ((i_end_date - i_start_date + 1)*zoom)
|
||||
d_width = ((i_done_date - i_start_date)*zoom)
|
||||
l_width = ((i_late_date - i_start_date+1)*zoom) if i_late_date
|
||||
l_width ||= 0
|
||||
|
||||
pdf.SetX(subject_width + i_left)
|
||||
pdf.SetFillColor(200,200,200)
|
||||
pdf.Cell(i_width, 2, "", 0, 0, "", 1)
|
||||
|
||||
if l_width > 0
|
||||
pdf.SetY(top+1.5)
|
||||
pdf.SetX(subject_width + i_left)
|
||||
pdf.SetFillColor(255,100,100)
|
||||
pdf.Cell(l_width, 2, "", 0, 0, "", 1)
|
||||
end
|
||||
if d_width > 0
|
||||
pdf.SetY(top+1.5)
|
||||
pdf.SetX(subject_width + i_left)
|
||||
pdf.SetFillColor(100,100,255)
|
||||
pdf.Cell(d_width, 2, "", 0, 0, "", 1)
|
||||
end
|
||||
|
||||
pdf.SetY(top+1.5)
|
||||
pdf.SetX(subject_width + i_left + i_width)
|
||||
pdf.Cell(30, 2, "#{i.status.name} #{i.done_ratio}%")
|
||||
else
|
||||
i_left = ((i.start_date - @gantt.date_from)*zoom)
|
||||
|
||||
pdf.SetX(subject_width + i_left)
|
||||
pdf.SetFillColor(50,200,50)
|
||||
pdf.Cell(2, 2, "", 0, 0, "", 1)
|
||||
|
||||
pdf.SetY(top+1.5)
|
||||
pdf.SetX(subject_width + i_left + 3)
|
||||
pdf.Cell(30, 2, "#{i.name}")
|
||||
end
|
||||
|
||||
|
||||
top = top + 5
|
||||
pdf.SetDrawColor(200, 200, 200)
|
||||
pdf.Line(15, top, subject_width+g_width, top)
|
||||
if pdf.GetY() > 180
|
||||
pdf.AddPage("L")
|
||||
top = 20
|
||||
pdf.Line(15, top, subject_width+g_width, top)
|
||||
end
|
||||
pdf.SetDrawColor(0, 0, 0)
|
||||
end
|
||||
|
||||
pdf.Line(15, top, subject_width+g_width, top)
|
||||
|
||||
%>
|
||||
<%= pdf.Output %>
|
||||
50
app/views/issues/index.rfpdf
Normal file
50
app/views/issues/index.rfpdf
Normal file
@@ -0,0 +1,50 @@
|
||||
<% pdf=IfpdfHelper::IFPDF.new(current_language)
|
||||
title = @project ? "#{@project.name} - #{l(:label_issue_plural)}" : "#{l(:label_issue_plural)}"
|
||||
pdf.SetTitle(title)
|
||||
pdf.AliasNbPages
|
||||
pdf.footer_date = format_date(Date.today)
|
||||
pdf.AddPage("L")
|
||||
row_height = 7
|
||||
|
||||
#
|
||||
# title
|
||||
#
|
||||
pdf.SetFontStyle('B',11)
|
||||
pdf.Cell(190,10, title)
|
||||
pdf.Ln
|
||||
|
||||
#
|
||||
# headers
|
||||
#
|
||||
pdf.SetFontStyle('B',10)
|
||||
pdf.SetFillColor(230, 230, 230)
|
||||
pdf.Cell(15, row_height, "#", 0, 0, 'L', 1)
|
||||
pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1)
|
||||
pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1)
|
||||
pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1)
|
||||
pdf.Cell(40, row_height, l(:field_assigned_to), 0, 0, 'L', 1)
|
||||
pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1)
|
||||
pdf.Cell(0, row_height, l(:field_subject), 0, 0, 'L', 1)
|
||||
pdf.Line(10, pdf.GetY, 287, pdf.GetY)
|
||||
pdf.Ln
|
||||
pdf.Line(10, pdf.GetY, 287, pdf.GetY)
|
||||
pdf.SetY(pdf.GetY() + 1)
|
||||
|
||||
#
|
||||
# rows
|
||||
#
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.SetFillColor(255, 255, 255)
|
||||
@issues.each do |issue|
|
||||
pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1)
|
||||
pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1)
|
||||
pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1)
|
||||
pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1)
|
||||
pdf.Cell(40, row_height, issue.assigned_to ? issue.assigned_to.name : '', 0, 0, 'L', 1)
|
||||
pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1)
|
||||
pdf.MultiCell(0, row_height, (@project == issue.project ? issue.subject : "#{issue.project.name} - #{issue.subject}"))
|
||||
pdf.Line(10, pdf.GetY, 287, pdf.GetY)
|
||||
pdf.SetY(pdf.GetY() + 1)
|
||||
end
|
||||
%>
|
||||
<%= pdf.Output %>
|
||||
@@ -42,7 +42,6 @@
|
||||
<% else %>
|
||||
<%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %>
|
||||
<p class="pagination"><%= pagination_links_full @issue_pages, @issue_count %></p>
|
||||
<% end %>
|
||||
|
||||
<p class="other-formats">
|
||||
<%= l(:label_export_to) %>
|
||||
@@ -51,6 +50,7 @@
|
||||
<span><%= link_to 'PDF', {:format => 'pdf'}, :class => 'pdf' %></span>
|
||||
</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% content_for :sidebar do %>
|
||||
<%= render :partial => 'issues/sidebar' %>
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
<%= render :partial => 'issues/form', :locals => {:f => f} %>
|
||||
</div>
|
||||
<%= submit_tag l(:button_create) %>
|
||||
<%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
|
||||
<%= link_to_remote l(:label_preview),
|
||||
{ :url => { :controller => 'issues', :action => 'preview', :project_id => @project },
|
||||
:method => 'post',
|
||||
@@ -15,12 +14,6 @@
|
||||
:with => "Form.serialize('issue-form')",
|
||||
:complete => "Element.scrollTo('preview')"
|
||||
}, :accesskey => accesskey(:preview) %>
|
||||
|
||||
<%= javascript_tag "Form.Element.focus('issue_subject');" %>
|
||||
<% end %>
|
||||
|
||||
<div id="preview" class="wiki"></div>
|
||||
|
||||
<% content_for :header_tags do %>
|
||||
<%= stylesheet_link_tag 'scm' %>
|
||||
<% end %>
|
||||
|
||||
126
app/views/issues/show.rfpdf
Normal file
126
app/views/issues/show.rfpdf
Normal file
@@ -0,0 +1,126 @@
|
||||
<% pdf=IfpdfHelper::IFPDF.new(current_language)
|
||||
pdf.SetTitle("#{@project.name} - ##{@issue.tracker.name} #{@issue.id}")
|
||||
pdf.AliasNbPages
|
||||
pdf.footer_date = format_date(Date.today)
|
||||
pdf.AddPage
|
||||
|
||||
pdf.SetFontStyle('B',11)
|
||||
pdf.Cell(190,10, "#{@issue.project} - #{@issue.tracker} # #{@issue.id}: #{@issue.subject}")
|
||||
pdf.Ln
|
||||
|
||||
y0 = pdf.GetY
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_status) + ":","LT")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, @issue.status.name,"RT")
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_priority) + ":","LT")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, @issue.priority.name,"RT")
|
||||
pdf.Ln
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_author) + ":","L")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, @issue.author.name,"R")
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_category) + ":","L")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, (@issue.category ? @issue.category.name : "-"),"R")
|
||||
pdf.Ln
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_created_on) + ":","L")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, format_date(@issue.created_on),"R")
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_assigned_to) + ":","L")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, (@issue.assigned_to ? @issue.assigned_to.name : "-"),"R")
|
||||
pdf.Ln
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_updated_on) + ":","LB")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, format_date(@issue.updated_on),"RB")
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_due_date) + ":","LB")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, format_date(@issue.due_date),"RB")
|
||||
pdf.Ln
|
||||
|
||||
for custom_value in @issue.custom_values
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, custom_value.custom_field.name + ":","L")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.MultiCell(155,5, (show_value custom_value),"R")
|
||||
end
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_subject) + ":","LTB")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(155,5, @issue.subject,"RTB")
|
||||
pdf.Ln
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_description) + ":")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.MultiCell(155,5, @issue.description,"BR")
|
||||
|
||||
pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
|
||||
pdf.Line(pdf.GetX, pdf.GetY, 170, pdf.GetY)
|
||||
|
||||
pdf.Ln
|
||||
|
||||
if @issue.changesets.any? && User.current.allowed_to?(:view_changesets, @issue.project)
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(190,5, l(:label_associated_revisions), "B")
|
||||
pdf.Ln
|
||||
for changeset in @issue.changesets
|
||||
pdf.SetFontStyle('B',8)
|
||||
pdf.Cell(190,5, format_time(changeset.committed_on) + " - " + changeset.author.to_s)
|
||||
pdf.Ln
|
||||
unless changeset.comments.blank?
|
||||
pdf.SetFontStyle('',8)
|
||||
pdf.MultiCell(190,5, changeset.comments)
|
||||
end
|
||||
pdf.Ln
|
||||
end
|
||||
end
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(190,5, l(:label_history), "B")
|
||||
pdf.Ln
|
||||
for journal in @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
|
||||
pdf.SetFontStyle('B',8)
|
||||
pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name)
|
||||
pdf.Ln
|
||||
pdf.SetFontStyle('I',8)
|
||||
for detail in journal.details
|
||||
pdf.Cell(190,5, "- " + show_detail(detail, true))
|
||||
pdf.Ln
|
||||
end
|
||||
if journal.notes?
|
||||
pdf.SetFontStyle('',8)
|
||||
pdf.MultiCell(190,5, journal.notes)
|
||||
end
|
||||
pdf.Ln
|
||||
end
|
||||
|
||||
if @issue.attachments.any?
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(190,5, l(:label_attachment_plural), "B")
|
||||
pdf.Ln
|
||||
for attachment in @issue.attachments
|
||||
pdf.SetFontStyle('',8)
|
||||
pdf.Cell(80,5, attachment.filename)
|
||||
pdf.Cell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
|
||||
pdf.Cell(25,5, format_date(attachment.created_on),0,0,"R")
|
||||
pdf.Cell(65,5, attachment.author.name,0,0,"R")
|
||||
pdf.Ln
|
||||
end
|
||||
end
|
||||
%>
|
||||
|
||||
<%= pdf.Output %>
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
<table width="100%">
|
||||
<tr>
|
||||
<td style="width:15%" class="status"><b><%=l(:field_status)%>:</b></td><td style="width:35%" class="status"><%= @issue.status.name %></td>
|
||||
<td style="width:15%" class="status"><b><%=l(:field_status)%>:</b></td><td style="width:35%" class="status status-<%= @issue.status.name %>"><%= @issue.status.name %></td>
|
||||
<td style="width:15%" class="start-date"><b><%=l(:field_start_date)%>:</b></td><td style="width:35%"><%= format_date(@issue.start_date) %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="priority"><b><%=l(:field_priority)%>:</b></td><td class="priority"><%= @issue.priority.name %></td>
|
||||
<td class="priority"><b><%=l(:field_priority)%>:</b></td><td class="priority priority-<%= @issue.priority.name %>"><%= @issue.priority.name %></td>
|
||||
<td class="due-date"><b><%=l(:field_due_date)%>:</b></td><td class="due-date"><%= format_date(@issue.due_date) %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -59,7 +59,7 @@ end %>
|
||||
<hr />
|
||||
|
||||
<div class="contextual">
|
||||
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:action => 'reply', :id => @issue} }, :class => 'icon icon-comment') unless @issue.description.blank? %>
|
||||
<%= link_to_remote_if_authorized l(:button_quote), { :url => {:action => 'reply', :id => @issue} }, :class => 'icon icon-comment' %>
|
||||
</div>
|
||||
|
||||
<p><strong><%=l(:field_description)%></strong></p>
|
||||
@@ -67,7 +67,9 @@ end %>
|
||||
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
|
||||
</div>
|
||||
|
||||
<%= link_to_attachments @issue %>
|
||||
<% if @issue.attachments.any? %>
|
||||
<%= link_to_attachments @issue.attachments, :delete_url => (authorize_for('issues', 'destroy_attachment') ? {:controller => 'issues', :action => 'destroy_attachment', :id => @issue} : nil) %>
|
||||
<% end %>
|
||||
|
||||
<% if authorize_for('issue_relations', 'new') || @issue.relations.any? %>
|
||||
<hr />
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<option selected="selected"><%= l(:label_jump_to_a_project) %></option>
|
||||
<option disabled="disabled">---</option>
|
||||
<% user_projects_by_root.keys.sort.each do |root| %>
|
||||
<%= content_tag('option', h(root.name), :value => url_for(:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item)) %>
|
||||
<%= content_tag('option', h(root.name), :value => url_for(:controller => 'projects', :action => 'show', :id => root)) %>
|
||||
<% user_projects_by_root[root].sort.each do |project| %>
|
||||
<% next if project == root %>
|
||||
<%= content_tag('option', ('» ' + h(project.name)), :value => url_for(:controller => 'projects', :action => 'show', :id => project, :jump => current_menu_item)) %>
|
||||
<%= content_tag('option', ('» ' + h(project.name)), :value => url_for(:controller => 'projects', :action => 'show', :id => project)) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</select>
|
||||
|
||||
@@ -47,20 +47,19 @@
|
||||
<%= tag('div', {:id => 'main', :class => (has_content?(:sidebar) ? '' : 'nosidebar')}, true) %>
|
||||
<div id="sidebar">
|
||||
<%= yield :sidebar %>
|
||||
<%= call_hook :view_layouts_base_sidebar %>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<%= render_flash_messages %>
|
||||
<%= content_tag('div', flash[:error], :class => 'flash error') if flash[:error] %>
|
||||
<%= content_tag('div', flash[:notice], :class => 'flash notice') if flash[:notice] %>
|
||||
<%= yield %>
|
||||
<%= call_hook :view_layouts_base_content %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
|
||||
|
||||
<div id="footer">
|
||||
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> © 2006-2009 Jean-Philippe Lang
|
||||
Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> © 2006-2008 Jean-Philippe Lang
|
||||
</div>
|
||||
</div>
|
||||
<%= call_hook :view_layouts_base_body_bottom %>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
<p><%= l(:notice_account_activated) %></p>
|
||||
<p><%= l(:label_login) %>: <%= link_to @login_url, @login_url %></p>
|
||||
@@ -1,2 +0,0 @@
|
||||
<%= l(:notice_account_activated) %>
|
||||
<%= l(:label_login) %>: <%= @login_url %>
|
||||
@@ -6,12 +6,17 @@ body {
|
||||
font-size: 0.8em;
|
||||
color:#484848;
|
||||
}
|
||||
h1, h2, h3 { font-family: "Trebuchet MS", Verdana, sans-serif; margin: 0px; }
|
||||
h1 { font-size: 1.2em; }
|
||||
h2, h3 { font-size: 1.1em; }
|
||||
a, a:link, a:visited { color: #2A5685;}
|
||||
a:hover, a:active { color: #c61a1a; }
|
||||
a.wiki-anchor { display: none; }
|
||||
h1 {
|
||||
font-family: "Trebuchet MS", Verdana, sans-serif;
|
||||
font-size: 1.2em;
|
||||
margin: 0px;
|
||||
}
|
||||
a, a:link, a:visited {
|
||||
color: #2A5685;
|
||||
}
|
||||
a:hover, a:active {
|
||||
color: #c61a1a;
|
||||
}
|
||||
hr {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<div class="wiki">
|
||||
<%= textilizable(@topic.content, :attachments => @topic.attachments) %>
|
||||
</div>
|
||||
<%= link_to_attachments @topic, :author => false %>
|
||||
<%= link_to_attachments @topic.attachments, :no_author => true %>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<div class="message reply">
|
||||
<h4><%=h message.subject %> - <%= authoring message.created_on, message.author %></h4>
|
||||
<div class="wiki"><%= textilizable message, :content, :attachments => message.attachments %></div>
|
||||
<%= link_to_attachments message, :author => false %>
|
||||
<%= link_to_attachments message.attachments, :no_author => true %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -47,6 +47,6 @@ entries_by_day = entries.group_by(&:spent_on)
|
||||
</tr>
|
||||
<% end -%>
|
||||
<% end -%>
|
||||
</tbody>
|
||||
</tbdoy>
|
||||
</table>
|
||||
<% end %>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<p><%= f.text_area :description, :rows => 5, :class => 'wiki-edit' %></p>
|
||||
<p><%= f.text_field :identifier, :required => true, :disabled => @project.identifier_frozen? %>
|
||||
<% unless @project.identifier_frozen? %>
|
||||
<br /><em><%= l(:text_length_between, 2, 20) %> <%= l(:text_project_identifier_info) %></em>
|
||||
<br /><em><%= l(:text_length_between, 3, 20) %> <%= l(:text_project_identifier_info) %></em>
|
||||
<% end %></p>
|
||||
<p><%= f.text_field :homepage, :size => 60 %></p>
|
||||
<p><%= f.check_box :is_public %></p>
|
||||
|
||||
@@ -46,9 +46,7 @@
|
||||
<% form_tag({}, :method => :get) do %>
|
||||
<h3><%= l(:label_activity) %></h3>
|
||||
<p><% @activity.event_types.each do |t| %>
|
||||
<%= check_box_tag "show_#{t}", 1, @activity.scope.include?(t) %>
|
||||
<%= link_to(l("label_#{t.singularize}_plural"), {"show_#{t}" => 1, :user_id => params[:user_id]})%>
|
||||
<br />
|
||||
<label><%= check_box_tag "show_#{t}", 1, @activity.scope.include?(t) %> <%= l("label_#{t.singularize}_plural")%></label><br />
|
||||
<% end %></p>
|
||||
<% if @project && @project.active_children.any? %>
|
||||
<p><label><%= check_box_tag 'with_subprojects', 1, @with_subprojects %> <%=l(:label_subproject_plural)%></label></p>
|
||||
|
||||
@@ -4,13 +4,10 @@
|
||||
<div class="box">
|
||||
<% form_tag({ :action => 'add_file', :id => @project }, :multipart => true, :class => "tabular") do %>
|
||||
|
||||
<% if @versions.any? %>
|
||||
<p><label for="version_id"><%=l(:field_version)%></label>
|
||||
<%= select_tag "version_id", content_tag('option', '') +
|
||||
options_from_collection_for_select(@versions, "id", "name") %></p>
|
||||
<% end %>
|
||||
<p><label for="version_id"><%=l(:field_version)%> <span class="required">*</span></label>
|
||||
<%= select_tag "version_id", options_from_collection_for_select(@versions, "id", "name") %></p>
|
||||
|
||||
<p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
|
||||
</div>
|
||||
<%= submit_tag l(:button_add) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -4,41 +4,39 @@
|
||||
|
||||
<h2><%=l(:label_attachment_plural)%></h2>
|
||||
|
||||
<% delete_allowed = User.current.allowed_to?(:manage_files, @project) %>
|
||||
<% delete_allowed = authorize_for('versions', 'destroy_file') %>
|
||||
|
||||
<table class="list files">
|
||||
<table class="list">
|
||||
<thead><tr>
|
||||
<th><%=l(:field_version)%></th>
|
||||
<%= sort_header_tag('filename', :caption => l(:field_filename)) %>
|
||||
<%= sort_header_tag('created_on', :caption => l(:label_date), :default_order => 'desc') %>
|
||||
<%= sort_header_tag('size', :caption => l(:field_filesize), :default_order => 'desc') %>
|
||||
<%= sort_header_tag('downloads', :caption => l(:label_downloads_abbr), :default_order => 'desc') %>
|
||||
<th>MD5</th>
|
||||
<th></th>
|
||||
<% if delete_allowed %><th></th><% end %>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<% @containers.each do |container| %>
|
||||
<% next if container.attachments.empty? -%>
|
||||
<% if container.is_a?(Version) -%>
|
||||
<tr>
|
||||
<th colspan="6" align="left">
|
||||
<%= link_to(h(container), {:controller => 'versions', :action => 'show', :id => container}, :class => "icon icon-package") %>
|
||||
</th>
|
||||
</tr>
|
||||
<% end -%>
|
||||
<% container.attachments.each do |file| %>
|
||||
<tr class="file <%= cycle("odd", "even") %>">
|
||||
<td class="filename"><%= link_to_attachment file, :download => true, :title => file.description %></td>
|
||||
<td class="created_on"><%= format_time(file.created_on) %></td>
|
||||
<td class="filesize"><%= number_to_human_size(file.filesize) %></td>
|
||||
<td class="downloads"><%= file.downloads %></td>
|
||||
<td class="digest"><%= file.digest %></td>
|
||||
<% for version in @versions %>
|
||||
<% unless version.attachments.empty? %>
|
||||
<tr><th colspan="7" align="left"><span class="icon icon-package"><b><%= version.name %></b></span></th></tr>
|
||||
<% for file in version.attachments %>
|
||||
<tr class="<%= cycle("odd", "even") %>">
|
||||
<td></td>
|
||||
<td><%= link_to_attachment file, :download => true, :title => file.description %></td>
|
||||
<td align="center"><%= format_time(file.created_on) %></td>
|
||||
<td align="center"><%= number_to_human_size(file.filesize) %></td>
|
||||
<td align="center"><%= file.downloads %></td>
|
||||
<td align="center"><small><%= file.digest %></small></td>
|
||||
<% if delete_allowed %>
|
||||
<td align="center">
|
||||
<%= link_to(image_tag('delete.png'), {:controller => 'attachments', :action => 'destroy', :id => file},
|
||||
:confirm => l(:text_are_you_sure), :method => :post) if delete_allowed %>
|
||||
<%= link_to_if_authorized image_tag('delete.png'), {:controller => 'versions', :action => 'destroy_file', :id => version, :attachment_id => file}, :confirm => l(:text_are_you_sure), :method => :post %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end
|
||||
reset_cycle %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<td><%= link_to(version.wiki_page_title, :controller => 'wiki', :page => Wiki.titleize(version.wiki_page_title)) unless version.wiki_page_title.blank? || @project.wiki.nil? %></td>
|
||||
<td align="center"><%= link_to_if_authorized l(:button_edit), { :controller => 'versions', :action => 'edit', :id => version }, :class => 'icon icon-edit' %></td>
|
||||
<td align="center"><%= link_to_if_authorized l(:button_delete), {:controller => 'versions', :action => 'destroy', :id => version}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %></td>
|
||||
</td>
|
||||
</tr>
|
||||
<% end; reset_cycle %>
|
||||
</tbody>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<p>
|
||||
<% if @repository.supports_cat? %>
|
||||
<%= link_to_if action_name != 'entry', l(:button_view), {:action => 'entry', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
|
||||
<% end %>
|
||||
<% if @repository.supports_annotate? %>
|
||||
<%= link_to_if action_name != 'annotate', l(:button_annotate), {:action => 'annotate', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
|
||||
<% end %>
|
||||
<%= link_to(l(:button_download), {:action => 'entry', :id => @project, :path => to_path_param(@path), :rev => @rev, :format => 'raw' }) if @repository.supports_cat? %>
|
||||
<%= "(#{number_to_human_size(@entry.size)})" if @entry.size %>
|
||||
</p>
|
||||
@@ -1,7 +1,5 @@
|
||||
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2>
|
||||
|
||||
<p><%= render :partial => 'link_to_functions' %></p>
|
||||
|
||||
<% colors = Hash.new {|k,v| k[v] = (k.size % 12) } %>
|
||||
|
||||
<div class="autoscroll">
|
||||
@@ -11,7 +9,7 @@
|
||||
<% syntax_highlight(@path, to_utf8(@annotate.content)).each_line do |line| %>
|
||||
<% revision = @annotate.revisions[line_num-1] %>
|
||||
<tr class="bloc-<%= revision.nil? ? 0 : colors[revision.identifier || revision.revision] %>">
|
||||
<th class="line-num" id="L<%= line_num %>"><a href="#L<%= line_num %>"><%= line_num %></a></th>
|
||||
<th class="line-num"><%= line_num %></th>
|
||||
<td class="revision">
|
||||
<%= (revision.identifier ? link_to(format_revision(revision.identifier), :action => 'revision', :id => @project, :rev => revision.identifier) : format_revision(revision.revision)) if revision %></td>
|
||||
<td class="author"><%= h(revision.author.to_s.split('<').first) if revision %></td>
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %></h2>
|
||||
|
||||
<p><%= render :partial => 'link_to_functions' %></p>
|
||||
<p>
|
||||
<% if @repository.supports_cat? %>
|
||||
<%= link_to l(:button_view), {:action => 'entry', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
|
||||
<% end %>
|
||||
<% if @repository.supports_annotate? %>
|
||||
<%= link_to l(:button_annotate), {:action => 'annotate', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
|
||||
<% end %>
|
||||
<%= link_to(l(:button_download), {:action => 'entry', :id => @project, :path => to_path_param(@path), :rev => @rev, :format => 'raw' }) if @repository.supports_cat? %>
|
||||
<%= "(#{number_to_human_size(@entry.size)})" if @entry.size %>
|
||||
</p>
|
||||
|
||||
<%= render_properties(@properties) %>
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2>
|
||||
|
||||
<p><%= render :partial => 'link_to_functions' %></p>
|
||||
|
||||
<%= render :partial => 'common/file', :locals => {:filename => @path, :content => @content} %>
|
||||
|
||||
<% content_for :header_tags do %>
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
<%= text_field_tag 'settings[issues_export_limit]', Setting.issues_export_limit, :size => 6 %></p>
|
||||
</div>
|
||||
|
||||
<fieldset class="box settings"><legend><%= l(:setting_issue_list_default_columns) %></legend>
|
||||
<fieldset class="box"><legend><%= l(:setting_issue_list_default_columns) %></legend>
|
||||
<%= hidden_field_tag 'settings[issue_list_default_columns][]', '' %>
|
||||
<% Query.new.available_columns.each do |column| %>
|
||||
<p><% Query.new.available_columns.each do |column| %>
|
||||
<label><%= check_box_tag 'settings[issue_list_default_columns][]', column.name, Setting.issue_list_default_columns.include?(column.name.to_s) %>
|
||||
<%= column.caption %></label><br />
|
||||
<% end %>
|
||||
<%= column.caption %></label>
|
||||
<% end %></p>
|
||||
</fieldset>
|
||||
|
||||
<%= submit_tag l(:button_save) %>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
body { font:80% Verdana,Tahoma,Arial,sans-serif; }
|
||||
h1, h2, h3, h4 { font-family: "Trebuchet MS",Georgia,"Times New Roman",serif; }
|
||||
h1, h2, h3, h4 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; }
|
||||
ul.toc { padding: 4px; margin-left: 0; }
|
||||
ul.toc li { list-style-type:none; }
|
||||
ul.toc li.heading2 { margin-left: 1em; }
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
body { font:80% Verdana,Tahoma,Arial,sans-serif; }
|
||||
h1, h2, h3, h4 { font-family: "Trebuchet MS",Georgia,"Times New Roman",serif; }
|
||||
h1, h2, h3, h4 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<%= render(:partial => "wiki/content", :locals => {:content => @content}) %>
|
||||
|
||||
<%= link_to_attachments @page %>
|
||||
<%= link_to_attachments @page.attachments, :delete_url => ((@editable && authorize_for('wiki', 'destroy_attachment')) ? {:controller => 'wiki', :action => 'destroy_attachment', :page => @page.title} : nil) %>
|
||||
|
||||
<% if @editable && authorize_for('wiki', 'add_attachment') %>
|
||||
<p><%= link_to l(:label_attachment_new), {}, :onclick => "Element.show('add_attachment_form'); Element.hide(this); Element.scrollTo('add_attachment_form'); return false;",
|
||||
|
||||
@@ -2,10 +2,9 @@ class SetTopicAuthorsAsWatchers < ActiveRecord::Migration
|
||||
def self.up
|
||||
# Sets active users who created/replied a topic as watchers of the topic
|
||||
# so that the new watch functionality at topic level doesn't affect notifications behaviour
|
||||
Message.connection.execute("INSERT INTO #{Watcher.table_name} (watchable_type, watchable_id, user_id)" +
|
||||
" SELECT DISTINCT 'Message', COALESCE(m.parent_id, m.id), m.author_id" +
|
||||
" FROM #{Message.table_name} m, #{User.table_name} u" +
|
||||
" WHERE m.author_id = u.id AND u.status = 1")
|
||||
Message.connection.execute("INSERT INTO watchers (watchable_type, watchable_id, user_id)" +
|
||||
" SELECT DISTINCT 'Message', COALESCE(messages.parent_id, messages.id), messages.author_id FROM messages, users" +
|
||||
" WHERE messages.author_id = users.id AND users.status = 1")
|
||||
end
|
||||
|
||||
def self.down
|
||||
|
||||
@@ -1,55 +1,10 @@
|
||||
== Redmine changelog
|
||||
|
||||
Redmine - project management software
|
||||
Copyright (C) 2006-2009 Jean-Philippe Lang
|
||||
Copyright (C) 2006-2008 Jean-Philippe Lang
|
||||
http://www.redmine.org/
|
||||
|
||||
|
||||
== 2009-03-07 v0.8.2
|
||||
|
||||
* Send an email to the user when an administrator activates a registered user
|
||||
* Strip keywords from received email body
|
||||
* Footer updated to 2009
|
||||
* Show RSS-link even when no issues is found
|
||||
* One click filter action in activity view
|
||||
* Clickable/linkable line #'s while browsing the repo or viewing a file
|
||||
* Links to versions on files list
|
||||
* Added request and controller objects to the hooks by default
|
||||
* Fixed: exporting an issue with attachments to PDF raises an error
|
||||
* Fixed: "too few arguments" error may occur on activerecord error translation
|
||||
* Fixed: "Default columns Displayed on the Issues list" setting is not easy to read
|
||||
* Fixed: visited links to closed tickets are not striked through with IE6
|
||||
* Fixed: MailHandler#plain_text_body returns nil if there was nothing to strip
|
||||
* Fixed: MailHandler raises an error when processing an email without From header
|
||||
|
||||
|
||||
== 2009-02-15 v0.8.1
|
||||
|
||||
* Select watchers on new issue form
|
||||
* Issue description is no longer a required field
|
||||
* Files module: ability to add files without version
|
||||
* Jump to the current tab when using the project quick-jump combo
|
||||
* Display a warning if some attachments were not saved
|
||||
* Import custom fields values from emails on issue creation
|
||||
* Show view/annotate/download links on entry and annotate views
|
||||
* Admin Info Screen: Display if plugin assets directory is writable
|
||||
* Adds a 'Create and continue' button on the new issue form
|
||||
* IMAP: add options to move received emails
|
||||
* Do not show Category field when categories are not defined
|
||||
* Lower the project identifier limit to a minimum of two characters
|
||||
* Add "closed" html class to closed entries in issue list
|
||||
* Fixed: broken redirect URL on login failure
|
||||
* Fixed: Deleted files are shown when using Darcs
|
||||
* Fixed: Darcs adapter works on Win32 only
|
||||
* Fixed: syntax highlight doesn't appear in new ticket preview
|
||||
* Fixed: email notification for changes I make still occurs when running Repository.fetch_changesets
|
||||
* Fixed: no error is raised when entering invalid hours on the issue update form
|
||||
* Fixed: Details time log report CSV export doesn't honour date format from settings
|
||||
* Fixed: invalid css classes on issue details
|
||||
* Fixed: Trac importer creates duplicate custom values
|
||||
* Fixed: inline attached image should not match partial filename
|
||||
|
||||
|
||||
== 2008-12-30 v0.8.0
|
||||
|
||||
* Setting added in order to limit the number of diff lines that should be displayed
|
||||
|
||||
@@ -696,6 +696,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -697,6 +697,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -701,6 +701,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -697,6 +697,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -698,6 +698,3 @@ default_activity_development: Entwicklung
|
||||
enumeration_issue_priorities: Ticket-Prioritäten
|
||||
enumeration_doc_categories: Dokumentenkategorien
|
||||
enumeration_activities: Aktivitäten (Zeiterfassung)
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -85,8 +85,6 @@ error_scm_command_failed: "An error occurred when trying to access the repositor
|
||||
error_scm_annotate: "The entry does not exist or can not be annotated."
|
||||
error_issue_not_found_in_project: 'The issue was not found or does not belong to this project'
|
||||
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
|
||||
mail_subject_lost_password: Your %s password
|
||||
mail_body_lost_password: 'To change your password, click on the following link:'
|
||||
mail_subject_register: Your %s account activation
|
||||
@@ -592,7 +590,6 @@ button_check_all: Check all
|
||||
button_uncheck_all: Uncheck all
|
||||
button_delete: Delete
|
||||
button_create: Create
|
||||
button_create_and_continue: Create and continue
|
||||
button_test: Test
|
||||
button_edit: Edit
|
||||
button_add: Add
|
||||
@@ -663,8 +660,7 @@ text_status_changed_by_changeset: Applied in changeset %s.
|
||||
text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s) ?'
|
||||
text_select_project_modules: 'Select modules to enable for this project:'
|
||||
text_default_administrator_account_changed: Default administrator account changed
|
||||
text_file_repository_writable: Attachments directory writable
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
text_file_repository_writable: File repository writable
|
||||
text_rmagick_available: RMagick available (optional)
|
||||
text_destroy_time_entries_question: %.02f hours were reported on the issues you are about to delete. What do you want to do ?
|
||||
text_destroy_time_entries: Delete reported hours
|
||||
|
||||
@@ -681,6 +681,3 @@ text_user_mail_option: "De los proyectos no seleccionados, sólo recibirá notif
|
||||
text_user_wrote: '%s escribió:'
|
||||
text_wiki_destroy_confirmation: ¿Seguro que quiere borrar el wiki y todo su contenido?
|
||||
text_workflow_edit: Seleccionar un flujo de trabajo para actualizar
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -696,6 +696,3 @@ label_user_activity: "Käyttäjän %s historia"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -85,8 +85,6 @@ error_scm_command_failed: "Une erreur s'est produite lors de l'accès au dépôt
|
||||
error_scm_annotate: "L'entrée n'existe pas ou ne peut pas être annotée."
|
||||
error_issue_not_found_in_project: "La demande n'existe pas ou n'appartient pas à ce projet"
|
||||
|
||||
warning_attachments_not_saved: "%d fichier(s) n'ont pas pu être sauvegardés."
|
||||
|
||||
mail_subject_lost_password: Votre mot de passe %s
|
||||
mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant:'
|
||||
mail_subject_register: Activation de votre compte %s
|
||||
@@ -582,7 +580,7 @@ label_reverse_chronological_order: Dans l'ordre chronologique inverse
|
||||
label_planning: Planning
|
||||
label_incoming_emails: Emails entrants
|
||||
label_generate_key: Générer une clé
|
||||
label_issue_watchers: Observateurs
|
||||
label_issue_watchers: Utilisateurs surveillant cette demande
|
||||
label_example: Exemple
|
||||
|
||||
button_login: Connexion
|
||||
@@ -592,7 +590,6 @@ button_check_all: Tout cocher
|
||||
button_uncheck_all: Tout décocher
|
||||
button_delete: Supprimer
|
||||
button_create: Créer
|
||||
button_create_and_continue: Créer et continuer
|
||||
button_test: Tester
|
||||
button_edit: Modifier
|
||||
button_add: Ajouter
|
||||
@@ -664,7 +661,6 @@ text_issues_destroy_confirmation: 'Etes-vous sûr de vouloir supprimer le(s) dem
|
||||
text_select_project_modules: 'Selectionner les modules à activer pour ce project:'
|
||||
text_default_administrator_account_changed: Compte administrateur par défaut changé
|
||||
text_file_repository_writable: Répertoire de stockage des fichiers accessible en écriture
|
||||
text_plugin_assets_writable: Répertoire public des plugins accessible en écriture
|
||||
text_rmagick_available: Bibliothèque RMagick présente (optionnelle)
|
||||
text_destroy_time_entries_question: %.02f heures ont été enregistrées sur les demandes à supprimer. Que voulez-vous faire ?
|
||||
text_destroy_time_entries: Supprimer les heures
|
||||
|
||||
@@ -694,8 +694,3 @@ permission_edit_own_messages: ערוך הודעות של עצמך
|
||||
permission_delete_own_messages: מחק הודעות של עצמך
|
||||
label_user_activity: "הפעילות של %s"
|
||||
label_updated_time_by: עודכן ע"י %s לפני %s
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -697,6 +697,3 @@ label_user_activity: "%s tevékenységei"
|
||||
label_updated_time_by: "Módosította %s ennyivel ezelőtt: %s"
|
||||
text_diff_truncated: '... A diff fájl vége nem jelenik meg, mert hosszab, mint a megjeleníthető sorok száma.'
|
||||
setting_diff_max_lines_displayed: A megjelenítendő sorok száma (maximum) a diff fájloknál
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -696,6 +696,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -697,6 +697,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -696,6 +696,3 @@ label_user_activity: "%s의 작업내역"
|
||||
label_updated_time_by: %s가 %s 전에 변경
|
||||
text_diff_truncated: '... 이 차이점은 표시할 수 있는 최대 줄수를 초과해서 이 차이점은 잘렸습니다.'
|
||||
setting_diff_max_lines_displayed: 차이점보기에 표시할 최대 줄수
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -698,6 +698,3 @@ default_activity_development: Vystymas
|
||||
enumeration_issue_priorities: Darbo prioritetai
|
||||
enumeration_doc_categories: Dokumento kategorijos
|
||||
enumeration_activities: Veiklos (laiko sekimas)
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -698,6 +698,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -697,6 +697,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -715,6 +715,3 @@ label_user_activity: "Aktywność: %s"
|
||||
label_updated_time_by: Uaktualnione przez %s %s temu
|
||||
text_diff_truncated: '... Ten plik różnic został przycięty ponieważ jest zbyt długi.'
|
||||
setting_diff_max_lines_displayed: Maksymalna liczba linii różnicy do pokazania
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -47,8 +47,8 @@ general_text_Yes: 'Sim'
|
||||
general_text_no: 'não'
|
||||
general_text_yes: 'sim'
|
||||
general_lang_name: 'Português(Brasil)'
|
||||
general_csv_separator: ';'
|
||||
general_csv_decimal_separator: ','
|
||||
general_csv_separator: ','
|
||||
general_csv_decimal_separator: '.'
|
||||
general_csv_encoding: ISO-8859-1
|
||||
general_pdf_encoding: ISO-8859-1
|
||||
general_day_names: Segunda,Terça,Quarta,Quinta,Sexta,Sábado,Domingo
|
||||
@@ -697,6 +697,3 @@ label_user_activity: "Atividade de %s"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -698,6 +698,3 @@ label_user_activity: "Actividade de %s"
|
||||
label_updated_time_by: Actualizado por %s há %s
|
||||
text_diff_truncated: '... Este diff foi truncado porque excede o tamanho máximo que pode ser mostrado.'
|
||||
setting_diff_max_lines_displayed: Número máximo de linhas de diff mostradas
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -696,6 +696,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -730,6 +730,3 @@ text_user_wrote: '%s написал(а):'
|
||||
text_wiki_destroy_confirmation: Вы уверены, что хотите удалить данную Wiki и все ее содержимое?
|
||||
text_workflow_edit: Выберите роль и трекер для редактирования последовательности состояний
|
||||
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -702,6 +702,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -697,6 +697,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -696,7 +696,4 @@ default_activity_development: Utveckling
|
||||
|
||||
enumeration_issue_priorities: Ärendeprioriteter
|
||||
enumeration_doc_categories: Dokumentkategorier
|
||||
enumeration_activities: Aktiviteter (tidsuppföljning)
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
enumeration_activities: Aktiviteter (tidsuppföljning)
|
||||
@@ -699,6 +699,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -697,6 +697,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -698,6 +698,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -699,6 +699,3 @@ label_user_activity: "%s's activity"
|
||||
label_updated_time_by: Updated by %s %s ago
|
||||
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
|
||||
setting_diff_max_lines_displayed: Max number of diff lines displayed
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -698,6 +698,3 @@ default_activity_development: 開發
|
||||
enumeration_issue_priorities: 項目優先權
|
||||
enumeration_doc_categories: 文件分類
|
||||
enumeration_activities: 活動 (時間追蹤)
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -698,6 +698,3 @@ default_activity_development: 开发
|
||||
enumeration_issue_priorities: 问题优先级
|
||||
enumeration_doc_categories: 文档类别
|
||||
enumeration_activities: 活动(时间跟踪)
|
||||
text_plugin_assets_writable: Plugin assets directory writable
|
||||
warning_attachments_not_saved: "%d file(s) could not be saved."
|
||||
button_create_and_continue: Create and continue
|
||||
|
||||
@@ -791,10 +791,7 @@ class RedCloth3 < String
|
||||
\s?
|
||||
(?:\(([^)]+?)\)(?="))? # $title
|
||||
":
|
||||
( # $url
|
||||
(\/|[a-zA-Z]+:\/\/|www\.) # $proto
|
||||
[\w\/]\S+?
|
||||
)
|
||||
([\w\/]\S+?) # $url
|
||||
(\/)? # $slash
|
||||
([^\w\=\/;\(\)]*?) # $post
|
||||
(?=<|\s|$)
|
||||
@@ -802,7 +799,7 @@ class RedCloth3 < String
|
||||
#"
|
||||
def inline_textile_link( text )
|
||||
text.gsub!( LINK_RE ) do |m|
|
||||
pre,atts,text,title,url,proto,slash,post = $~[1..8]
|
||||
pre,atts,text,title,url,slash,post = $~[1..7]
|
||||
|
||||
url, url_title = check_refs( url )
|
||||
title ||= url_title
|
||||
|
||||
@@ -35,7 +35,7 @@ Redmine::AccessControl.map do |map|
|
||||
:queries => :index,
|
||||
:reports => :issue_report}, :public => true
|
||||
map.permission :add_issues, {:issues => :new}
|
||||
map.permission :edit_issues, {:issues => [:edit, :reply, :bulk_edit]}
|
||||
map.permission :edit_issues, {:issues => [:edit, :reply, :bulk_edit, :destroy_attachment]}
|
||||
map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}
|
||||
map.permission :add_issue_notes, {:issues => [:edit, :reply]}
|
||||
map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin
|
||||
@@ -67,12 +67,12 @@ Redmine::AccessControl.map do |map|
|
||||
end
|
||||
|
||||
map.project_module :documents do |map|
|
||||
map.permission :manage_documents, {:documents => [:new, :edit, :destroy, :add_attachment]}, :require => :loggedin
|
||||
map.permission :manage_documents, {:documents => [:new, :edit, :destroy, :add_attachment, :destroy_attachment]}, :require => :loggedin
|
||||
map.permission :view_documents, :documents => [:index, :show, :download]
|
||||
end
|
||||
|
||||
map.project_module :files do |map|
|
||||
map.permission :manage_files, {:projects => :add_file}, :require => :loggedin
|
||||
map.permission :manage_files, {:projects => :add_file, :versions => :destroy_file}, :require => :loggedin
|
||||
map.permission :view_files, :projects => :list_files, :versions => :download
|
||||
end
|
||||
|
||||
@@ -83,7 +83,7 @@ Redmine::AccessControl.map do |map|
|
||||
map.permission :view_wiki_pages, :wiki => [:index, :special]
|
||||
map.permission :view_wiki_edits, :wiki => [:history, :diff, :annotate]
|
||||
map.permission :edit_wiki_pages, :wiki => [:edit, :preview, :add_attachment]
|
||||
map.permission :delete_wiki_pages_attachments, {}
|
||||
map.permission :delete_wiki_pages_attachments, :wiki => :destroy_attachment
|
||||
map.permission :protect_wiki_pages, {:wiki => :protect}, :require => :member
|
||||
end
|
||||
|
||||
|
||||
@@ -24,9 +24,7 @@ module Redmine #:nodoc:
|
||||
def to_hours
|
||||
s = self.dup
|
||||
s.strip!
|
||||
if s =~ %r{^(\d+([.,]\d+)?)h?$}
|
||||
s = $1
|
||||
else
|
||||
unless s =~ %r{^[\d\.,]+$}
|
||||
# 2:30 => 2.5
|
||||
s.gsub!(%r{^(\d+):(\d+)$}) { $1.to_i + $2.to_i / 60.0 }
|
||||
# 2h30, 2h, 30m => 2.5, 2, 0.5
|
||||
|
||||
@@ -1,461 +0,0 @@
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2009 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
require 'iconv'
|
||||
require 'rfpdf/chinese'
|
||||
|
||||
module Redmine
|
||||
module Export
|
||||
module PDF
|
||||
include ActionView::Helpers::NumberHelper
|
||||
|
||||
class IFPDF < FPDF
|
||||
include GLoc
|
||||
attr_accessor :footer_date
|
||||
|
||||
def initialize(lang)
|
||||
super()
|
||||
set_language_if_valid lang
|
||||
case current_language.to_s
|
||||
when 'ja'
|
||||
extend(PDF_Japanese)
|
||||
AddSJISFont()
|
||||
@font_for_content = 'SJIS'
|
||||
@font_for_footer = 'SJIS'
|
||||
when 'zh'
|
||||
extend(PDF_Chinese)
|
||||
AddGBFont()
|
||||
@font_for_content = 'GB'
|
||||
@font_for_footer = 'GB'
|
||||
when 'zh-tw'
|
||||
extend(PDF_Chinese)
|
||||
AddBig5Font()
|
||||
@font_for_content = 'Big5'
|
||||
@font_for_footer = 'Big5'
|
||||
else
|
||||
@font_for_content = 'Arial'
|
||||
@font_for_footer = 'Helvetica'
|
||||
end
|
||||
SetCreator(Redmine::Info.app_name)
|
||||
SetFont(@font_for_content)
|
||||
end
|
||||
|
||||
def SetFontStyle(style, size)
|
||||
SetFont(@font_for_content, style, size)
|
||||
end
|
||||
|
||||
def SetTitle(txt)
|
||||
txt = begin
|
||||
utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
|
||||
hextxt = "<FEFF" # FEFF is BOM
|
||||
hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
|
||||
hextxt << ">"
|
||||
rescue
|
||||
txt
|
||||
end || ''
|
||||
super(txt)
|
||||
end
|
||||
|
||||
def textstring(s)
|
||||
# Format a text string
|
||||
if s =~ /^</ # This means the string is hex-dumped.
|
||||
return s
|
||||
else
|
||||
return '('+escape(s)+')'
|
||||
end
|
||||
end
|
||||
|
||||
def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
|
||||
@ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
|
||||
# these quotation marks are not correctly rendered in the pdf
|
||||
txt = txt.gsub(/[“â€<C3A2>]/, '"') if txt
|
||||
txt = begin
|
||||
# 0x5c char handling
|
||||
txtar = txt.split('\\')
|
||||
txtar << '' if txt[-1] == ?\\
|
||||
txtar.collect {|x| @ic.iconv(x)}.join('\\').gsub(/\\/, "\\\\\\\\")
|
||||
rescue
|
||||
txt
|
||||
end || ''
|
||||
super w,h,txt,border,ln,align,fill,link
|
||||
end
|
||||
|
||||
def Footer
|
||||
SetFont(@font_for_footer, 'I', 8)
|
||||
SetY(-15)
|
||||
SetX(15)
|
||||
Cell(0, 5, @footer_date, 0, 0, 'L')
|
||||
SetY(-15)
|
||||
SetX(-30)
|
||||
Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a PDF string of a list of issues
|
||||
def issues_to_pdf(issues, project)
|
||||
pdf = IFPDF.new(current_language)
|
||||
title = project ? "#{project} - #{l(:label_issue_plural)}" : "#{l(:label_issue_plural)}"
|
||||
pdf.SetTitle(title)
|
||||
pdf.AliasNbPages
|
||||
pdf.footer_date = format_date(Date.today)
|
||||
pdf.AddPage("L")
|
||||
row_height = 7
|
||||
|
||||
# title
|
||||
pdf.SetFontStyle('B',11)
|
||||
pdf.Cell(190,10, title)
|
||||
pdf.Ln
|
||||
|
||||
# headers
|
||||
pdf.SetFontStyle('B',10)
|
||||
pdf.SetFillColor(230, 230, 230)
|
||||
pdf.Cell(15, row_height, "#", 0, 0, 'L', 1)
|
||||
pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1)
|
||||
pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1)
|
||||
pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1)
|
||||
pdf.Cell(40, row_height, l(:field_assigned_to), 0, 0, 'L', 1)
|
||||
pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1)
|
||||
pdf.Cell(0, row_height, l(:field_subject), 0, 0, 'L', 1)
|
||||
pdf.Line(10, pdf.GetY, 287, pdf.GetY)
|
||||
pdf.Ln
|
||||
pdf.Line(10, pdf.GetY, 287, pdf.GetY)
|
||||
pdf.SetY(pdf.GetY() + 1)
|
||||
|
||||
# rows
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.SetFillColor(255, 255, 255)
|
||||
issues.each do |issue|
|
||||
pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1)
|
||||
pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1)
|
||||
pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1)
|
||||
pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1)
|
||||
pdf.Cell(40, row_height, issue.assigned_to ? issue.assigned_to.to_s : '', 0, 0, 'L', 1)
|
||||
pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1)
|
||||
pdf.MultiCell(0, row_height, (project == issue.project ? issue.subject : "#{issue.project} - #{issue.subject}"))
|
||||
pdf.Line(10, pdf.GetY, 287, pdf.GetY)
|
||||
pdf.SetY(pdf.GetY() + 1)
|
||||
end
|
||||
pdf.Output
|
||||
end
|
||||
|
||||
# Returns a PDF string of a single issue
|
||||
def issue_to_pdf(issue)
|
||||
pdf = IFPDF.new(current_language)
|
||||
pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
|
||||
pdf.AliasNbPages
|
||||
pdf.footer_date = format_date(Date.today)
|
||||
pdf.AddPage
|
||||
|
||||
pdf.SetFontStyle('B',11)
|
||||
pdf.Cell(190,10, "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}")
|
||||
pdf.Ln
|
||||
|
||||
y0 = pdf.GetY
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_status) + ":","LT")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, issue.status.to_s,"RT")
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_priority) + ":","LT")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, issue.priority.to_s,"RT")
|
||||
pdf.Ln
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_author) + ":","L")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, issue.author.to_s,"R")
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_category) + ":","L")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, issue.category.to_s,"R")
|
||||
pdf.Ln
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_created_on) + ":","L")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, format_date(issue.created_on),"R")
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_assigned_to) + ":","L")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, issue.assigned_to.to_s,"R")
|
||||
pdf.Ln
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_updated_on) + ":","LB")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, format_date(issue.updated_on),"RB")
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_due_date) + ":","LB")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(60,5, format_date(issue.due_date),"RB")
|
||||
pdf.Ln
|
||||
|
||||
for custom_value in issue.custom_values
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, custom_value.custom_field.name + ":","L")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.MultiCell(155,5, (show_value custom_value),"R")
|
||||
end
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_subject) + ":","LTB")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.Cell(155,5, issue.subject,"RTB")
|
||||
pdf.Ln
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(35,5, l(:field_description) + ":")
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.MultiCell(155,5, @issue.description,"BR")
|
||||
|
||||
pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
|
||||
pdf.Line(pdf.GetX, pdf.GetY, 170, pdf.GetY)
|
||||
pdf.Ln
|
||||
|
||||
if issue.changesets.any? && User.current.allowed_to?(:view_changesets, issue.project)
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(190,5, l(:label_associated_revisions), "B")
|
||||
pdf.Ln
|
||||
for changeset in issue.changesets
|
||||
pdf.SetFontStyle('B',8)
|
||||
pdf.Cell(190,5, format_time(changeset.committed_on) + " - " + changeset.author.to_s)
|
||||
pdf.Ln
|
||||
unless changeset.comments.blank?
|
||||
pdf.SetFontStyle('',8)
|
||||
pdf.MultiCell(190,5, changeset.comments)
|
||||
end
|
||||
pdf.Ln
|
||||
end
|
||||
end
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(190,5, l(:label_history), "B")
|
||||
pdf.Ln
|
||||
for journal in issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
|
||||
pdf.SetFontStyle('B',8)
|
||||
pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name)
|
||||
pdf.Ln
|
||||
pdf.SetFontStyle('I',8)
|
||||
for detail in journal.details
|
||||
pdf.Cell(190,5, "- " + show_detail(detail, true))
|
||||
pdf.Ln
|
||||
end
|
||||
if journal.notes?
|
||||
pdf.SetFontStyle('',8)
|
||||
pdf.MultiCell(190,5, journal.notes)
|
||||
end
|
||||
pdf.Ln
|
||||
end
|
||||
|
||||
if issue.attachments.any?
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.Cell(190,5, l(:label_attachment_plural), "B")
|
||||
pdf.Ln
|
||||
for attachment in issue.attachments
|
||||
pdf.SetFontStyle('',8)
|
||||
pdf.Cell(80,5, attachment.filename)
|
||||
pdf.Cell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
|
||||
pdf.Cell(25,5, format_date(attachment.created_on),0,0,"R")
|
||||
pdf.Cell(65,5, attachment.author.name,0,0,"R")
|
||||
pdf.Ln
|
||||
end
|
||||
end
|
||||
pdf.Output
|
||||
end
|
||||
|
||||
# Returns a PDF string of a gantt chart
|
||||
def gantt_to_pdf(gantt, project)
|
||||
pdf = IFPDF.new(current_language)
|
||||
pdf.SetTitle("#{l(:label_gantt)} #{project}")
|
||||
pdf.AliasNbPages
|
||||
pdf.footer_date = format_date(Date.today)
|
||||
pdf.AddPage("L")
|
||||
pdf.SetFontStyle('B',12)
|
||||
pdf.SetX(15)
|
||||
pdf.Cell(70, 20, project.to_s)
|
||||
pdf.Ln
|
||||
pdf.SetFontStyle('B',9)
|
||||
|
||||
subject_width = 70
|
||||
header_heigth = 5
|
||||
|
||||
headers_heigth = header_heigth
|
||||
show_weeks = false
|
||||
show_days = false
|
||||
|
||||
if gantt.months < 7
|
||||
show_weeks = true
|
||||
headers_heigth = 2*header_heigth
|
||||
if gantt.months < 3
|
||||
show_days = true
|
||||
headers_heigth = 3*header_heigth
|
||||
end
|
||||
end
|
||||
|
||||
g_width = 210
|
||||
zoom = (g_width) / (gantt.date_to - gantt.date_from + 1)
|
||||
g_height = 120
|
||||
t_height = g_height + headers_heigth
|
||||
|
||||
y_start = pdf.GetY
|
||||
|
||||
# Months headers
|
||||
month_f = gantt.date_from
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
gantt.months.times do
|
||||
width = ((month_f >> 1) - month_f) * zoom
|
||||
pdf.SetY(y_start)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
|
||||
left = left + width
|
||||
month_f = month_f >> 1
|
||||
end
|
||||
|
||||
# Weeks headers
|
||||
if show_weeks
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
if gantt.date_from.cwday == 1
|
||||
# gantt.date_from is monday
|
||||
week_f = gantt.date_from
|
||||
else
|
||||
# find next monday after gantt.date_from
|
||||
week_f = gantt.date_from + (7 - gantt.date_from.cwday + 1)
|
||||
width = (7 - gantt.date_from.cwday + 1) * zoom-1
|
||||
pdf.SetY(y_start + header_heigth)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width + 1, height, "", "LTR")
|
||||
left = left + width+1
|
||||
end
|
||||
while week_f <= gantt.date_to
|
||||
width = (week_f + 6 <= gantt.date_to) ? 7 * zoom : (gantt.date_to - week_f + 1) * zoom
|
||||
pdf.SetY(y_start + header_heigth)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
|
||||
left = left + width
|
||||
week_f = week_f+7
|
||||
end
|
||||
end
|
||||
|
||||
# Days headers
|
||||
if show_days
|
||||
left = subject_width
|
||||
height = header_heigth
|
||||
wday = gantt.date_from.cwday
|
||||
pdf.SetFontStyle('B',7)
|
||||
(gantt.date_to - gantt.date_from + 1).to_i.times do
|
||||
width = zoom
|
||||
pdf.SetY(y_start + 2 * header_heigth)
|
||||
pdf.SetX(left)
|
||||
pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C")
|
||||
left = left + width
|
||||
wday = wday + 1
|
||||
wday = 1 if wday > 7
|
||||
end
|
||||
end
|
||||
|
||||
pdf.SetY(y_start)
|
||||
pdf.SetX(15)
|
||||
pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
|
||||
|
||||
# Tasks
|
||||
top = headers_heigth + y_start
|
||||
pdf.SetFontStyle('B',7)
|
||||
gantt.events.each do |i|
|
||||
pdf.SetY(top)
|
||||
pdf.SetX(15)
|
||||
|
||||
if i.is_a? Issue
|
||||
pdf.Cell(subject_width-15, 5, "#{i.tracker} #{i.id}: #{i.subject}".sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)'), "LR")
|
||||
else
|
||||
pdf.Cell(subject_width-15, 5, "#{l(:label_version)}: #{i.name}", "LR")
|
||||
end
|
||||
|
||||
pdf.SetY(top)
|
||||
pdf.SetX(subject_width)
|
||||
pdf.Cell(g_width, 5, "", "LR")
|
||||
|
||||
pdf.SetY(top+1.5)
|
||||
|
||||
if i.is_a? Issue
|
||||
i_start_date = (i.start_date >= gantt.date_from ? i.start_date : gantt.date_from )
|
||||
i_end_date = (i.due_before <= gantt.date_to ? i.due_before : gantt.date_to )
|
||||
|
||||
i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor
|
||||
i_done_date = (i_done_date <= gantt.date_from ? gantt.date_from : i_done_date )
|
||||
i_done_date = (i_done_date >= gantt.date_to ? gantt.date_to : i_done_date )
|
||||
|
||||
i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
|
||||
|
||||
i_left = ((i_start_date - gantt.date_from)*zoom)
|
||||
i_width = ((i_end_date - i_start_date + 1)*zoom)
|
||||
d_width = ((i_done_date - i_start_date)*zoom)
|
||||
l_width = ((i_late_date - i_start_date+1)*zoom) if i_late_date
|
||||
l_width ||= 0
|
||||
|
||||
pdf.SetX(subject_width + i_left)
|
||||
pdf.SetFillColor(200,200,200)
|
||||
pdf.Cell(i_width, 2, "", 0, 0, "", 1)
|
||||
|
||||
if l_width > 0
|
||||
pdf.SetY(top+1.5)
|
||||
pdf.SetX(subject_width + i_left)
|
||||
pdf.SetFillColor(255,100,100)
|
||||
pdf.Cell(l_width, 2, "", 0, 0, "", 1)
|
||||
end
|
||||
if d_width > 0
|
||||
pdf.SetY(top+1.5)
|
||||
pdf.SetX(subject_width + i_left)
|
||||
pdf.SetFillColor(100,100,255)
|
||||
pdf.Cell(d_width, 2, "", 0, 0, "", 1)
|
||||
end
|
||||
|
||||
pdf.SetY(top+1.5)
|
||||
pdf.SetX(subject_width + i_left + i_width)
|
||||
pdf.Cell(30, 2, "#{i.status} #{i.done_ratio}%")
|
||||
else
|
||||
i_left = ((i.start_date - gantt.date_from)*zoom)
|
||||
|
||||
pdf.SetX(subject_width + i_left)
|
||||
pdf.SetFillColor(50,200,50)
|
||||
pdf.Cell(2, 2, "", 0, 0, "", 1)
|
||||
|
||||
pdf.SetY(top+1.5)
|
||||
pdf.SetX(subject_width + i_left + 3)
|
||||
pdf.Cell(30, 2, "#{i.name}")
|
||||
end
|
||||
|
||||
top = top + 5
|
||||
pdf.SetDrawColor(200, 200, 200)
|
||||
pdf.Line(15, top, subject_width+g_width, top)
|
||||
if pdf.GetY() > 180
|
||||
pdf.AddPage("L")
|
||||
top = 20
|
||||
pdf.Line(15, top, subject_width+g_width, top)
|
||||
end
|
||||
pdf.SetDrawColor(0, 0, 0)
|
||||
end
|
||||
|
||||
pdf.Line(15, top, subject_width+g_width, top)
|
||||
pdf.Output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
module Redmine
|
||||
module Hook
|
||||
include ActionController::UrlWriter
|
||||
|
||||
@@listener_classes = []
|
||||
@@listeners = nil
|
||||
@@hook_listeners = {}
|
||||
@@ -57,12 +55,11 @@ module Redmine
|
||||
# Calls a hook.
|
||||
# Returns the listeners response.
|
||||
def call_hook(hook, context={})
|
||||
returning [] do |response|
|
||||
hls = hook_listeners(hook)
|
||||
if hls.any?
|
||||
hls.each {|listener| response << listener.send(hook, context)}
|
||||
end
|
||||
response = ''
|
||||
hook_listeners(hook).each do |listener|
|
||||
response << listener.send(hook, context).to_s
|
||||
end
|
||||
response
|
||||
end
|
||||
end
|
||||
|
||||
@@ -76,9 +73,8 @@ module Redmine
|
||||
Redmine::Hook.add_listener(child)
|
||||
super
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
# Listener class used for views hooks.
|
||||
# Listeners that inherit this class will include various helpers by default.
|
||||
class ViewListener < Listener
|
||||
@@ -95,54 +91,17 @@ module Redmine
|
||||
include ActionView::Helpers::TextHelper
|
||||
include ActionController::UrlWriter
|
||||
include ApplicationHelper
|
||||
|
||||
# Default to creating links using only the path. Subclasses can
|
||||
# change this default as needed
|
||||
def self.default_url_options
|
||||
{:only_path => true }
|
||||
end
|
||||
|
||||
# Helper method to directly render a partial using the context:
|
||||
#
|
||||
# class MyHook < Redmine::Hook::ViewListener
|
||||
# render_on :view_issues_show_details_bottom, :partial => "show_more_data"
|
||||
# end
|
||||
#
|
||||
def self.render_on(hook, options={})
|
||||
define_method hook do |context|
|
||||
context[:controller].send(:render_to_string, {:locals => context}.merge(options))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Helper module included in ApplicationHelper and ActionControllerso that
|
||||
# hooks can be called in views like this:
|
||||
#
|
||||
# Helper module included in ApplicationHelper so that hooks can be called
|
||||
# in views like this:
|
||||
# <%= call_hook(:some_hook) %>
|
||||
# <%= call_hook(:another_hook, :foo => 'bar' %>
|
||||
#
|
||||
# Or in controllers like:
|
||||
# call_hook(:some_hook)
|
||||
# call_hook(:another_hook, :foo => 'bar'
|
||||
#
|
||||
# Hooks added to views will be concatenated into a string. Hooks added to
|
||||
# controllers will return an array of results.
|
||||
#
|
||||
# Several objects are automatically added to the call context:
|
||||
#
|
||||
# * project => current project
|
||||
# * request => Request instance
|
||||
# * controller => current Controller instance
|
||||
#
|
||||
# Current project is automatically added to the call context.
|
||||
module Helper
|
||||
def call_hook(hook, context={})
|
||||
if is_a?(ActionController::Base)
|
||||
default_context = {:controller => self, :project => @project, :request => request}
|
||||
Redmine::Hook.call_hook(hook, default_context.merge(context))
|
||||
else
|
||||
default_context = {:controller => controller, :project => @project, :request => request}
|
||||
Redmine::Hook.call_hook(hook, default_context.merge(context)).join(' ')
|
||||
end
|
||||
Redmine::Hook.call_hook(hook, {:project => @project}.merge(context))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Redmine - project management software
|
||||
# redMine - project management software
|
||||
# Copyright (C) 2006-2008 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
@@ -33,18 +33,9 @@ module Redmine
|
||||
msg = imap.fetch(message_id,'RFC822')[0].attr['RFC822']
|
||||
logger.debug "Receiving message #{message_id}" if logger && logger.debug?
|
||||
if MailHandler.receive(msg, options)
|
||||
logger.debug "Message #{message_id} successfully received" if logger && logger.debug?
|
||||
if imap_options[:move_on_success]
|
||||
imap.copy(message_id, imap_options[:move_on_success])
|
||||
end
|
||||
imap.store(message_id, "+FLAGS", [:Seen, :Deleted])
|
||||
else
|
||||
logger.debug "Message #{message_id} can not be processed" if logger && logger.debug?
|
||||
imap.store(message_id, "+FLAGS", [:Seen])
|
||||
if imap_options[:move_on_failure]
|
||||
imap.copy(message_id, imap_options[:move_on_failure])
|
||||
imap.store(message_id, "+FLAGS", [:Deleted])
|
||||
end
|
||||
end
|
||||
end
|
||||
imap.expunge
|
||||
|
||||
@@ -52,19 +52,8 @@ module Redmine
|
||||
|
||||
# Returns the menu item name according to the current action
|
||||
def current_menu_item
|
||||
@current_menu_item ||= menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
|
||||
menu_items[controller_name.to_sym][:default]
|
||||
end
|
||||
|
||||
# Redirects user to the menu item of the given project
|
||||
# Returns false if user is not authorized
|
||||
def redirect_to_project_menu_item(project, name)
|
||||
item = Redmine::MenuManager.items(:project_menu).detect {|i| i.name.to_s == name.to_s}
|
||||
if item && User.current.allowed_to?(item.url, project) && (item.condition.nil? || item.condition.call(project))
|
||||
redirect_to({item.param => project}.merge(item.url))
|
||||
return true
|
||||
end
|
||||
false
|
||||
menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
|
||||
menu_items[controller_name.to_sym][:default]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user