Revision 1298:4f746d8966dd .svn/pristine/40
| .svn/pristine/40/4020a77b0d5d691da1ac32e725f8f600aff372b8.svn-base | ||
|---|---|---|
| 1 |
<h2><%= class_name %>#<%= action %></h2> |
|
| .svn/pristine/40/4038e83eefe53cf4e2ba6b2f10fddf413b1d2edb.svn-base | ||
|---|---|---|
| 1 |
// ** I18N |
|
| 2 |
|
|
| 3 |
// Calendar GL (galician) language |
|
| 4 |
// Author: Martín Vázquez Cabanas, <eu@martinvazquez.net> |
|
| 5 |
// Updated: 2009-01-23 |
|
| 6 |
// Encoding: utf-8 |
|
| 7 |
// Distributed under the same terms as the calendar itself. |
|
| 8 |
|
|
| 9 |
// For translators: please use UTF-8 if possible. We strongly believe that |
|
| 10 |
// Unicode is the answer to a real internationalized world. Also please |
|
| 11 |
// include your contact information in the header, as can be seen above. |
|
| 12 |
|
|
| 13 |
// full day names |
|
| 14 |
Calendar._DN = new Array |
|
| 15 |
("Domingo",
|
|
| 16 |
"Luns", |
|
| 17 |
"Martes", |
|
| 18 |
"Mércores", |
|
| 19 |
"Xoves", |
|
| 20 |
"Venres", |
|
| 21 |
"Sábado", |
|
| 22 |
"Domingo"); |
|
| 23 |
|
|
| 24 |
// Please note that the following array of short day names (and the same goes |
|
| 25 |
// for short month names, _SMN) isn't absolutely necessary. We give it here |
|
| 26 |
// for exemplification on how one can customize the short day names, but if |
|
| 27 |
// they are simply the first N letters of the full name you can simply say: |
|
| 28 |
// |
|
| 29 |
// Calendar._SDN_len = N; // short day name length |
|
| 30 |
// Calendar._SMN_len = N; // short month name length |
|
| 31 |
// |
|
| 32 |
// If N = 3 then this is not needed either since we assume a value of 3 if not |
|
| 33 |
// present, to be compatible with translation files that were written before |
|
| 34 |
// this feature. |
|
| 35 |
|
|
| 36 |
// short day names |
|
| 37 |
Calendar._SDN = new Array |
|
| 38 |
("Dom",
|
|
| 39 |
"Lun", |
|
| 40 |
"Mar", |
|
| 41 |
"Mér", |
|
| 42 |
"Xov", |
|
| 43 |
"Ven", |
|
| 44 |
"Sáb", |
|
| 45 |
"Dom"); |
|
| 46 |
|
|
| 47 |
// First day of the week. "0" means display Sunday first, "1" means display |
|
| 48 |
// Monday first, etc. |
|
| 49 |
Calendar._FD = 1; |
|
| 50 |
|
|
| 51 |
// full month names |
|
| 52 |
Calendar._MN = new Array |
|
| 53 |
("Xaneiro",
|
|
| 54 |
"Febreiro", |
|
| 55 |
"Marzo", |
|
| 56 |
"Abril", |
|
| 57 |
"Maio", |
|
| 58 |
"Xuño", |
|
| 59 |
"Xullo", |
|
| 60 |
"Agosto", |
|
| 61 |
"Setembro", |
|
| 62 |
"Outubro", |
|
| 63 |
"Novembro", |
|
| 64 |
"Decembro"); |
|
| 65 |
|
|
| 66 |
// short month names |
|
| 67 |
Calendar._SMN = new Array |
|
| 68 |
("Xan",
|
|
| 69 |
"Feb", |
|
| 70 |
"Mar", |
|
| 71 |
"Abr", |
|
| 72 |
"Mai", |
|
| 73 |
"Xun", |
|
| 74 |
"Xull", |
|
| 75 |
"Ago", |
|
| 76 |
"Set", |
|
| 77 |
"Out", |
|
| 78 |
"Nov", |
|
| 79 |
"Dec"); |
|
| 80 |
|
|
| 81 |
// tooltips |
|
| 82 |
Calendar._TT = {};
|
|
| 83 |
Calendar._TT["INFO"] = "Acerca do calendario"; |
|
| 84 |
|
|
| 85 |
Calendar._TT["ABOUT"] = |
|
| 86 |
"Selector DHTML de Data/Hora\n" + |
|
| 87 |
"(c) dynarch.com 2002-2005 / Author: Mihai Bazon\n" + // don't translate this this ;-) |
|
| 88 |
"Para conseguila última versión visite: http://www.dynarch.com/projects/calendar/\n" + |
|
| 89 |
"Distribuído baixo licenza GNU LGPL. Visite http://gnu.org/licenses/lgpl.html para máis detalles." + |
|
| 90 |
"\n\n" + |
|
| 91 |
"Selección de data:\n" + |
|
| 92 |
"- Use os botóns \xab, \xbb para seleccionalo ano\n" + |
|
| 93 |
"- Use os botóns " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " para seleccionalo mes\n" + |
|
| 94 |
"- Manteña pulsado o rato en calquera destes botóns para unha selección rápida."; |
|
| 95 |
Calendar._TT["ABOUT_TIME"] = "\n\n" + |
|
| 96 |
"Selección de hora:\n" + |
|
| 97 |
"- Pulse en calquera das partes da hora para incrementala\n" + |
|
| 98 |
"- ou pulse maiúsculas mentres fai clic para decrementala\n" + |
|
| 99 |
"- ou faga clic e arrastre o rato para unha selección máis rápida."; |
|
| 100 |
|
|
| 101 |
Calendar._TT["PREV_YEAR"] = "Ano anterior (manter para menú)"; |
|
| 102 |
Calendar._TT["PREV_MONTH"] = "Mes anterior (manter para menú)"; |
|
| 103 |
Calendar._TT["GO_TODAY"] = "Ir a hoxe"; |
|
| 104 |
Calendar._TT["NEXT_MONTH"] = "Mes seguinte (manter para menú)"; |
|
| 105 |
Calendar._TT["NEXT_YEAR"] = "Ano seguinte (manter para menú)"; |
|
| 106 |
Calendar._TT["SEL_DATE"] = "Seleccionar data"; |
|
| 107 |
Calendar._TT["DRAG_TO_MOVE"] = "Arrastrar para mover"; |
|
| 108 |
Calendar._TT["PART_TODAY"] = " (hoxe)"; |
|
| 109 |
|
|
| 110 |
// the following is to inform that "%s" is to be the first day of week |
|
| 111 |
// %s will be replaced with the day name. |
|
| 112 |
Calendar._TT["DAY_FIRST"] = "Facer %s primeiro día da semana"; |
|
| 113 |
|
|
| 114 |
// This may be locale-dependent. It specifies the week-end days, as an array |
|
| 115 |
// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 |
|
| 116 |
// means Monday, etc. |
|
| 117 |
Calendar._TT["WEEKEND"] = "0,6"; |
|
| 118 |
|
|
| 119 |
Calendar._TT["CLOSE"] = "Pechar"; |
|
| 120 |
Calendar._TT["TODAY"] = "Hoxe"; |
|
| 121 |
Calendar._TT["TIME_PART"] = "(Maiúscula-)Clic ou arrastre para cambiar valor"; |
|
| 122 |
|
|
| 123 |
// date formats |
|
| 124 |
Calendar._TT["DEF_DATE_FORMAT"] = "%d/%m/%Y"; |
|
| 125 |
Calendar._TT["TT_DATE_FORMAT"] = "%A, %e de %B de %Y"; |
|
| 126 |
|
|
| 127 |
Calendar._TT["WK"] = "sem"; |
|
| 128 |
Calendar._TT["TIME"] = "Hora:"; |
|
| .svn/pristine/40/40689c0c3542244c3df167e6c12a7f9af9376e9b.svn-base | ||
|---|---|---|
| 1 |
# encoding: utf-8 |
|
| 2 |
# |
|
| 3 |
# Redmine - project management software |
|
| 4 |
# Copyright (C) 2006-2013 Jean-Philippe Lang |
|
| 5 |
# |
|
| 6 |
# This program is free software; you can redistribute it and/or |
|
| 7 |
# modify it under the terms of the GNU General Public License |
|
| 8 |
# as published by the Free Software Foundation; either version 2 |
|
| 9 |
# of the License, or (at your option) any later version. |
|
| 10 |
# |
|
| 11 |
# This program is distributed in the hope that it will be useful, |
|
| 12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 14 |
# GNU General Public License for more details. |
|
| 15 |
# |
|
| 16 |
# You should have received a copy of the GNU General Public License |
|
| 17 |
# along with this program; if not, write to the Free Software |
|
| 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
| 19 |
|
|
| 20 |
module DocumentsHelper |
|
| 21 |
end |
|
| .svn/pristine/40/406f7b24c26c10e7c0b7a7dfc884f38ff3b10cd2.svn-base | ||
|---|---|---|
| 1 |
# Redmine - project management software |
|
| 2 |
# Copyright (C) 2006-2011 Jean-Philippe Lang |
|
| 3 |
# |
|
| 4 |
# This program is free software; you can redistribute it and/or |
|
| 5 |
# modify it under the terms of the GNU General Public License |
|
| 6 |
# as published by the Free Software Foundation; either version 2 |
|
| 7 |
# of the License, or (at your option) any later version. |
|
| 8 |
# |
|
| 9 |
# This program is distributed in the hope that it will be useful, |
|
| 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 12 |
# GNU General Public License for more details. |
|
| 13 |
# |
|
| 14 |
# You should have received a copy of the GNU General Public License |
|
| 15 |
# along with this program; if not, write to the Free Software |
|
| 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
| 17 |
|
|
| 18 |
require 'SVG/Graph/Bar' |
|
| 19 |
require 'SVG/Graph/BarHorizontal' |
|
| 20 |
require 'digest/sha1' |
|
| 21 |
|
|
| 22 |
class ChangesetNotFound < Exception; end |
|
| 23 |
class InvalidRevisionParam < Exception; end |
|
| 24 |
|
|
| 25 |
class RepositoriesController < ApplicationController |
|
| 26 |
menu_item :repository |
|
| 27 |
menu_item :settings, :only => :edit |
|
| 28 |
default_search_scope :changesets |
|
| 29 |
|
|
| 30 |
before_filter :find_repository, :except => :edit |
|
| 31 |
before_filter :find_project, :only => :edit |
|
| 32 |
before_filter :authorize |
|
| 33 |
accept_rss_auth :revisions |
|
| 34 |
|
|
| 35 |
rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed |
|
| 36 |
|
|
| 37 |
def edit |
|
| 38 |
@repository = @project.repository |
|
| 39 |
if !@repository && !params[:repository_scm].blank? |
|
| 40 |
@repository = Repository.factory(params[:repository_scm]) |
|
| 41 |
@repository.project = @project if @repository |
|
| 42 |
end |
|
| 43 |
if request.post? && @repository |
|
| 44 |
p1 = params[:repository] |
|
| 45 |
p = {}
|
|
| 46 |
p_extra = {}
|
|
| 47 |
p1.each do |k, v| |
|
| 48 |
if k =~ /^extra_/ |
|
| 49 |
p_extra[k] = v |
|
| 50 |
else |
|
| 51 |
p[k] = v |
|
| 52 |
end |
|
| 53 |
end |
|
| 54 |
@repository.attributes = p |
|
| 55 |
@repository.merge_extra_info(p_extra) |
|
| 56 |
@repository.save |
|
| 57 |
end |
|
| 58 |
render(:update) do |page| |
|
| 59 |
page.replace_html "tab-content-repository", |
|
| 60 |
:partial => 'projects/settings/repository' |
|
| 61 |
if @repository && !@project.repository |
|
| 62 |
@project.reload # needed to reload association |
|
| 63 |
page.replace_html "main-menu", render_main_menu(@project) |
|
| 64 |
end |
|
| 65 |
end |
|
| 66 |
end |
|
| 67 |
|
|
| 68 |
def committers |
|
| 69 |
@committers = @repository.committers |
|
| 70 |
@users = @project.users |
|
| 71 |
additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id) |
|
| 72 |
@users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty? |
|
| 73 |
@users.compact! |
|
| 74 |
@users.sort! |
|
| 75 |
if request.post? && params[:committers].is_a?(Hash) |
|
| 76 |
# Build a hash with repository usernames as keys and corresponding user ids as values |
|
| 77 |
@repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
|
|
| 78 |
flash[:notice] = l(:notice_successful_update) |
|
| 79 |
redirect_to :action => 'committers', :id => @project |
|
| 80 |
end |
|
| 81 |
end |
|
| 82 |
|
|
| 83 |
def destroy |
|
| 84 |
@repository.destroy |
|
| 85 |
redirect_to :controller => 'projects', |
|
| 86 |
:action => 'settings', |
|
| 87 |
:id => @project, |
|
| 88 |
:tab => 'repository' |
|
| 89 |
end |
|
| 90 |
|
|
| 91 |
def show |
|
| 92 |
@repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty? |
|
| 93 |
|
|
| 94 |
@entries = @repository.entries(@path, @rev) |
|
| 95 |
@changeset = @repository.find_changeset_by_name(@rev) |
|
| 96 |
if request.xhr? |
|
| 97 |
@entries ? render(:partial => 'dir_list_content') : render(:nothing => true) |
|
| 98 |
else |
|
| 99 |
(show_error_not_found; return) unless @entries |
|
| 100 |
@changesets = @repository.latest_changesets(@path, @rev) |
|
| 101 |
@properties = @repository.properties(@path, @rev) |
|
| 102 |
render :action => 'show' |
|
| 103 |
end |
|
| 104 |
end |
|
| 105 |
|
|
| 106 |
alias_method :browse, :show |
|
| 107 |
|
|
| 108 |
def changes |
|
| 109 |
@entry = @repository.entry(@path, @rev) |
|
| 110 |
(show_error_not_found; return) unless @entry |
|
| 111 |
@changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i) |
|
| 112 |
@properties = @repository.properties(@path, @rev) |
|
| 113 |
@changeset = @repository.find_changeset_by_name(@rev) |
|
| 114 |
end |
|
| 115 |
|
|
| 116 |
def revisions |
|
| 117 |
@changeset_count = @repository.changesets.count |
|
| 118 |
@changeset_pages = Paginator.new self, @changeset_count, |
|
| 119 |
per_page_option, |
|
| 120 |
params['page'] |
|
| 121 |
@changesets = @repository.changesets.find(:all, |
|
| 122 |
:limit => @changeset_pages.items_per_page, |
|
| 123 |
:offset => @changeset_pages.current.offset, |
|
| 124 |
:include => [:user, :repository, :parents]) |
|
| 125 |
|
|
| 126 |
respond_to do |format| |
|
| 127 |
format.html { render :layout => false if request.xhr? }
|
|
| 128 |
format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
|
|
| 129 |
end |
|
| 130 |
end |
|
| 131 |
|
|
| 132 |
def entry |
|
| 133 |
@entry = @repository.entry(@path, @rev) |
|
| 134 |
(show_error_not_found; return) unless @entry |
|
| 135 |
|
|
| 136 |
# If the entry is a dir, show the browser |
|
| 137 |
(show; return) if @entry.is_dir? |
|
| 138 |
|
|
| 139 |
@content = @repository.cat(@path, @rev) |
|
| 140 |
(show_error_not_found; return) unless @content |
|
| 141 |
if 'raw' == params[:format] || |
|
| 142 |
(@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) || |
|
| 143 |
! is_entry_text_data?(@content, @path) |
|
| 144 |
# Force the download |
|
| 145 |
send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
|
|
| 146 |
send_type = Redmine::MimeType.of(@path) |
|
| 147 |
send_opt[:type] = send_type.to_s if send_type |
|
| 148 |
send_data @content, send_opt |
|
| 149 |
else |
|
| 150 |
# Prevent empty lines when displaying a file with Windows style eol |
|
| 151 |
# TODO: UTF-16 |
|
| 152 |
# Is this needs? AttachmentsController reads file simply. |
|
| 153 |
@content.gsub!("\r\n", "\n")
|
|
| 154 |
@changeset = @repository.find_changeset_by_name(@rev) |
|
| 155 |
end |
|
| 156 |
end |
|
| 157 |
|
|
| 158 |
def is_entry_text_data?(ent, path) |
|
| 159 |
# UTF-16 contains "\x00". |
|
| 160 |
# It is very strict that file contains less than 30% of ascii symbols |
|
| 161 |
# in non Western Europe. |
|
| 162 |
return true if Redmine::MimeType.is_type?('text', path)
|
|
| 163 |
# Ruby 1.8.6 has a bug of integer divisions. |
|
| 164 |
# http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F |
|
| 165 |
return false if ent.is_binary_data? |
|
| 166 |
true |
|
| 167 |
end |
|
| 168 |
private :is_entry_text_data? |
|
| 169 |
|
|
| 170 |
def annotate |
|
| 171 |
@entry = @repository.entry(@path, @rev) |
|
| 172 |
(show_error_not_found; return) unless @entry |
|
| 173 |
|
|
| 174 |
@annotate = @repository.scm.annotate(@path, @rev) |
|
| 175 |
if @annotate.nil? || @annotate.empty? |
|
| 176 |
(render_error l(:error_scm_annotate); return) |
|
| 177 |
end |
|
| 178 |
ann_buf_size = 0 |
|
| 179 |
@annotate.lines.each do |buf| |
|
| 180 |
ann_buf_size += buf.size |
|
| 181 |
end |
|
| 182 |
if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte |
|
| 183 |
(render_error l(:error_scm_annotate_big_text_file); return) |
|
| 184 |
end |
|
| 185 |
@changeset = @repository.find_changeset_by_name(@rev) |
|
| 186 |
end |
|
| 187 |
|
|
| 188 |
def revision |
|
| 189 |
raise ChangesetNotFound if @rev.blank? |
|
| 190 |
@changeset = @repository.find_changeset_by_name(@rev) |
|
| 191 |
raise ChangesetNotFound unless @changeset |
|
| 192 |
|
|
| 193 |
respond_to do |format| |
|
| 194 |
format.html |
|
| 195 |
format.js {render :layout => false}
|
|
| 196 |
end |
|
| 197 |
rescue ChangesetNotFound |
|
| 198 |
show_error_not_found |
|
| 199 |
end |
|
| 200 |
|
|
| 201 |
def diff |
|
| 202 |
if params[:format] == 'diff' |
|
| 203 |
@diff = @repository.diff(@path, @rev, @rev_to) |
|
| 204 |
(show_error_not_found; return) unless @diff |
|
| 205 |
filename = "changeset_r#{@rev}"
|
|
| 206 |
filename << "_r#{@rev_to}" if @rev_to
|
|
| 207 |
send_data @diff.join, :filename => "#{filename}.diff",
|
|
| 208 |
:type => 'text/x-patch', |
|
| 209 |
:disposition => 'attachment' |
|
| 210 |
else |
|
| 211 |
@diff_type = params[:type] || User.current.pref[:diff_type] || 'inline' |
|
| 212 |
@diff_type = 'inline' unless %w(inline sbs).include?(@diff_type) |
|
| 213 |
|
|
| 214 |
# Save diff type as user preference |
|
| 215 |
if User.current.logged? && @diff_type != User.current.pref[:diff_type] |
|
| 216 |
User.current.pref[:diff_type] = @diff_type |
|
| 217 |
User.current.preference.save |
|
| 218 |
end |
|
| 219 |
@cache_key = "repositories/diff/#{@repository.id}/" +
|
|
| 220 |
Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
|
|
| 221 |
unless read_fragment(@cache_key) |
|
| 222 |
@diff = @repository.diff(@path, @rev, @rev_to) |
|
| 223 |
show_error_not_found unless @diff |
|
| 224 |
end |
|
| 225 |
|
|
| 226 |
@changeset = @repository.find_changeset_by_name(@rev) |
|
| 227 |
@changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil |
|
| 228 |
@diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to) |
|
| 229 |
end |
|
| 230 |
end |
|
| 231 |
|
|
| 232 |
def stats |
|
| 233 |
end |
|
| 234 |
|
|
| 235 |
def graph |
|
| 236 |
data = nil |
|
| 237 |
case params[:graph] |
|
| 238 |
when "commits_per_month" |
|
| 239 |
data = graph_commits_per_month(@repository) |
|
| 240 |
when "commits_per_author" |
|
| 241 |
data = graph_commits_per_author(@repository) |
|
| 242 |
end |
|
| 243 |
if data |
|
| 244 |
headers["Content-Type"] = "image/svg+xml" |
|
| 245 |
send_data(data, :type => "image/svg+xml", :disposition => "inline") |
|
| 246 |
else |
|
| 247 |
render_404 |
|
| 248 |
end |
|
| 249 |
end |
|
| 250 |
|
|
| 251 |
private |
|
| 252 |
|
|
| 253 |
REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
|
|
| 254 |
|
|
| 255 |
def find_repository |
|
| 256 |
@project = Project.find(params[:id]) |
|
| 257 |
@repository = @project.repository |
|
| 258 |
(render_404; return false) unless @repository |
|
| 259 |
@path = params[:path].join('/') unless params[:path].nil?
|
|
| 260 |
@path ||= '' |
|
| 261 |
@rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip |
|
| 262 |
@rev_to = params[:rev_to] |
|
| 263 |
|
|
| 264 |
unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE) |
|
| 265 |
if @repository.branches.blank? |
|
| 266 |
raise InvalidRevisionParam |
|
| 267 |
end |
|
| 268 |
end |
|
| 269 |
rescue ActiveRecord::RecordNotFound |
|
| 270 |
render_404 |
|
| 271 |
rescue InvalidRevisionParam |
|
| 272 |
show_error_not_found |
|
| 273 |
end |
|
| 274 |
|
|
| 275 |
def show_error_not_found |
|
| 276 |
render_error :message => l(:error_scm_not_found), :status => 404 |
|
| 277 |
end |
|
| 278 |
|
|
| 279 |
# Handler for Redmine::Scm::Adapters::CommandFailed exception |
|
| 280 |
def show_error_command_failed(exception) |
|
| 281 |
render_error l(:error_scm_command_failed, exception.message) |
|
| 282 |
end |
|
| 283 |
|
|
| 284 |
def graph_commits_per_month(repository) |
|
| 285 |
@date_to = Date.today |
|
| 286 |
@date_from = @date_to << 11 |
|
| 287 |
@date_from = Date.civil(@date_from.year, @date_from.month, 1) |
|
| 288 |
commits_by_day = repository.changesets.count( |
|
| 289 |
:all, :group => :commit_date, |
|
| 290 |
:conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to]) |
|
| 291 |
commits_by_month = [0] * 12 |
|
| 292 |
commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
|
|
| 293 |
|
|
| 294 |
changes_by_day = repository.changes.count( |
|
| 295 |
:all, :group => :commit_date, |
|
| 296 |
:conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to]) |
|
| 297 |
changes_by_month = [0] * 12 |
|
| 298 |
changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
|
|
| 299 |
|
|
| 300 |
fields = [] |
|
| 301 |
12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
|
|
| 302 |
|
|
| 303 |
graph = SVG::Graph::Bar.new( |
|
| 304 |
:height => 300, |
|
| 305 |
:width => 800, |
|
| 306 |
:fields => fields.reverse, |
|
| 307 |
:stack => :side, |
|
| 308 |
:scale_integers => true, |
|
| 309 |
:step_x_labels => 2, |
|
| 310 |
:show_data_values => false, |
|
| 311 |
:graph_title => l(:label_commits_per_month), |
|
| 312 |
:show_graph_title => true |
|
| 313 |
) |
|
| 314 |
|
|
| 315 |
graph.add_data( |
|
| 316 |
:data => commits_by_month[0..11].reverse, |
|
| 317 |
:title => l(:label_revision_plural) |
|
| 318 |
) |
|
| 319 |
|
|
| 320 |
graph.add_data( |
|
| 321 |
:data => changes_by_month[0..11].reverse, |
|
| 322 |
:title => l(:label_change_plural) |
|
| 323 |
) |
|
| 324 |
|
|
| 325 |
graph.burn |
|
| 326 |
end |
|
| 327 |
|
|
| 328 |
def graph_commits_per_author(repository) |
|
| 329 |
commits_by_author = repository.changesets.count(:all, :group => :committer) |
|
| 330 |
commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
|
|
| 331 |
|
|
| 332 |
changes_by_author = repository.changes.count(:all, :group => :committer) |
|
| 333 |
h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
|
|
| 334 |
|
|
| 335 |
fields = commits_by_author.collect {|r| r.first}
|
|
| 336 |
commits_data = commits_by_author.collect {|r| r.last}
|
|
| 337 |
changes_data = commits_by_author.collect {|r| h[r.first] || 0}
|
|
| 338 |
|
|
| 339 |
fields = fields + [""]*(10 - fields.length) if fields.length<10 |
|
| 340 |
commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10 |
|
| 341 |
changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10 |
|
| 342 |
|
|
| 343 |
# Remove email adress in usernames |
|
| 344 |
fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
|
|
| 345 |
|
|
| 346 |
graph = SVG::Graph::BarHorizontal.new( |
|
| 347 |
:height => 400, |
|
| 348 |
:width => 800, |
|
| 349 |
:fields => fields, |
|
| 350 |
:stack => :side, |
|
| 351 |
:scale_integers => true, |
|
| 352 |
:show_data_values => false, |
|
| 353 |
:rotate_y_labels => false, |
|
| 354 |
:graph_title => l(:label_commits_per_author), |
|
| 355 |
:show_graph_title => true |
|
| 356 |
) |
|
| 357 |
graph.add_data( |
|
| 358 |
:data => commits_data, |
|
| 359 |
:title => l(:label_revision_plural) |
|
| 360 |
) |
|
| 361 |
graph.add_data( |
|
| 362 |
:data => changes_data, |
|
| 363 |
:title => l(:label_change_plural) |
|
| 364 |
) |
|
| 365 |
graph.burn |
|
| 366 |
end |
|
| 367 |
end |
|
| 368 |
|
|
| 369 |
class Date |
|
| 370 |
def months_ago(date = Date.today) |
|
| 371 |
(date.year - self.year)*12 + (date.month - self.month) |
|
| 372 |
end |
|
| 373 |
|
|
| 374 |
def weeks_ago(date = Date.today) |
|
| 375 |
(date.year - self.year)*52 + (date.cweek - self.cweek) |
|
| 376 |
end |
|
| 377 |
end |
|
| 378 |
|
|
| 379 |
class String |
|
| 380 |
def with_leading_slash |
|
| 381 |
starts_with?('/') ? self : "/#{self}"
|
|
| 382 |
end |
|
| 383 |
end |
|
| .svn/pristine/40/407f7e2d4e08a32e78ca65cbc6b2ee007be59736.svn-base | ||
|---|---|---|
| 1 |
# Redmine - project management software |
|
| 2 |
# Copyright (C) 2006-2013 Jean-Philippe Lang |
|
| 3 |
# |
|
| 4 |
# This program is free software; you can redistribute it and/or |
|
| 5 |
# modify it under the terms of the GNU General Public License |
|
| 6 |
# as published by the Free Software Foundation; either version 2 |
|
| 7 |
# of the License, or (at your option) any later version. |
|
| 8 |
# |
|
| 9 |
# This program is distributed in the hope that it will be useful, |
|
| 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 12 |
# GNU General Public License for more details. |
|
| 13 |
# |
|
| 14 |
# You should have received a copy of the GNU General Public License |
|
| 15 |
# along with this program; if not, write to the Free Software |
|
| 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
| 17 |
|
|
| 18 |
class WorkflowRule < ActiveRecord::Base |
|
| 19 |
self.table_name = "#{table_name_prefix}workflows#{table_name_suffix}"
|
|
| 20 |
|
|
| 21 |
belongs_to :role |
|
| 22 |
belongs_to :tracker |
|
| 23 |
belongs_to :old_status, :class_name => 'IssueStatus', :foreign_key => 'old_status_id' |
|
| 24 |
belongs_to :new_status, :class_name => 'IssueStatus', :foreign_key => 'new_status_id' |
|
| 25 |
|
|
| 26 |
validates_presence_of :role, :tracker, :old_status |
|
| 27 |
|
|
| 28 |
# Copies workflows from source to targets |
|
| 29 |
def self.copy(source_tracker, source_role, target_trackers, target_roles) |
|
| 30 |
unless source_tracker.is_a?(Tracker) || source_role.is_a?(Role) |
|
| 31 |
raise ArgumentError.new("source_tracker or source_role must be specified")
|
|
| 32 |
end |
|
| 33 |
|
|
| 34 |
target_trackers = [target_trackers].flatten.compact |
|
| 35 |
target_roles = [target_roles].flatten.compact |
|
| 36 |
|
|
| 37 |
target_trackers = Tracker.sorted.all if target_trackers.empty? |
|
| 38 |
target_roles = Role.all if target_roles.empty? |
|
| 39 |
|
|
| 40 |
target_trackers.each do |target_tracker| |
|
| 41 |
target_roles.each do |target_role| |
|
| 42 |
copy_one(source_tracker || target_tracker, |
|
| 43 |
source_role || target_role, |
|
| 44 |
target_tracker, |
|
| 45 |
target_role) |
|
| 46 |
end |
|
| 47 |
end |
|
| 48 |
end |
|
| 49 |
|
|
| 50 |
# Copies a single set of workflows from source to target |
|
| 51 |
def self.copy_one(source_tracker, source_role, target_tracker, target_role) |
|
| 52 |
unless source_tracker.is_a?(Tracker) && !source_tracker.new_record? && |
|
| 53 |
source_role.is_a?(Role) && !source_role.new_record? && |
|
| 54 |
target_tracker.is_a?(Tracker) && !target_tracker.new_record? && |
|
| 55 |
target_role.is_a?(Role) && !target_role.new_record? |
|
| 56 |
|
|
| 57 |
raise ArgumentError.new("arguments can not be nil or unsaved objects")
|
|
| 58 |
end |
|
| 59 |
|
|
| 60 |
if source_tracker == target_tracker && source_role == target_role |
|
| 61 |
false |
|
| 62 |
else |
|
| 63 |
transaction do |
|
| 64 |
delete_all :tracker_id => target_tracker.id, :role_id => target_role.id |
|
| 65 |
connection.insert "INSERT INTO #{WorkflowRule.table_name} (tracker_id, role_id, old_status_id, new_status_id, author, assignee, field_name, #{connection.quote_column_name 'rule'}, type)" +
|
|
| 66 |
" SELECT #{target_tracker.id}, #{target_role.id}, old_status_id, new_status_id, author, assignee, field_name, #{connection.quote_column_name 'rule'}, type" +
|
|
| 67 |
" FROM #{WorkflowRule.table_name}" +
|
|
| 68 |
" WHERE tracker_id = #{source_tracker.id} AND role_id = #{source_role.id}"
|
|
| 69 |
end |
|
| 70 |
true |
|
| 71 |
end |
|
| 72 |
end |
|
| 73 |
end |
|
| .svn/pristine/40/40d857531cd6b4668cb1e019211d87ead6d54850.svn-base | ||
|---|---|---|
| 1 |
require 'test/unit' |
|
| 2 |
|
|
| 3 |
$VERBOSE = $CODERAY_DEBUG = true |
|
| 4 |
$:.unshift File.expand_path('../../../lib', __FILE__)
|
|
| 5 |
require 'coderay' |
|
| 6 |
|
|
| 7 |
mydir = File.dirname(__FILE__) |
|
| 8 |
suite = Dir[File.join(mydir, '*.rb')]. |
|
| 9 |
map { |tc| File.basename(tc).sub(/\.rb$/, '') } - %w'suite for_redcloth'
|
|
| 10 |
|
|
| 11 |
puts "Running basic CodeRay #{CodeRay::VERSION} tests: #{suite.join(', ')}"
|
|
| 12 |
|
|
| 13 |
for test_case in suite |
|
| 14 |
load File.join(mydir, test_case + '.rb') |
|
| 15 |
end |
|
| .svn/pristine/40/40fb7c915312a0bec722da5f4b2c6582d58a02b9.svn-base | ||
|---|---|---|
| 1 |
require 'active_support' |
|
| 2 |
require File.join(File.dirname(__FILE__), 'engines/plugin') |
|
| 3 |
require File.join(File.dirname(__FILE__), 'engines/plugin/list') |
|
| 4 |
require File.join(File.dirname(__FILE__), 'engines/plugin/loader') |
|
| 5 |
require File.join(File.dirname(__FILE__), 'engines/plugin/locator') |
|
| 6 |
require File.join(File.dirname(__FILE__), 'engines/assets') |
|
| 7 |
require File.join(File.dirname(__FILE__), 'engines/rails_extensions/rails') |
|
| 8 |
|
|
| 9 |
# == Parameters |
|
| 10 |
# |
|
| 11 |
# The Engines module has a number of public configuration parameters: |
|
| 12 |
# |
|
| 13 |
# [+public_directory+] The directory into which plugin assets should be |
|
| 14 |
# mirrored. Defaults to <tt>RAILS_ROOT/public/plugin_assets</tt>. |
|
| 15 |
# [+schema_info_table+] The table to use when storing plugin migration |
|
| 16 |
# version information. Defaults to +plugin_schema_info+. |
|
| 17 |
# |
|
| 18 |
# Additionally, there are a few flags which control the behaviour of |
|
| 19 |
# some of the features the engines plugin adds to Rails: |
|
| 20 |
# |
|
| 21 |
# [+disable_application_view_loading+] A boolean flag determining whether |
|
| 22 |
# or not views should be loaded from |
|
| 23 |
# the main <tt>app/views</tt> directory. |
|
| 24 |
# Defaults to false; probably only |
|
| 25 |
# useful when testing your plugin. |
|
| 26 |
# [+disable_application_code_loading+] A boolean flag determining whether |
|
| 27 |
# or not to load controllers/helpers |
|
| 28 |
# from the main +app+ directory, |
|
| 29 |
# if corresponding code exists within |
|
| 30 |
# a plugin. Defaults to false; again, |
|
| 31 |
# probably only useful when testing |
|
| 32 |
# your plugin. |
|
| 33 |
# [+disable_code_mixing+] A boolean flag indicating whether all plugin |
|
| 34 |
# copies of a particular controller/helper should |
|
| 35 |
# be loaded and allowed to override each other, |
|
| 36 |
# or if the first matching file should be loaded |
|
| 37 |
# instead. Defaults to false. |
|
| 38 |
# |
|
| 39 |
module Engines |
|
| 40 |
# The set of all loaded plugins |
|
| 41 |
mattr_accessor :plugins |
|
| 42 |
self.plugins = Engines::Plugin::List.new |
|
| 43 |
|
|
| 44 |
# List of extensions to load, can be changed in init.rb before calling Engines.init |
|
| 45 |
mattr_accessor :rails_extensions |
|
| 46 |
self.rails_extensions = %w(asset_helpers form_tag_helpers migrations dependencies) |
|
| 47 |
|
|
| 48 |
# The name of the public directory to mirror public engine assets into. |
|
| 49 |
# Defaults to <tt>RAILS_ROOT/public/plugin_assets</tt>. |
|
| 50 |
mattr_accessor :public_directory |
|
| 51 |
self.public_directory = File.join(RAILS_ROOT, 'public', 'plugin_assets') |
|
| 52 |
|
|
| 53 |
# The table in which to store plugin schema information. Defaults to |
|
| 54 |
# "plugin_schema_info". |
|
| 55 |
mattr_accessor :schema_info_table |
|
| 56 |
self.schema_info_table = "plugin_schema_info" |
|
| 57 |
|
|
| 58 |
#-- |
|
| 59 |
# These attributes control the behaviour of the engines extensions |
|
| 60 |
#++ |
|
| 61 |
|
|
| 62 |
# Set this to true if views should *only* be loaded from plugins |
|
| 63 |
mattr_accessor :disable_application_view_loading |
|
| 64 |
self.disable_application_view_loading = false |
|
| 65 |
|
|
| 66 |
# Set this to true if controller/helper code shouldn't be loaded |
|
| 67 |
# from the application |
|
| 68 |
mattr_accessor :disable_application_code_loading |
|
| 69 |
self.disable_application_code_loading = false |
|
| 70 |
|
|
| 71 |
# Set this to true if code should not be mixed (i.e. it will be loaded |
|
| 72 |
# from the first valid path on $LOAD_PATH) |
|
| 73 |
mattr_accessor :disable_code_mixing |
|
| 74 |
self.disable_code_mixing = false |
|
| 75 |
|
|
| 76 |
# This is used to determine which files are candidates for the "code |
|
| 77 |
# mixing" feature that the engines plugin provides, where classes from |
|
| 78 |
# plugins can be loaded, and then code from the application loaded |
|
| 79 |
# on top of that code to override certain methods. |
|
| 80 |
mattr_accessor :code_mixing_file_types |
|
| 81 |
self.code_mixing_file_types = %w(controller helper) |
|
| 82 |
|
|
| 83 |
class << self |
|
| 84 |
def init(initializer) |
|
| 85 |
load_extensions |
|
| 86 |
Engines::Assets.initialize_base_public_directory |
|
| 87 |
end |
|
| 88 |
|
|
| 89 |
def logger |
|
| 90 |
RAILS_DEFAULT_LOGGER |
|
| 91 |
end |
|
| 92 |
|
|
| 93 |
def load_extensions |
|
| 94 |
rails_extensions.each { |name| require "engines/rails_extensions/#{name}" }
|
|
| 95 |
# load the testing extensions, if we are in the test environment. |
|
| 96 |
require "engines/testing" if RAILS_ENV == "test" |
|
| 97 |
end |
|
| 98 |
|
|
| 99 |
def select_existing_paths(paths) |
|
| 100 |
paths.select { |path| File.directory?(path) }
|
|
| 101 |
end |
|
| 102 |
|
|
| 103 |
# The engines plugin will, by default, mix code from controllers and helpers, |
|
| 104 |
# allowing application code to override specific methods in the corresponding |
|
| 105 |
# controller or helper classes and modules. However, if other file types should |
|
| 106 |
# also be mixed like this, they can be added by calling this method. For example, |
|
| 107 |
# if you want to include "things" within your plugin and override them from |
|
| 108 |
# your applications, you should use the following layout: |
|
| 109 |
# |
|
| 110 |
# app/ |
|
| 111 |
# +-- things/ |
|
| 112 |
# | +-- one_thing.rb |
|
| 113 |
# | +-- another_thing.rb |
|
| 114 |
# ... |
|
| 115 |
# vendor/ |
|
| 116 |
# +-- plugins/ |
|
| 117 |
# +-- my_plugin/ |
|
| 118 |
# +-- app/ |
|
| 119 |
# +-- things/ |
|
| 120 |
# +-- one_thing.rb |
|
| 121 |
# +-- another_thing.rb |
|
| 122 |
# |
|
| 123 |
# The important point here is that your "things" are named <whatever>_thing.rb, |
|
| 124 |
# and that they are placed within plugin/app/things (the pluralized form of 'thing'). |
|
| 125 |
# |
|
| 126 |
# It's important to note that you'll also want to ensure that the "things" are |
|
| 127 |
# on your load path by including them in Rails load path mechanism, e.g. in init.rb: |
|
| 128 |
# |
|
| 129 |
# ActiveSupport::Dependencies.load_paths << File.join(File.dirname(__FILE__), 'app', 'things')) |
|
| 130 |
# |
|
| 131 |
def mix_code_from(*types) |
|
| 132 |
self.code_mixing_file_types += types.map { |x| x.to_s.singularize }
|
|
| 133 |
end |
|
| 134 |
|
|
| 135 |
# A general purpose method to mirror a directory (+source+) into a destination |
|
| 136 |
# directory, including all files and subdirectories. Files will not be mirrored |
|
| 137 |
# if they are identical already (checked via FileUtils#identical?). |
|
| 138 |
def mirror_files_from(source, destination) |
|
| 139 |
return unless File.directory?(source) |
|
| 140 |
|
|
| 141 |
# TODO: use Rake::FileList#pathmap? |
|
| 142 |
source_files = Dir[source + "/**/*"] |
|
| 143 |
source_dirs = source_files.select { |d| File.directory?(d) }
|
|
| 144 |
source_files -= source_dirs |
|
| 145 |
|
|
| 146 |
unless source_files.empty? |
|
| 147 |
base_target_dir = File.join(destination, File.dirname(source_files.first).gsub(source, '')) |
|
| 148 |
FileUtils.mkdir_p(base_target_dir) |
|
| 149 |
end |
|
| 150 |
|
|
| 151 |
source_dirs.each do |dir| |
|
| 152 |
# strip down these paths so we have simple, relative paths we can |
|
| 153 |
# add to the destination |
|
| 154 |
target_dir = File.join(destination, dir.gsub(source, '')) |
|
| 155 |
begin |
|
| 156 |
FileUtils.mkdir_p(target_dir) |
|
| 157 |
rescue Exception => e |
|
| 158 |
raise "Could not create directory #{target_dir}: \n" + e
|
|
| 159 |
end |
|
| 160 |
end |
|
| 161 |
|
|
| 162 |
source_files.each do |file| |
|
| 163 |
begin |
|
| 164 |
target = File.join(destination, file.gsub(source, '')) |
|
| 165 |
unless File.exist?(target) && FileUtils.identical?(file, target) |
|
| 166 |
FileUtils.cp(file, target) |
|
| 167 |
end |
|
| 168 |
rescue Exception => e |
|
| 169 |
raise "Could not copy #{file} to #{target}: \n" + e
|
|
| 170 |
end |
|
| 171 |
end |
|
| 172 |
end |
|
| 173 |
end |
|
| 174 |
end |
|
Also available in: Unified diff