Compare commits

..
1 Commits
Author SHA1 Message Date
Jean-Philippe Lang 9c7b8c8192 tagged version 2.2.1
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/tags/2.2.1@11153 e93f8b46-1217-0410-a6f0-8f06a7374b81
2013-01-09 14:08:55 +00:00
34 changed files with 2724 additions and 2857 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
source 'http://rubygems.org' source 'http://rubygems.org'
gem 'rails', '3.2.12' gem 'rails', '3.2.11'
gem "jquery-rails", "~> 2.0.2" gem "jquery-rails", "~> 2.0.2"
gem "i18n", "~> 0.6.0" gem "i18n", "~> 0.6.0"
gem "coderay", "~> 1.0.6" gem "coderay", "~> 1.0.6"
+9 -10
View File
@@ -147,16 +147,15 @@ class MyController < ApplicationController
# params[:block] : id of the block to add # params[:block] : id of the block to add
def add_block def add_block
block = params[:block].to_s.underscore block = params[:block].to_s.underscore
if block.present? && BLOCKS.key?(block) (render :nothing => true; return) unless block && (BLOCKS.keys.include? block)
@user = User.current @user = User.current
layout = @user.pref[:my_page_layout] || {} layout = @user.pref[:my_page_layout] || {}
# remove if already present in a group # remove if already present in a group
%w(top left right).each {|f| (layout[f] ||= []).delete block } %w(top left right).each {|f| (layout[f] ||= []).delete block }
# add it on top # add it on top
layout['top'].unshift block layout['top'].unshift block
@user.pref[:my_page_layout] = layout @user.pref[:my_page_layout] = layout
@user.pref.save @user.pref.save
end
redirect_to :action => 'page_layout' redirect_to :action => 'page_layout'
end end
+6 -8
View File
@@ -597,9 +597,8 @@ module ApplicationHelper
def parse_inline_attachments(text, project, obj, attr, only_path, options) def parse_inline_attachments(text, project, obj, attr, only_path, options)
# when using an image link, try to use an attachment, if possible # when using an image link, try to use an attachment, if possible
attachments = options[:attachments] || [] if options[:attachments] || (obj && obj.respond_to?(:attachments))
attachments += obj.attachments if obj.respond_to?(:attachments) attachments = options[:attachments] || obj.attachments
if attachments.present?
text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m| text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
filename, ext, alt, alttext = $1.downcase, $2, $3, $4 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
# search for the picture in attachments # search for the picture in attachments
@@ -704,11 +703,10 @@ module ApplicationHelper
# identifier:document:"Some document" # identifier:document:"Some document"
# identifier:version:1.0.0 # identifier:version:1.0.0
# identifier:source:some/file # identifier:source:some/file
def parse_redmine_links(text, default_project, obj, attr, only_path, options) def parse_redmine_links(text, project, obj, attr, only_path, options)
text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-_]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m| text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m|
leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $1, $2, $3, $4, $5, $10, $11, $8 || $12 || $18, $14 || $19, $15, $17 leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $1, $2, $3, $4, $5, $10, $11, $8 || $12 || $18, $14 || $19, $15, $17
link = nil link = nil
project = default_project
if project_identifier if project_identifier
project = Project.visible.find_by_identifier(project_identifier) project = Project.visible.find_by_identifier(project_identifier)
end end
@@ -794,7 +792,7 @@ module ApplicationHelper
when 'commit', 'source', 'export' when 'commit', 'source', 'export'
if project if project
repository = nil repository = nil
if name =~ %r{^(([a-z0-9\-_]+)\|)(.+)$} if name =~ %r{^(([a-z0-9\-]+)\|)(.+)$}
repo_prefix, repo_identifier, name = $1, $2, $3 repo_prefix, repo_identifier, name = $1, $2, $3
repository = project.repositories.detect {|repo| repo.identifier == repo_identifier} repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
else else
@@ -821,7 +819,7 @@ module ApplicationHelper
end end
when 'attachment' when 'attachment'
attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil) attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
if attachments && attachment = Attachment.latest_attach(attachments, name) if attachments && attachment = attachments.detect {|a| a.filename == name }
link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment}, link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
:class => 'attachment' :class => 'attachment'
end end
+4 -4
View File
@@ -87,14 +87,14 @@ module QueriesHelper
format_time(value) format_time(value)
when 'Date' when 'Date'
format_date(value) format_date(value)
when 'Fixnum' when 'Fixnum', 'Float'
if column.name == :done_ratio if column.name == :done_ratio
progress_bar(value, :width => '80px') progress_bar(value, :width => '80px')
elsif column.name == :spent_hours
sprintf "%.2f", value
else else
value.to_s h(value.to_s)
end end
when 'Float'
sprintf "%.2f", value
when 'User' when 'User'
link_to_user value link_to_user value
when 'Project' when 'Project'
+1 -1
View File
@@ -418,7 +418,7 @@ class Issue < ActiveRecord::Base
if attrs['parent_issue_id'].present? if attrs['parent_issue_id'].present?
s = attrs['parent_issue_id'].to_s s = attrs['parent_issue_id'].to_s
unless (m = s.match(%r{\A#?(\d+)\z})) && (m[1] == parent_id.to_s || Issue.visible(user).exists?(m[1])) unless (m = s.match(%r{\A#?(\d+)\z})) && Issue.visible(user).exists?(m[1])
@invalid_parent_issue_id = attrs.delete('parent_issue_id') @invalid_parent_issue_id = attrs.delete('parent_issue_id')
end end
end end
+1 -3
View File
@@ -731,7 +731,7 @@ class Project < ActiveRecord::Base
def copy_wiki(project) def copy_wiki(project)
# Check that the source project has a wiki first # Check that the source project has a wiki first
unless project.wiki.nil? unless project.wiki.nil?
wiki = self.wiki || Wiki.new self.wiki ||= Wiki.new
wiki.attributes = project.wiki.attributes.dup.except("id", "project_id") wiki.attributes = project.wiki.attributes.dup.except("id", "project_id")
wiki_pages_map = {} wiki_pages_map = {}
project.wiki.pages.each do |page| project.wiki.pages.each do |page|
@@ -743,8 +743,6 @@ class Project < ActiveRecord::Base
wiki.pages << new_wiki_page wiki.pages << new_wiki_page
wiki_pages_map[page.id] = new_wiki_page wiki_pages_map[page.id] = new_wiki_page
end end
self.wiki = wiki
wiki.save wiki.save
# Reproduce page hierarchy # Reproduce page hierarchy
project.wiki.pages.each do |page| project.wiki.pages.each do |page|
+1 -1
View File
@@ -111,7 +111,7 @@
<li><%= bulk_update_custom_field_context_menu_link(field, text, value || text) %></li> <li><%= bulk_update_custom_field_context_menu_link(field, text, value || text) %></li>
<% end %> <% end %>
<% unless field.is_required? %> <% unless field.is_required? %>
<li><%= bulk_update_custom_field_context_menu_link(field, l(:label_none), '__none__') %></li> <li><%= bulk_update_custom_field_context_menu_link(field, l(:label_none), '') %></li>
<% end %> <% end %>
</ul> </ul>
</li> </li>
+1 -1
View File
@@ -18,7 +18,7 @@
<% end %> <% end %>
<% if @issue.safe_attribute? 'subject' %> <% if @issue.safe_attribute? 'subject' %>
<p><%= f.text_field :subject, :size => 80, :maxlength => 255, :required => true %></p> <p><%= f.text_field :subject, :size => 80, :required => true %></p>
<% end %> <% end %>
<% if @issue.safe_attribute? 'description' %> <% if @issue.safe_attribute? 'description' %>
+1 -1
View File
@@ -12,7 +12,7 @@
<p><%= f.text_field :issue_id, :size => 6 %> <em><%= h("#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}") if @time_entry.issue %></em></p> <p><%= f.text_field :issue_id, :size => 6 %> <em><%= h("#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}") if @time_entry.issue %></em></p>
<p><%= f.text_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %></p> <p><%= f.text_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %></p>
<p><%= f.text_field :hours, :size => 6, :required => true %></p> <p><%= f.text_field :hours, :size => 6, :required => true %></p>
<p><%= f.text_field :comments, :size => 100, :maxlength => 255 %></p> <p><%= f.text_field :comments, :size => 100 %></p>
<p><%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %></p> <p><%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %></p>
<% @time_entry.custom_field_values.each do |value| %> <% @time_entry.custom_field_values.each do |value| %>
<p><%= custom_field_tag_with_label :time_entry, value %></p> <p><%= custom_field_tag_with_label :time_entry, value %></p>
+1 -1
View File
@@ -833,7 +833,7 @@ bg:
label_generate_key: Генериране на ключ label_generate_key: Генериране на ключ
label_issue_watchers: Наблюдатели label_issue_watchers: Наблюдатели
label_example: Пример label_example: Пример
label_display: Показване label_display: Display
label_sort: Сортиране label_sort: Сортиране
label_ascending: Нарастващ label_ascending: Нарастващ
label_descending: Намаляващ label_descending: Намаляващ
+66 -66
View File
@@ -1037,84 +1037,84 @@ es:
description_selected_columns: Columnas seleccionadas description_selected_columns: Columnas seleccionadas
label_parent_revision: Padre label_parent_revision: Padre
label_child_revision: Hijo label_child_revision: Hijo
setting_default_issue_start_date_to_creation_date: Utilizar la fecha actual como fecha de inicio para nuevas peticiones setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
button_edit_section: Editar esta sección button_edit_section: Edit this section
setting_repositories_encodings: Codificación de adjuntos y repositorios setting_repositories_encodings: Attachments and repositories encodings
description_all_columns: Todas las columnas description_all_columns: Todas las columnas
button_export: Exportar button_export: Exportar
label_export_options: "%{export_format} opciones de exportación" label_export_options: "%{export_format} opciones de exportación"
error_attachment_too_big: Este fichero no se puede adjuntar porque excede el tamaño máximo de fichero (%{max_size}) error_attachment_too_big: Este fichero no se puede adjuntar porque excede el tamaño máximo de fichero (%{max_size})
notice_failed_to_save_time_entries: "Error al guardar %{count} entradas de tiempo de las %{total} seleccionadas: %{ids}." notice_failed_to_save_time_entries: "Error al guarda %{count} entradas de tiempo de las %{total} seleccionadas: %{ids}."
label_x_issues: label_x_issues:
zero: 0 petición zero: 0 petición
one: 1 petición one: 1 petición
other: "%{count} peticiones" other: "%{count} peticiones"
label_repository_new: Nuevo repositorio label_repository_new: New repository
field_repository_is_default: Repositorio principal field_repository_is_default: Main repository
label_copy_attachments: Copiar adjuntos label_copy_attachments: Copy attachments
label_item_position: "%{position}/%{count}" label_item_position: "%{position}/%{count}"
label_completed_versions: Versiones completadas label_completed_versions: Completed versions
text_project_identifier_info: Solo se permiten letras en minúscula (a-z), números, guiones y barras bajas.<br />Una vez guardado, el identificador no se puede cambiar. text_project_identifier_info: Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.
field_multiple: Valores múltiples field_multiple: Multiple values
setting_commit_cross_project_ref: Permitir referenciar y resolver peticiones de todos los demás proyectos setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed
text_issue_conflict_resolution_add_notes: Añadir mis notas y descartar mis otros cambios text_issue_conflict_resolution_add_notes: Add my notes and discard my other changes
text_issue_conflict_resolution_overwrite: Aplicar mis campos de todas formas (las notas anteriores se mantendrán pero algunos cambios podrían ser sobreescritos) text_issue_conflict_resolution_overwrite: Apply my changes anyway (previous notes will be kept but some changes may be overwritten)
notice_issue_update_conflict: La petición ha sido actualizada por otro usuario mientras la editaba. notice_issue_update_conflict: The issue has been updated by an other user while you were editing it.
text_issue_conflict_resolution_cancel: Descartar todos mis cambios y mostrar de nuevo %{link} text_issue_conflict_resolution_cancel: Discard all my changes and redisplay %{link}
permission_manage_related_issues: Gestionar peticiones relacionadas permission_manage_related_issues: Manage related issues
field_auth_source_ldap_filter: Filtro LDAP field_auth_source_ldap_filter: LDAP filter
label_search_for_watchers: Buscar seguidores para añadirlos label_search_for_watchers: Search for watchers to add
notice_account_deleted: Su cuenta ha sido eliminada notice_account_deleted: Your account has been permanently deleted.
setting_unsubscribe: Permitir a los usuarios borrar sus propias cuentas setting_unsubscribe: Allow users to delete their own account
button_delete_my_account: Borrar mi cuenta button_delete_my_account: Delete my account
text_account_destroy_confirmation: |- text_account_destroy_confirmation: |-
¿Está seguro de querer proceder? Are you sure you want to proceed?
Su cuenta quedará borrada permanentemente, sin la posibilidad de reactivarla. Your account will be permanently deleted, with no way to reactivate it.
error_session_expired: Su sesión ha expirado. Por favor, vuelva a identificarse. error_session_expired: Your session has expired. Please login again.
text_session_expiration_settings: "Advertencia: el cambio de estas opciones podría hacer expirar las sesiones activas, incluyendo la suya." text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours."
setting_session_lifetime: Tiempo de vida máximo de las sesiones setting_session_lifetime: Session maximum lifetime
setting_session_timeout: Tiempo máximo de inactividad de las sesiones setting_session_timeout: Session inactivity timeout
label_session_expiration: Expiración de sesiones label_session_expiration: Session expiration
permission_close_project: Cerrar / reabrir el proyecto permission_close_project: Close / reopen the project
label_show_closed_projects: Ver proyectos cerrados label_show_closed_projects: View closed projects
button_close: Cerrar button_close: Close
button_reopen: Reabrir button_reopen: Reopen
project_status_active: activo project_status_active: active
project_status_closed: cerrado project_status_closed: closed
project_status_archived: archivado project_status_archived: archived
text_project_closed: Este proyecto está cerrado y es de sólo lectura text_project_closed: This project is closed and read-only.
notice_user_successful_create: Usuario %{id} creado. notice_user_successful_create: User %{id} created.
field_core_fields: Campos básicos field_core_fields: Standard fields
field_timeout: Tiempo de inactividad (en segundos) field_timeout: Timeout (in seconds)
setting_thumbnails_enabled: Mostrar miniaturas de los adjuntos setting_thumbnails_enabled: Display attachment thumbnails
setting_thumbnails_size: Tamaño de las miniaturas (en píxeles) setting_thumbnails_size: Thumbnails size (in pixels)
label_status_transitions: Transiciones de estado label_status_transitions: Status transitions
label_fields_permissions: Permisos sobre los campos label_fields_permissions: Fields permissions
label_readonly: Sólo lectura label_readonly: Read-only
label_required: Requerido label_required: Required
text_repository_identifier_info: Solo se permiten letras en minúscula (a-z), números, guiones y barras bajas.<br />Una vez guardado, el identificador no se puede cambiar. text_repository_identifier_info: Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.
field_board_parent: Foro padre field_board_parent: Parent forum
label_attribute_of_project: "%{name} del proyecto" label_attribute_of_project: Project's %{name}
label_attribute_of_author: "%{name} del autor" label_attribute_of_author: Author's %{name}
label_attribute_of_assigned_to: "%{name} de la persona asignada" label_attribute_of_assigned_to: Assignee's %{name}
label_attribute_of_fixed_version: "%{name} de la versión indicada" label_attribute_of_fixed_version: Target version's %{name}
label_copy_subtasks: Copiar subtareas label_copy_subtasks: Copy subtasks
label_copied_to: copiada a label_copied_to: copied to
label_copied_from: copiada desde label_copied_from: copied from
label_any_issues_in_project: cualquier petición del proyecto label_any_issues_in_project: any issues in project
label_any_issues_not_in_project: cualquier petición que no sea del proyecto label_any_issues_not_in_project: any issues not in project
field_private_notes: Notas privadas field_private_notes: Private notes
permission_view_private_notes: Ver notas privadas permission_view_private_notes: View private notes
permission_set_notes_private: Poner notas como privadas permission_set_notes_private: Set notes as private
label_no_issues_in_project: no hay peticiones en el proyecto label_no_issues_in_project: no issues in project
label_any: todos label_any: todos
label_last_n_weeks: en las últimas %{count} semanas label_last_n_weeks: last %{count} weeks
setting_cross_project_subtasks: Permitir subtareas cruzadas entre proyectos setting_cross_project_subtasks: Allow cross-project subtasks
label_cross_project_descendants: Con proyectos hijo label_cross_project_descendants: Con proyectos hijo
label_cross_project_tree: Con el árbol del proyecto label_cross_project_tree: Con el árbol del proyecto
label_cross_project_hierarchy: Con la jerarquía del proyecto label_cross_project_hierarchy: Con la jerarquía del proyecto
label_cross_project_system: Con todos los proyectos label_cross_project_system: Con todos los proyectos
button_hide: Ocultar button_hide: Hide
setting_non_working_week_days: Días no laborables setting_non_working_week_days: Non-working days
label_in_the_next_days: en los próximos label_in_the_next_days: in the next
label_in_the_past_days: en los anteriores label_in_the_past_days: in the past
+1 -1
View File
@@ -1147,7 +1147,7 @@ ru:
button_delete_my_account: "Удалить мою учетную запись" button_delete_my_account: "Удалить мою учетную запись"
text_account_destroy_confirmation: "Ваша учетная запись будет полностью удалена без возможности восстановления.\nВы уверены, что хотите продолжить?" text_account_destroy_confirmation: "Ваша учетная запись будет полностью удалена без возможности восстановления.\nВы уверены, что хотите продолжить?"
error_session_expired: Срок вашей сессии истек. Пожалуйста войдите еще раз error_session_expired: Срок вашей сессии истек. Пожалуйста войдите еще раз
text_session_expiration_settings: "Внимание! Изменение этих настроек может привести к завершению текущих сессий, включая вашу." text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours."
setting_session_lifetime: Максимальная продолжительность сессии setting_session_lifetime: Максимальная продолжительность сессии
setting_session_timeout: Таймут сессии setting_session_timeout: Таймут сессии
label_session_expiration: Срок истечения сессии label_session_expiration: Срок истечения сессии
+32 -32
View File
@@ -923,47 +923,47 @@ sr-YU:
project_module_calendar: Kalendar project_module_calendar: Kalendar
button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
field_text: Text field field_text: Text field
label_user_mail_option_only_owner: Samo za stvari koje posedujem label_user_mail_option_only_owner: Only for things I am the owner of
setting_default_notification_option: Podrazumevana opcija za notifikaciju setting_default_notification_option: Default notification option
label_user_mail_option_only_my_events: Za dogadjaje koje pratim ili sam u njih uključen label_user_mail_option_only_my_events: Only for things I watch or I'm involved in
label_user_mail_option_only_assigned: Za dogadjaje koji su mi dodeljeni lično label_user_mail_option_only_assigned: Only for things I am assigned to
label_user_mail_option_none: Bez obaveštenja label_user_mail_option_none: No events
field_member_of_group: Assignee's group field_member_of_group: Assignee's group
field_assigned_to_role: Assignee's role field_assigned_to_role: Assignee's role
notice_not_authorized_archived_project: Projekat kome pokušavate da pristupite je arhiviran notice_not_authorized_archived_project: The project you're trying to access has been archived.
label_principal_search: "Traži korisnike ili grupe:" label_principal_search: "Search for user or group:"
label_user_search: "Traži korisnike:" label_user_search: "Search for user:"
field_visible: Vidljivo field_visible: Visible
setting_emails_header: Email zaglavlje setting_emails_header: Emails header
setting_commit_logtime_activity_id: Activity for logged time setting_commit_logtime_activity_id: Activity for logged time
text_time_logged_by_changeset: Applied in changeset %{value}. text_time_logged_by_changeset: Applied in changeset %{value}.
setting_commit_logtime_enabled: Omogući praćenje vremena setting_commit_logtime_enabled: Enable time logging
notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max}) notice_gantt_chart_truncated: The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})
setting_gantt_items_limit: Maksimalan broj stavki na gant grafiku setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
field_warn_on_leaving_unsaved: Upozori me ako napuštam stranu sa tekstom koji nije snimljen field_warn_on_leaving_unsaved: Warn me when leaving a page with unsaved text
text_warn_on_leaving_unsaved: Strana sadrži tekst koji nije snimljen i biće izgubljen ako je napustite. text_warn_on_leaving_unsaved: The current page contains unsaved text that will be lost if you leave this page.
label_my_queries: My custom queries label_my_queries: My custom queries
text_journal_changed_no_detail: "%{label} ažuriran" text_journal_changed_no_detail: "%{label} updated"
label_news_comment_added: Komentar dodat u novosti label_news_comment_added: Comment added to a news
button_expand_all: Proširi sve button_expand_all: Expand all
button_collapse_all: Zatvori sve button_collapse_all: Collapse all
label_additional_workflow_transitions_for_assignee: Additional transitions allowed when the user is the assignee label_additional_workflow_transitions_for_assignee: Additional transitions allowed when the user is the assignee
label_additional_workflow_transitions_for_author: Additional transitions allowed when the user is the author label_additional_workflow_transitions_for_author: Additional transitions allowed when the user is the author
label_bulk_edit_selected_time_entries: Bulk edit selected time entries label_bulk_edit_selected_time_entries: Bulk edit selected time entries
text_time_entries_destroy_confirmation: Da li ste sigurni da želite da obrišete selektovane stavke ? text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies)?
label_role_anonymous: Anonimus label_role_anonymous: Anonymous
label_role_non_member: Nije član label_role_non_member: Non member
label_issue_note_added: Nota dodana label_issue_note_added: Note added
label_issue_status_updated: Status ažuriran label_issue_status_updated: Status updated
label_issue_priority_updated: Prioritet ažuriran label_issue_priority_updated: Priority updated
label_issues_visibility_own: Problem kreiran od strane ili je dodeljen korisniku label_issues_visibility_own: Issues created by or assigned to the user
field_issues_visibility: Vidljivost problema field_issues_visibility: Issues visibility
label_issues_visibility_all: Svi problemi label_issues_visibility_all: All issues
permission_set_own_issues_private: Podesi sopstveni problem kao privatan ili javan permission_set_own_issues_private: Set own issues public or private
field_is_private: Privatno field_is_private: Private
permission_set_issues_private: Podesi problem kao privatan ili javan permission_set_issues_private: Set issues public or private
label_issues_visibility_public: Svi javni problemi label_issues_visibility_public: All non private issues
text_issues_destroy_descendants_confirmation: Ova operacija će takođe obrisati %{count} podzadataka. text_issues_destroy_descendants_confirmation: This will also delete %{count} subtask(s).
field_commit_logs_encoding: Kodiranje izvršnih poruka field_commit_logs_encoding: Kodiranje izvršnih poruka
field_scm_path_encoding: Path encoding field_scm_path_encoding: Path encoding
text_scm_path_encoding_note: "Default: UTF-8" text_scm_path_encoding_note: "Default: UTF-8"
+44 -45
View File
@@ -79,8 +79,8 @@ sv:
one: "ungefär en timme" one: "ungefär en timme"
other: "ungefär %{count} timmar" other: "ungefär %{count} timmar"
x_hours: x_hours:
one: "1 timme" one: "1 hour"
other: "%{count} timmar" other: "%{count} hours"
x_days: x_days:
one: "en dag" one: "en dag"
other: "%{count} dagar" other: "%{count} dagar"
@@ -219,7 +219,6 @@ sv:
notice_issue_successful_create: Ärende %{id} skapades. notice_issue_successful_create: Ärende %{id} skapades.
notice_issue_update_conflict: Detta ärende har uppdaterats av en annan användare samtidigt som du redigerade det. notice_issue_update_conflict: Detta ärende har uppdaterats av en annan användare samtidigt som du redigerade det.
notice_account_deleted: Ditt konto har avslutats permanent. notice_account_deleted: Ditt konto har avslutats permanent.
notice_user_successful_create: "Användare %{id} skapad."
error_can_t_load_default_data: "Standardkonfiguration gick inte att läsa in: %{value}" error_can_t_load_default_data: "Standardkonfiguration gick inte att läsa in: %{value}"
error_scm_not_found: "Inlägg och/eller revision finns inte i detta versionsarkiv." error_scm_not_found: "Inlägg och/eller revision finns inte i detta versionsarkiv."
@@ -240,7 +239,6 @@ sv:
error_unable_delete_issue_status: 'Ärendestatus kunde inte tas bort' error_unable_delete_issue_status: 'Ärendestatus kunde inte tas bort'
error_unable_to_connect: "Kan inte ansluta (%{value})" error_unable_to_connect: "Kan inte ansluta (%{value})"
error_attachment_too_big: Denna fil kan inte laddas upp eftersom den överstiger maximalt tillåten filstorlek (%{max_size}) error_attachment_too_big: Denna fil kan inte laddas upp eftersom den överstiger maximalt tillåten filstorlek (%{max_size})
error_session_expired: "Din session har gått ut. Vänligen logga in på nytt."
warning_attachments_not_saved: "%{count} fil(er) kunde inte sparas." warning_attachments_not_saved: "%{count} fil(er) kunde inte sparas."
mail_subject_lost_password: "Ditt %{value} lösenord" mail_subject_lost_password: "Ditt %{value} lösenord"
@@ -370,10 +368,6 @@ sv:
field_repository_is_default: Huvudarkiv field_repository_is_default: Huvudarkiv
field_multiple: Flera värden field_multiple: Flera värden
field_auth_source_ldap_filter: LDAP-filter field_auth_source_ldap_filter: LDAP-filter
field_core_fields: Standardfält
field_timeout: "Timeout (i sekunder)"
field_board_parent: Förälderforum
field_private_notes: Privata anteckningar
setting_app_title: Applikationsrubrik setting_app_title: Applikationsrubrik
setting_app_subtitle: Applikationsunderrubrik setting_app_subtitle: Applikationsunderrubrik
@@ -398,7 +392,6 @@ sv:
setting_autologin: Automatisk inloggning setting_autologin: Automatisk inloggning
setting_date_format: Datumformat setting_date_format: Datumformat
setting_time_format: Tidsformat setting_time_format: Tidsformat
setting_cross_project_subtasks: Tillåt underaktiviteter mellan projekt
setting_cross_project_issue_relations: Tillåt ärenderelationer mellan projekt setting_cross_project_issue_relations: Tillåt ärenderelationer mellan projekt
setting_issue_list_default_columns: Standardkolumner i ärendelistan setting_issue_list_default_columns: Standardkolumner i ärendelistan
setting_repositories_encodings: Encoding för bilagor och versionsarkiv setting_repositories_encodings: Encoding för bilagor och versionsarkiv
@@ -437,16 +430,10 @@ sv:
setting_default_issue_start_date_to_creation_date: Använd dagens datum som startdatum för nya ärenden setting_default_issue_start_date_to_creation_date: Använd dagens datum som startdatum för nya ärenden
setting_commit_cross_project_ref: Tillåt ärende i alla de andra projekten att bli refererade och fixade setting_commit_cross_project_ref: Tillåt ärende i alla de andra projekten att bli refererade och fixade
setting_unsubscribe: Tillåt användare att avsluta prenumereration setting_unsubscribe: Tillåt användare att avsluta prenumereration
setting_session_lifetime: Maximal sessionslivslängd
setting_session_timeout: Tidsgräns för sessionsinaktivitet
setting_thumbnails_enabled: Visa miniatyrbilder av bilagor
setting_thumbnails_size: Storlek på miniatyrbilder (i pixlar)
setting_non_working_week_days: Lediga dagar
permission_add_project: Skapa projekt permission_add_project: Skapa projekt
permission_add_subprojects: Skapa underprojekt permission_add_subprojects: Skapa underprojekt
permission_edit_project: Ändra projekt permission_edit_project: Ändra projekt
permission_close_project: Stänga / återöppna projektet
permission_select_project_modules: Välja projektmoduler permission_select_project_modules: Välja projektmoduler
permission_manage_members: Hantera medlemmar permission_manage_members: Hantera medlemmar
permission_manage_project_activities: Hantera projektaktiviteter permission_manage_project_activities: Hantera projektaktiviteter
@@ -461,8 +448,6 @@ sv:
permission_add_issue_notes: Lägga till ärendenotering permission_add_issue_notes: Lägga till ärendenotering
permission_edit_issue_notes: Ändra ärendenoteringar permission_edit_issue_notes: Ändra ärendenoteringar
permission_edit_own_issue_notes: Ändra egna ärendenoteringar permission_edit_own_issue_notes: Ändra egna ärendenoteringar
permission_view_private_notes: Visa privata anteckningar
permission_set_notes_private: Ställa in anteckningar som privata
permission_move_issues: Flytta ärenden permission_move_issues: Flytta ärenden
permission_delete_issues: Ta bort ärenden permission_delete_issues: Ta bort ärenden
permission_manage_public_queries: Hantera publika frågor permission_manage_public_queries: Hantera publika frågor
@@ -697,8 +682,6 @@ sv:
label_not_equals: är inte label_not_equals: är inte
label_in_less_than: om mindre än label_in_less_than: om mindre än
label_in_more_than: om mer än label_in_more_than: om mer än
label_in_the_next_days: under kommande
label_in_the_past_days: under föregående
label_greater_or_equal: '>=' label_greater_or_equal: '>='
label_less_or_equal: '<=' label_less_or_equal: '<='
label_between: mellan label_between: mellan
@@ -708,7 +691,6 @@ sv:
label_yesterday: igår label_yesterday: igår
label_this_week: denna vecka label_this_week: denna vecka
label_last_week: senaste veckan label_last_week: senaste veckan
label_last_n_weeks: "senaste %{count} veckorna"
label_last_n_days: "senaste %{count} dagarna" label_last_n_days: "senaste %{count} dagarna"
label_this_month: denna månad label_this_month: denna månad
label_last_month: senaste månaden label_last_month: senaste månaden
@@ -719,9 +701,6 @@ sv:
label_ago: dagar sedan label_ago: dagar sedan
label_contains: innehåller label_contains: innehåller
label_not_contains: innehåller inte label_not_contains: innehåller inte
label_any_issues_in_project: några ärenden i projektet
label_any_issues_not_in_project: några ärenden utanför projektet
label_no_issues_in_project: inga ärenden i projektet
label_day_plural: dagar label_day_plural: dagar
label_repository: Versionsarkiv label_repository: Versionsarkiv
label_repository_new: Nytt versionsarkiv label_repository_new: Nytt versionsarkiv
@@ -797,8 +776,6 @@ sv:
label_blocked_by: blockerad av label_blocked_by: blockerad av
label_precedes: kommer före label_precedes: kommer före
label_follows: följer label_follows: följer
label_copied_to: Kopierad till
label_copied_from: Kopierad från
label_end_to_start: slut till start label_end_to_start: slut till start
label_end_to_end: slut till slut label_end_to_end: slut till slut
label_start_to_start: start till start label_start_to_start: start till start
@@ -912,20 +889,9 @@ sv:
label_child_revision: Barn label_child_revision: Barn
label_export_options: "%{export_format} exportalternativ" label_export_options: "%{export_format} exportalternativ"
label_copy_attachments: Kopiera bilagor label_copy_attachments: Kopiera bilagor
label_copy_subtasks: Kopiera underaktiviteter
label_item_position: "%{position}/%{count}" label_item_position: "%{position}/%{count}"
label_completed_versions: Klara versioner label_completed_versions: Klara versioner
label_search_for_watchers: Sök efter bevakare att lägga till label_search_for_watchers: Sök efter bevakare att lägga till
label_session_expiration: Sessionsutgång
label_show_closed_projects: Visa stängda projekt
label_status_transitions: Statusövergångar
label_fields_permissions: Fältbehörigheter
label_readonly: Skrivskyddad
label_required: Nödvändig
label_attribute_of_project: Projektets %{name}
label_attribute_of_author: Författarens %{name}
label_attribute_of_assigned_to: Tilldelads %{name}
label_attribute_of_fixed_version: Målversionens %{name}
button_login: Logga in button_login: Logga in
button_submit: Skicka button_submit: Skicka
@@ -973,21 +939,14 @@ sv:
button_quote: Citera button_quote: Citera
button_duplicate: Duplicera button_duplicate: Duplicera
button_show: Visa button_show: Visa
button_hide: Göm
button_edit_section: Redigera denna sektion button_edit_section: Redigera denna sektion
button_export: Exportera button_export: Exportera
button_delete_my_account: Ta bort mitt konto button_delete_my_account: Ta bort mitt konto
button_close: Stäng
button_reopen: Återöppna
status_active: aktiv status_active: aktiv
status_registered: registrerad status_registered: registrerad
status_locked: låst status_locked: låst
project_status_active: aktiv
project_status_closed: stängd
project_status_archived: arkiverad
version_status_open: öppen version_status_open: öppen
version_status_locked: låst version_status_locked: låst
version_status_closed: stängd version_status_closed: stängd
@@ -1067,8 +1026,6 @@ sv:
text_issue_conflict_resolution_add_notes: Lägg till mina anteckningar och kasta mina andra ändringar text_issue_conflict_resolution_add_notes: Lägg till mina anteckningar och kasta mina andra ändringar
text_issue_conflict_resolution_cancel: Kasta alla mina ändringar och visa igen %{link} text_issue_conflict_resolution_cancel: Kasta alla mina ändringar och visa igen %{link}
text_account_destroy_confirmation: "Är du säker på att du vill fortsätta?\nDitt konto kommer tas bort permanent, utan möjlighet att återaktivera det." text_account_destroy_confirmation: "Är du säker på att du vill fortsätta?\nDitt konto kommer tas bort permanent, utan möjlighet att återaktivera det."
text_session_expiration_settings: "Varning: ändring av dessa inställningar kan få alla nuvarande sessioner, inklusive din egen, att gå ut."
text_project_closed: Detta projekt är stängt och skrivskyddat.
default_role_manager: Projektledare default_role_manager: Projektledare
default_role_developer: Utvecklare default_role_developer: Utvecklare
@@ -1114,9 +1071,51 @@ sv:
description_date_range_interval: Ange intervall genom att välja start- och slutdatum description_date_range_interval: Ange intervall genom att välja start- och slutdatum
description_date_from: Ange startdatum description_date_from: Ange startdatum
description_date_to: Ange slutdatum description_date_to: Ange slutdatum
error_session_expired: Your session has expired. Please login again.
text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours."
setting_session_lifetime: Session maximum lifetime
setting_session_timeout: Session inactivity timeout
label_session_expiration: Session expiration
permission_close_project: Close / reopen the project
label_show_closed_projects: View closed projects
button_close: Close
button_reopen: Reopen
project_status_active: active
project_status_closed: closed
project_status_archived: archived
text_project_closed: This project is closed and read-only.
notice_user_successful_create: User %{id} created.
field_core_fields: Standard fields
field_timeout: Timeout (in seconds)
setting_thumbnails_enabled: Display attachment thumbnails
setting_thumbnails_size: Thumbnails size (in pixels)
label_status_transitions: Status transitions
label_fields_permissions: Fields permissions
label_readonly: Read-only
label_required: Required
text_repository_identifier_info: Ändast gemener (a-z), siffror, streck och understreck är tillåtna.<br />När identifieraren sparats kan den inte ändras. text_repository_identifier_info: Ändast gemener (a-z), siffror, streck och understreck är tillåtna.<br />När identifieraren sparats kan den inte ändras.
field_board_parent: Parent forum
label_attribute_of_project: Project's %{name}
label_attribute_of_author: Author's %{name}
label_attribute_of_assigned_to: Assignee's %{name}
label_attribute_of_fixed_version: Target version's %{name}
label_copy_subtasks: Copy subtasks
label_copied_to: copied to
label_copied_from: copied from
label_any_issues_in_project: any issues in project
label_any_issues_not_in_project: any issues not in project
field_private_notes: Private notes
permission_view_private_notes: View private notes
permission_set_notes_private: Set notes as private
label_no_issues_in_project: no issues in project
label_any: alla label_any: alla
label_last_n_weeks: last %{count} weeks
setting_cross_project_subtasks: Allow cross-project subtasks
label_cross_project_descendants: Med underprojekt label_cross_project_descendants: Med underprojekt
label_cross_project_tree: Med projektträd label_cross_project_tree: Med projektträd
label_cross_project_hierarchy: Med projekthierarki label_cross_project_hierarchy: Med projekthierarki
label_cross_project_system: Med alla projekt label_cross_project_system: Med alla projekt
button_hide: Hide
setting_non_working_week_days: Non-working days
label_in_the_next_days: in the next
label_in_the_past_days: in the past
-27
View File
@@ -4,33 +4,6 @@ Redmine - project management software
Copyright (C) 2006-2012 Jean-Philippe Lang Copyright (C) 2006-2012 Jean-Philippe Lang
http://www.redmine.org/ http://www.redmine.org/
== 2013-02-12 v2.2.3
* Upgrade to Rails 3.2.12
* Defect #11987: pdf: Broken new line in table
* Defect #12930: 404 Error when referencing different project source files in the wiki syntax
* Defect #12979: Wiki link syntax commit:repo_a:abcd doesn't work
* Defect #13075: Can't clear custom field value through context menu in the issue list
* Defect #13097: Project copy fails when wiki module is disabled
* Defect #13126: Issue view: estimated time vs. spent time
* Patch #12922: Update Spanish translation
* Patch #12928: Bulgarian translation for 2.2-stable
* Patch #12987: Russian translation for 2.2-stable
== 2013-01-20 v2.2.2
* Defect #7510: Link to attachment should return latest attachment
* Defect #9842: {{toc}} is not replaced by table of content when exporting wiki page to pdf
* Defect #12749: Plugins cannot route wiki page sub-path
* Defect #12799: Cannot edit a wiki section which title starts with a tab
* Defect #12801: Viewing the history of a wiki page with attachments raises an error
* Defect #12833: Input fields restricted on length should have maxlength parameter set
* Defect #12838: Blank page when clicking Add with no block selected on my page layout
* Defect #12851: "Parent task is invalid" while editing child issues by Role with restricted Issues Visibility
* Patch #12800: Serbian Latin translation patch (sr-YU.yml)
* Patch #12809: Swedish Translation for r11162
* Patch #12818: Minor swedish translation fix
== 2013-01-09 v2.2.1 == 2013-01-09 v2.2.1
* Upgrade to Rails 3.2.11 * Upgrade to Rails 3.2.11
+148 -148
View File
@@ -1,148 +1,148 @@
require 'rexml/document' require 'rexml/document'
require 'SVG/Graph/Graph' require 'SVG/Graph/Graph'
require 'SVG/Graph/BarBase' require 'SVG/Graph/BarBase'
module SVG module SVG
module Graph module Graph
# === Create presentation quality SVG bar graphs easily # === Create presentation quality SVG bar graphs easily
# #
# = Synopsis # = Synopsis
# #
# require 'SVG/Graph/Bar' # require 'SVG/Graph/Bar'
# #
# fields = %w(Jan Feb Mar); # fields = %w(Jan Feb Mar);
# data_sales_02 = [12, 45, 21] # data_sales_02 = [12, 45, 21]
# #
# graph = SVG::Graph::Bar.new( # graph = SVG::Graph::Bar.new(
# :height => 500, # :height => 500,
# :width => 300, # :width => 300,
# :fields => fields # :fields => fields
# ) # )
# #
# graph.add_data( # graph.add_data(
# :data => data_sales_02, # :data => data_sales_02,
# :title => 'Sales 2002' # :title => 'Sales 2002'
# ) # )
# #
# print "Content-type: image/svg+xml\r\n\r\n" # print "Content-type: image/svg+xml\r\n\r\n"
# print graph.burn # print graph.burn
# #
# = Description # = Description
# #
# This object aims to allow you to easily create high quality # This object aims to allow you to easily create high quality
# SVG[http://www.w3c.org/tr/svg bar graphs. You can either use the default # SVG[http://www.w3c.org/tr/svg bar graphs. You can either use the default
# style sheet or supply your own. Either way there are many options which # style sheet or supply your own. Either way there are many options which
# can be configured to give you control over how the graph is generated - # can be configured to give you control over how the graph is generated -
# with or without a key, data elements at each point, title, subtitle etc. # with or without a key, data elements at each point, title, subtitle etc.
# #
# = Notes # = Notes
# #
# The default stylesheet handles upto 12 data sets, if you # The default stylesheet handles upto 12 data sets, if you
# use more you must create your own stylesheet and add the # use more you must create your own stylesheet and add the
# additional settings for the extra data sets. You will know # additional settings for the extra data sets. You will know
# if you go over 12 data sets as they will have no style and # if you go over 12 data sets as they will have no style and
# be in black. # be in black.
# #
# = Examples # = Examples
# #
# * http://germane-software.com/repositories/public/SVG/test/test.rb # * http://germane-software.com/repositories/public/SVG/test/test.rb
# #
# = See also # = See also
# #
# * SVG::Graph::Graph # * SVG::Graph::Graph
# * SVG::Graph::BarHorizontal # * SVG::Graph::BarHorizontal
# * SVG::Graph::Line # * SVG::Graph::Line
# * SVG::Graph::Pie # * SVG::Graph::Pie
# * SVG::Graph::Plot # * SVG::Graph::Plot
# * SVG::Graph::TimeSeries # * SVG::Graph::TimeSeries
class Bar < BarBase class Bar < BarBase
include REXML include REXML
# See Graph::initialize and BarBase::set_defaults # See Graph::initialize and BarBase::set_defaults
def set_defaults def set_defaults
super super
self.top_align = self.top_font = 1 self.top_align = self.top_font = 1
end end
protected protected
def get_x_labels def get_x_labels
@config[:fields] @config[:fields]
end end
def get_y_labels def get_y_labels
maxvalue = max_value maxvalue = max_value
minvalue = min_value minvalue = min_value
range = maxvalue - minvalue range = maxvalue - minvalue
top_pad = range == 0 ? 10 : range / 20.0 top_pad = range == 0 ? 10 : range / 20.0
scale_range = (maxvalue + top_pad) - minvalue scale_range = (maxvalue + top_pad) - minvalue
scale_division = scale_divisions || (scale_range / 10.0) scale_division = scale_divisions || (scale_range / 10.0)
if scale_integers if scale_integers
scale_division = scale_division < 1 ? 1 : scale_division.round scale_division = scale_division < 1 ? 1 : scale_division.round
end end
rv = [] rv = []
maxvalue = maxvalue%scale_division == 0 ? maxvalue = maxvalue%scale_division == 0 ?
maxvalue : maxvalue + scale_division maxvalue : maxvalue + scale_division
minvalue.step( maxvalue, scale_division ) {|v| rv << v} minvalue.step( maxvalue, scale_division ) {|v| rv << v}
return rv return rv
end end
def x_label_offset( width ) def x_label_offset( width )
width / 2.0 width / 2.0
end end
def draw_data def draw_data
minvalue = min_value minvalue = min_value
fieldwidth = field_width fieldwidth = field_width
unit_size = (@graph_height.to_f - font_size*2*top_font) / unit_size = (@graph_height.to_f - font_size*2*top_font) /
(get_y_labels.max - get_y_labels.min) (get_y_labels.max - get_y_labels.min)
bargap = bar_gap ? (fieldwidth < 10 ? fieldwidth / 2 : 10) : 0 bargap = bar_gap ? (fieldwidth < 10 ? fieldwidth / 2 : 10) : 0
bar_width = fieldwidth - bargap bar_width = fieldwidth - bargap
bar_width /= @data.length if stack == :side bar_width /= @data.length if stack == :side
x_mod = (@graph_width-bargap)/2 - (stack==:side ? bar_width/2 : 0) x_mod = (@graph_width-bargap)/2 - (stack==:side ? bar_width/2 : 0)
bottom = @graph_height bottom = @graph_height
field_count = 0 field_count = 0
@config[:fields].each_index { |i| @config[:fields].each_index { |i|
dataset_count = 0 dataset_count = 0
for dataset in @data for dataset in @data
# cases (assume 0 = +ve): # cases (assume 0 = +ve):
# value min length # value min length
# +ve +ve value - min # +ve +ve value - min
# +ve -ve value - 0 # +ve -ve value - 0
# -ve -ve value.abs - 0 # -ve -ve value.abs - 0
value = dataset[:data][i] value = dataset[:data][i]
left = (fieldwidth * field_count) left = (fieldwidth * field_count)
length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
# top is 0 if value is negative # top is 0 if value is negative
top = bottom - (((value < 0 ? 0 : value) - minvalue) * unit_size) top = bottom - (((value < 0 ? 0 : value) - minvalue) * unit_size)
left += bar_width * dataset_count if stack == :side left += bar_width * dataset_count if stack == :side
@graph.add_element( "rect", { @graph.add_element( "rect", {
"x" => left.to_s, "x" => left.to_s,
"y" => top.to_s, "y" => top.to_s,
"width" => bar_width.to_s, "width" => bar_width.to_s,
"height" => length.to_s, "height" => length.to_s,
"class" => "fill#{dataset_count+1}" "class" => "fill#{dataset_count+1}"
}) })
make_datapoint_text(left + bar_width/2.0, top - 6, value.to_s) make_datapoint_text(left + bar_width/2.0, top - 6, value.to_s)
dataset_count += 1 dataset_count += 1
end end
field_count += 1 field_count += 1
} }
end end
end end
end end
end end
+139 -139
View File
@@ -1,139 +1,139 @@
require 'rexml/document' require 'rexml/document'
require 'SVG/Graph/Graph' require 'SVG/Graph/Graph'
module SVG module SVG
module Graph module Graph
# = Synopsis # = Synopsis
# #
# A superclass for bar-style graphs. Do not attempt to instantiate # A superclass for bar-style graphs. Do not attempt to instantiate
# directly; use one of the subclasses instead. # directly; use one of the subclasses instead.
# #
# = Author # = Author
# #
# Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom> # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
# #
# Copyright 2004 Sean E. Russell # Copyright 2004 Sean E. Russell
# This software is available under the Ruby license[LICENSE.txt] # This software is available under the Ruby license[LICENSE.txt]
# #
class BarBase < SVG::Graph::Graph class BarBase < SVG::Graph::Graph
# Ensures that :fields are provided in the configuration. # Ensures that :fields are provided in the configuration.
def initialize config def initialize config
raise "fields was not supplied or is empty" unless config[:fields] && raise "fields was not supplied or is empty" unless config[:fields] &&
config[:fields].kind_of?(Array) && config[:fields].kind_of?(Array) &&
config[:fields].length > 0 config[:fields].length > 0
super super
end end
# In addition to the defaults set in Graph::initialize, sets # In addition to the defaults set in Graph::initialize, sets
# [bar_gap] true # [bar_gap] true
# [stack] :overlap # [stack] :overlap
def set_defaults def set_defaults
init_with( :bar_gap => true, :stack => :overlap ) init_with( :bar_gap => true, :stack => :overlap )
end end
# Whether to have a gap between the bars or not, default # Whether to have a gap between the bars or not, default
# is true, set to false if you don't want gaps. # is true, set to false if you don't want gaps.
attr_accessor :bar_gap attr_accessor :bar_gap
# How to stack data sets. :overlap overlaps bars with # How to stack data sets. :overlap overlaps bars with
# transparent colors, :top stacks bars on top of one another, # transparent colors, :top stacks bars on top of one another,
# :side stacks the bars side-by-side. Defaults to :overlap. # :side stacks the bars side-by-side. Defaults to :overlap.
attr_accessor :stack attr_accessor :stack
protected protected
def max_value def max_value
@data.collect{|x| x[:data].max}.max @data.collect{|x| x[:data].max}.max
end end
def min_value def min_value
min = 0 min = 0
if min_scale_value.nil? if min_scale_value.nil?
min = @data.collect{|x| x[:data].min}.min min = @data.collect{|x| x[:data].min}.min
min = min > 0 ? 0 : min min = min > 0 ? 0 : min
else else
min = min_scale_value min = min_scale_value
end end
return min return min
end end
def get_css def get_css
return <<EOL return <<EOL
/* default fill styles for multiple datasets (probably only use a single dataset on this graph though) */ /* default fill styles for multiple datasets (probably only use a single dataset on this graph though) */
.key1,.fill1{ .key1,.fill1{
fill: #ff0000; fill: #ff0000;
fill-opacity: 0.5; fill-opacity: 0.5;
stroke: none; stroke: none;
stroke-width: 0.5px; stroke-width: 0.5px;
} }
.key2,.fill2{ .key2,.fill2{
fill: #0000ff; fill: #0000ff;
fill-opacity: 0.5; fill-opacity: 0.5;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key3,.fill3{ .key3,.fill3{
fill: #00ff00; fill: #00ff00;
fill-opacity: 0.5; fill-opacity: 0.5;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key4,.fill4{ .key4,.fill4{
fill: #ffcc00; fill: #ffcc00;
fill-opacity: 0.5; fill-opacity: 0.5;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key5,.fill5{ .key5,.fill5{
fill: #00ccff; fill: #00ccff;
fill-opacity: 0.5; fill-opacity: 0.5;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key6,.fill6{ .key6,.fill6{
fill: #ff00ff; fill: #ff00ff;
fill-opacity: 0.5; fill-opacity: 0.5;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key7,.fill7{ .key7,.fill7{
fill: #00ffff; fill: #00ffff;
fill-opacity: 0.5; fill-opacity: 0.5;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key8,.fill8{ .key8,.fill8{
fill: #ffff00; fill: #ffff00;
fill-opacity: 0.5; fill-opacity: 0.5;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key9,.fill9{ .key9,.fill9{
fill: #cc6666; fill: #cc6666;
fill-opacity: 0.5; fill-opacity: 0.5;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key10,.fill10{ .key10,.fill10{
fill: #663399; fill: #663399;
fill-opacity: 0.5; fill-opacity: 0.5;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key11,.fill11{ .key11,.fill11{
fill: #339900; fill: #339900;
fill-opacity: 0.5; fill-opacity: 0.5;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key12,.fill12{ .key12,.fill12{
fill: #9966FF; fill: #9966FF;
fill-opacity: 0.5; fill-opacity: 0.5;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
EOL EOL
end end
end end
end end
end end
+149 -149
View File
@@ -1,149 +1,149 @@
require 'rexml/document' require 'rexml/document'
require 'SVG/Graph/BarBase' require 'SVG/Graph/BarBase'
module SVG module SVG
module Graph module Graph
# === Create presentation quality SVG horitonzal bar graphs easily # === Create presentation quality SVG horitonzal bar graphs easily
# #
# = Synopsis # = Synopsis
# #
# require 'SVG/Graph/BarHorizontal' # require 'SVG/Graph/BarHorizontal'
# #
# fields = %w(Jan Feb Mar) # fields = %w(Jan Feb Mar)
# data_sales_02 = [12, 45, 21] # data_sales_02 = [12, 45, 21]
# #
# graph = SVG::Graph::BarHorizontal.new({ # graph = SVG::Graph::BarHorizontal.new({
# :height => 500, # :height => 500,
# :width => 300, # :width => 300,
# :fields => fields, # :fields => fields,
# }) # })
# #
# graph.add_data({ # graph.add_data({
# :data => data_sales_02, # :data => data_sales_02,
# :title => 'Sales 2002', # :title => 'Sales 2002',
# }) # })
# #
# print "Content-type: image/svg+xml\r\n\r\n" # print "Content-type: image/svg+xml\r\n\r\n"
# print graph.burn # print graph.burn
# #
# = Description # = Description
# #
# This object aims to allow you to easily create high quality # This object aims to allow you to easily create high quality
# SVG horitonzal bar graphs. You can either use the default style sheet # SVG horitonzal bar graphs. You can either use the default style sheet
# or supply your own. Either way there are many options which can # or supply your own. Either way there are many options which can
# be configured to give you control over how the graph is # be configured to give you control over how the graph is
# generated - with or without a key, data elements at each point, # generated - with or without a key, data elements at each point,
# title, subtitle etc. # title, subtitle etc.
# #
# = Examples # = Examples
# #
# * http://germane-software.com/repositories/public/SVG/test/test.rb # * http://germane-software.com/repositories/public/SVG/test/test.rb
# #
# = See also # = See also
# #
# * SVG::Graph::Graph # * SVG::Graph::Graph
# * SVG::Graph::Bar # * SVG::Graph::Bar
# * SVG::Graph::Line # * SVG::Graph::Line
# * SVG::Graph::Pie # * SVG::Graph::Pie
# * SVG::Graph::Plot # * SVG::Graph::Plot
# * SVG::Graph::TimeSeries # * SVG::Graph::TimeSeries
# #
# == Author # == Author
# #
# Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom> # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
# #
# Copyright 2004 Sean E. Russell # Copyright 2004 Sean E. Russell
# This software is available under the Ruby license[LICENSE.txt] # This software is available under the Ruby license[LICENSE.txt]
# #
class BarHorizontal < BarBase class BarHorizontal < BarBase
# In addition to the defaults set in BarBase::set_defaults, sets # In addition to the defaults set in BarBase::set_defaults, sets
# [rotate_y_labels] true # [rotate_y_labels] true
# [show_x_guidelines] true # [show_x_guidelines] true
# [show_y_guidelines] false # [show_y_guidelines] false
def set_defaults def set_defaults
super super
init_with( init_with(
:rotate_y_labels => true, :rotate_y_labels => true,
:show_x_guidelines => true, :show_x_guidelines => true,
:show_y_guidelines => false :show_y_guidelines => false
) )
self.right_align = self.right_font = 1 self.right_align = self.right_font = 1
end end
protected protected
def get_x_labels def get_x_labels
maxvalue = max_value maxvalue = max_value
minvalue = min_value minvalue = min_value
range = maxvalue - minvalue range = maxvalue - minvalue
top_pad = range == 0 ? 10 : range / 20.0 top_pad = range == 0 ? 10 : range / 20.0
scale_range = (maxvalue + top_pad) - minvalue scale_range = (maxvalue + top_pad) - minvalue
scale_division = scale_divisions || (scale_range / 10.0) scale_division = scale_divisions || (scale_range / 10.0)
if scale_integers if scale_integers
scale_division = scale_division < 1 ? 1 : scale_division.round scale_division = scale_division < 1 ? 1 : scale_division.round
end end
rv = [] rv = []
maxvalue = maxvalue%scale_division == 0 ? maxvalue = maxvalue%scale_division == 0 ?
maxvalue : maxvalue + scale_division maxvalue : maxvalue + scale_division
minvalue.step( maxvalue, scale_division ) {|v| rv << v} minvalue.step( maxvalue, scale_division ) {|v| rv << v}
return rv return rv
end end
def get_y_labels def get_y_labels
@config[:fields] @config[:fields]
end end
def y_label_offset( height ) def y_label_offset( height )
height / -2.0 height / -2.0
end end
def draw_data def draw_data
minvalue = min_value minvalue = min_value
fieldheight = field_height fieldheight = field_height
unit_size = (@graph_width.to_f - font_size*2*right_font ) / unit_size = (@graph_width.to_f - font_size*2*right_font ) /
(get_x_labels.max - get_x_labels.min ) (get_x_labels.max - get_x_labels.min )
bargap = bar_gap ? (fieldheight < 10 ? fieldheight / 2 : 10) : 0 bargap = bar_gap ? (fieldheight < 10 ? fieldheight / 2 : 10) : 0
bar_height = fieldheight - bargap bar_height = fieldheight - bargap
bar_height /= @data.length if stack == :side bar_height /= @data.length if stack == :side
y_mod = (bar_height / 2) + (font_size / 2) y_mod = (bar_height / 2) + (font_size / 2)
field_count = 1 field_count = 1
@config[:fields].each_index { |i| @config[:fields].each_index { |i|
dataset_count = 0 dataset_count = 0
for dataset in @data for dataset in @data
value = dataset[:data][i] value = dataset[:data][i]
top = @graph_height - (fieldheight * field_count) top = @graph_height - (fieldheight * field_count)
top += (bar_height * dataset_count) if stack == :side top += (bar_height * dataset_count) if stack == :side
# cases (assume 0 = +ve): # cases (assume 0 = +ve):
# value min length left # value min length left
# +ve +ve value.abs - min minvalue.abs # +ve +ve value.abs - min minvalue.abs
# +ve -ve value.abs - 0 minvalue.abs # +ve -ve value.abs - 0 minvalue.abs
# -ve -ve value.abs - 0 minvalue.abs + value # -ve -ve value.abs - 0 minvalue.abs + value
length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size length = (value.abs - (minvalue > 0 ? minvalue : 0)) * unit_size
left = (minvalue.abs + (value < 0 ? value : 0)) * unit_size left = (minvalue.abs + (value < 0 ? value : 0)) * unit_size
@graph.add_element( "rect", { @graph.add_element( "rect", {
"x" => left.to_s, "x" => left.to_s,
"y" => top.to_s, "y" => top.to_s,
"width" => length.to_s, "width" => length.to_s,
"height" => bar_height.to_s, "height" => bar_height.to_s,
"class" => "fill#{dataset_count+1}" "class" => "fill#{dataset_count+1}"
}) })
make_datapoint_text( make_datapoint_text(
left+length+5, top+y_mod, value, "text-anchor: start; " left+length+5, top+y_mod, value, "text-anchor: start; "
) )
dataset_count += 1 dataset_count += 1
end end
field_count += 1 field_count += 1
} }
end end
end end
end end
end end
+978 -978
View File
File diff suppressed because it is too large Load Diff
+395 -395
View File
@@ -1,395 +1,395 @@
require 'SVG/Graph/Graph' require 'SVG/Graph/Graph'
module SVG module SVG
module Graph module Graph
# === Create presentation quality SVG pie graphs easily # === Create presentation quality SVG pie graphs easily
# #
# == Synopsis # == Synopsis
# #
# require 'SVG/Graph/Pie' # require 'SVG/Graph/Pie'
# #
# fields = %w(Jan Feb Mar) # fields = %w(Jan Feb Mar)
# data_sales_02 = [12, 45, 21] # data_sales_02 = [12, 45, 21]
# #
# graph = SVG::Graph::Pie.new({ # graph = SVG::Graph::Pie.new({
# :height => 500, # :height => 500,
# :width => 300, # :width => 300,
# :fields => fields, # :fields => fields,
# }) # })
# #
# graph.add_data({ # graph.add_data({
# :data => data_sales_02, # :data => data_sales_02,
# :title => 'Sales 2002', # :title => 'Sales 2002',
# }) # })
# #
# print "Content-type: image/svg+xml\r\n\r\n" # print "Content-type: image/svg+xml\r\n\r\n"
# print graph.burn(); # print graph.burn();
# #
# == Description # == Description
# #
# This object aims to allow you to easily create high quality # This object aims to allow you to easily create high quality
# SVG pie graphs. You can either use the default style sheet # SVG pie graphs. You can either use the default style sheet
# or supply your own. Either way there are many options which can # or supply your own. Either way there are many options which can
# be configured to give you control over how the graph is # be configured to give you control over how the graph is
# generated - with or without a key, display percent on pie chart, # generated - with or without a key, display percent on pie chart,
# title, subtitle etc. # title, subtitle etc.
# #
# = Examples # = Examples
# #
# http://www.germane-software/repositories/public/SVG/test/single.rb # http://www.germane-software/repositories/public/SVG/test/single.rb
# #
# == See also # == See also
# #
# * SVG::Graph::Graph # * SVG::Graph::Graph
# * SVG::Graph::BarHorizontal # * SVG::Graph::BarHorizontal
# * SVG::Graph::Bar # * SVG::Graph::Bar
# * SVG::Graph::Line # * SVG::Graph::Line
# * SVG::Graph::Plot # * SVG::Graph::Plot
# * SVG::Graph::TimeSeries # * SVG::Graph::TimeSeries
# #
# == Author # == Author
# #
# Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom> # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
# #
# Copyright 2004 Sean E. Russell # Copyright 2004 Sean E. Russell
# This software is available under the Ruby license[LICENSE.txt] # This software is available under the Ruby license[LICENSE.txt]
# #
class Pie < Graph class Pie < Graph
# Defaults are those set by Graph::initialize, and # Defaults are those set by Graph::initialize, and
# [show_shadow] true # [show_shadow] true
# [shadow_offset] 10 # [shadow_offset] 10
# [show_data_labels] false # [show_data_labels] false
# [show_actual_values] false # [show_actual_values] false
# [show_percent] true # [show_percent] true
# [show_key_data_labels] true # [show_key_data_labels] true
# [show_key_actual_values] true # [show_key_actual_values] true
# [show_key_percent] false # [show_key_percent] false
# [expanded] false # [expanded] false
# [expand_greatest] false # [expand_greatest] false
# [expand_gap] 10 # [expand_gap] 10
# [show_x_labels] false # [show_x_labels] false
# [show_y_labels] false # [show_y_labels] false
# [datapoint_font_size] 12 # [datapoint_font_size] 12
def set_defaults def set_defaults
init_with( init_with(
:show_shadow => true, :show_shadow => true,
:shadow_offset => 10, :shadow_offset => 10,
:show_data_labels => false, :show_data_labels => false,
:show_actual_values => false, :show_actual_values => false,
:show_percent => true, :show_percent => true,
:show_key_data_labels => true, :show_key_data_labels => true,
:show_key_actual_values => true, :show_key_actual_values => true,
:show_key_percent => false, :show_key_percent => false,
:expanded => false, :expanded => false,
:expand_greatest => false, :expand_greatest => false,
:expand_gap => 10, :expand_gap => 10,
:show_x_labels => false, :show_x_labels => false,
:show_y_labels => false, :show_y_labels => false,
:datapoint_font_size => 12 :datapoint_font_size => 12
) )
@data = [] @data = []
end end
# Adds a data set to the graph. # Adds a data set to the graph.
# #
# graph.add_data( { :data => [1,2,3,4] } ) # graph.add_data( { :data => [1,2,3,4] } )
# #
# Note that the :title is not necessary. If multiple # Note that the :title is not necessary. If multiple
# data sets are added to the graph, the pie chart will # data sets are added to the graph, the pie chart will
# display the +sums+ of the data. EG: # display the +sums+ of the data. EG:
# #
# graph.add_data( { :data => [1,2,3,4] } ) # graph.add_data( { :data => [1,2,3,4] } )
# graph.add_data( { :data => [2,3,5,9] } ) # graph.add_data( { :data => [2,3,5,9] } )
# #
# is the same as: # is the same as:
# #
# graph.add_data( { :data => [3,5,8,13] } ) # graph.add_data( { :data => [3,5,8,13] } )
def add_data arg def add_data arg
arg[:data].each_index {|idx| arg[:data].each_index {|idx|
@data[idx] = 0 unless @data[idx] @data[idx] = 0 unless @data[idx]
@data[idx] += arg[:data][idx] @data[idx] += arg[:data][idx]
} }
end end
# If true, displays a drop shadow for the chart # If true, displays a drop shadow for the chart
attr_accessor :show_shadow attr_accessor :show_shadow
# Sets the offset of the shadow from the pie chart # Sets the offset of the shadow from the pie chart
attr_accessor :shadow_offset attr_accessor :shadow_offset
# If true, display the data labels on the chart # If true, display the data labels on the chart
attr_accessor :show_data_labels attr_accessor :show_data_labels
# If true, display the actual field values in the data labels # If true, display the actual field values in the data labels
attr_accessor :show_actual_values attr_accessor :show_actual_values
# If true, display the percentage value of each pie wedge in the data # If true, display the percentage value of each pie wedge in the data
# labels # labels
attr_accessor :show_percent attr_accessor :show_percent
# If true, display the labels in the key # If true, display the labels in the key
attr_accessor :show_key_data_labels attr_accessor :show_key_data_labels
# If true, display the actual value of the field in the key # If true, display the actual value of the field in the key
attr_accessor :show_key_actual_values attr_accessor :show_key_actual_values
# If true, display the percentage value of the wedges in the key # If true, display the percentage value of the wedges in the key
attr_accessor :show_key_percent attr_accessor :show_key_percent
# If true, "explode" the pie (put space between the wedges) # If true, "explode" the pie (put space between the wedges)
attr_accessor :expanded attr_accessor :expanded
# If true, expand the largest pie wedge # If true, expand the largest pie wedge
attr_accessor :expand_greatest attr_accessor :expand_greatest
# The amount of space between expanded wedges # The amount of space between expanded wedges
attr_accessor :expand_gap attr_accessor :expand_gap
# The font size of the data point labels # The font size of the data point labels
attr_accessor :datapoint_font_size attr_accessor :datapoint_font_size
protected protected
def add_defs defs def add_defs defs
gradient = defs.add_element( "filter", { gradient = defs.add_element( "filter", {
"id"=>"dropshadow", "id"=>"dropshadow",
"width" => "1.2", "width" => "1.2",
"height" => "1.2", "height" => "1.2",
} ) } )
gradient.add_element( "feGaussianBlur", { gradient.add_element( "feGaussianBlur", {
"stdDeviation" => "4", "stdDeviation" => "4",
"result" => "blur" "result" => "blur"
}) })
end end
# We don't need the graph # We don't need the graph
def draw_graph def draw_graph
end end
def get_y_labels def get_y_labels
[""] [""]
end end
def get_x_labels def get_x_labels
[""] [""]
end end
def keys def keys
total = 0 total = 0
max_value = 0 max_value = 0
@data.each {|x| total += x } @data.each {|x| total += x }
percent_scale = 100.0 / total percent_scale = 100.0 / total
count = -1 count = -1
a = @config[:fields].collect{ |x| a = @config[:fields].collect{ |x|
count += 1 count += 1
v = @data[count] v = @data[count]
perc = show_key_percent ? " "+(v * percent_scale).round.to_s+"%" : "" perc = show_key_percent ? " "+(v * percent_scale).round.to_s+"%" : ""
x + " [" + v.to_s + "]" + perc x + " [" + v.to_s + "]" + perc
} }
end end
RADIANS = Math::PI/180 RADIANS = Math::PI/180
def draw_data def draw_data
@graph = @root.add_element( "g" ) @graph = @root.add_element( "g" )
background = @graph.add_element("g") background = @graph.add_element("g")
midground = @graph.add_element("g") midground = @graph.add_element("g")
diameter = @graph_height > @graph_width ? @graph_width : @graph_height diameter = @graph_height > @graph_width ? @graph_width : @graph_height
diameter -= expand_gap if expanded or expand_greatest diameter -= expand_gap if expanded or expand_greatest
diameter -= datapoint_font_size if show_data_labels diameter -= datapoint_font_size if show_data_labels
diameter -= 10 if show_shadow diameter -= 10 if show_shadow
radius = diameter / 2.0 radius = diameter / 2.0
xoff = (width - diameter) / 2 xoff = (width - diameter) / 2
yoff = (height - @border_bottom - diameter) yoff = (height - @border_bottom - diameter)
yoff -= 10 if show_shadow yoff -= 10 if show_shadow
@graph.attributes['transform'] = "translate( #{xoff} #{yoff} )" @graph.attributes['transform'] = "translate( #{xoff} #{yoff} )"
wedge_text_pad = 5 wedge_text_pad = 5
wedge_text_pad = 20 if show_percent and show_data_labels wedge_text_pad = 20 if show_percent and show_data_labels
total = 0 total = 0
max_value = 0 max_value = 0
@data.each {|x| @data.each {|x|
max_value = max_value < x ? x : max_value max_value = max_value < x ? x : max_value
total += x total += x
} }
percent_scale = 100.0 / total percent_scale = 100.0 / total
prev_percent = 0 prev_percent = 0
rad_mult = 3.6 * RADIANS rad_mult = 3.6 * RADIANS
@config[:fields].each_index { |count| @config[:fields].each_index { |count|
value = @data[count] value = @data[count]
percent = percent_scale * value percent = percent_scale * value
radians = prev_percent * rad_mult radians = prev_percent * rad_mult
x_start = radius+(Math.sin(radians) * radius) x_start = radius+(Math.sin(radians) * radius)
y_start = radius-(Math.cos(radians) * radius) y_start = radius-(Math.cos(radians) * radius)
radians = (prev_percent+percent) * rad_mult radians = (prev_percent+percent) * rad_mult
x_end = radius+(Math.sin(radians) * radius) x_end = radius+(Math.sin(radians) * radius)
x_end -= 0.00001 if @data.length == 1 x_end -= 0.00001 if @data.length == 1
y_end = radius-(Math.cos(radians) * radius) y_end = radius-(Math.cos(radians) * radius)
path = "M#{radius},#{radius} L#{x_start},#{y_start} "+ path = "M#{radius},#{radius} L#{x_start},#{y_start} "+
"A#{radius},#{radius} "+ "A#{radius},#{radius} "+
"0, #{percent >= 50 ? '1' : '0'},1, "+ "0, #{percent >= 50 ? '1' : '0'},1, "+
"#{x_end} #{y_end} Z" "#{x_end} #{y_end} Z"
wedge = @foreground.add_element( "path", { wedge = @foreground.add_element( "path", {
"d" => path, "d" => path,
"class" => "fill#{count+1}" "class" => "fill#{count+1}"
}) })
translate = nil translate = nil
tx = 0 tx = 0
ty = 0 ty = 0
half_percent = prev_percent + percent / 2 half_percent = prev_percent + percent / 2
radians = half_percent * rad_mult radians = half_percent * rad_mult
if show_shadow if show_shadow
shadow = background.add_element( "path", { shadow = background.add_element( "path", {
"d" => path, "d" => path,
"filter" => "url(#dropshadow)", "filter" => "url(#dropshadow)",
"style" => "fill: #ccc; stroke: none;" "style" => "fill: #ccc; stroke: none;"
}) })
clear = midground.add_element( "path", { clear = midground.add_element( "path", {
"d" => path, "d" => path,
"style" => "fill: #fff; stroke: none;" "style" => "fill: #fff; stroke: none;"
}) })
end end
if expanded or (expand_greatest && value == max_value) if expanded or (expand_greatest && value == max_value)
tx = (Math.sin(radians) * expand_gap) tx = (Math.sin(radians) * expand_gap)
ty = -(Math.cos(radians) * expand_gap) ty = -(Math.cos(radians) * expand_gap)
translate = "translate( #{tx} #{ty} )" translate = "translate( #{tx} #{ty} )"
wedge.attributes["transform"] = translate wedge.attributes["transform"] = translate
clear.attributes["transform"] = translate if clear clear.attributes["transform"] = translate if clear
end end
if show_shadow if show_shadow
shadow.attributes["transform"] = shadow.attributes["transform"] =
"translate( #{tx+shadow_offset} #{ty+shadow_offset} )" "translate( #{tx+shadow_offset} #{ty+shadow_offset} )"
end end
if show_data_labels and value != 0 if show_data_labels and value != 0
label = "" label = ""
label += @config[:fields][count] if show_key_data_labels label += @config[:fields][count] if show_key_data_labels
label += " ["+value.to_s+"]" if show_actual_values label += " ["+value.to_s+"]" if show_actual_values
label += " "+percent.round.to_s+"%" if show_percent label += " "+percent.round.to_s+"%" if show_percent
msr = Math.sin(radians) msr = Math.sin(radians)
mcr = Math.cos(radians) mcr = Math.cos(radians)
tx = radius + (msr * radius) tx = radius + (msr * radius)
ty = radius -(mcr * radius) ty = radius -(mcr * radius)
if expanded or (expand_greatest && value == max_value) if expanded or (expand_greatest && value == max_value)
tx += (msr * expand_gap) tx += (msr * expand_gap)
ty -= (mcr * expand_gap) ty -= (mcr * expand_gap)
end end
@foreground.add_element( "text", { @foreground.add_element( "text", {
"x" => tx.to_s, "x" => tx.to_s,
"y" => ty.to_s, "y" => ty.to_s,
"class" => "dataPointLabel", "class" => "dataPointLabel",
"style" => "stroke: #fff; stroke-width: 2;" "style" => "stroke: #fff; stroke-width: 2;"
}).text = label.to_s }).text = label.to_s
@foreground.add_element( "text", { @foreground.add_element( "text", {
"x" => tx.to_s, "x" => tx.to_s,
"y" => ty.to_s, "y" => ty.to_s,
"class" => "dataPointLabel", "class" => "dataPointLabel",
}).text = label.to_s }).text = label.to_s
end end
prev_percent += percent prev_percent += percent
} }
end end
def round val, to def round val, to
up = 10**to.to_f up = 10**to.to_f
(val * up).to_i / up (val * up).to_i / up
end end
def get_css def get_css
return <<EOL return <<EOL
.dataPointLabel{ .dataPointLabel{
fill: #000000; fill: #000000;
text-anchor:middle; text-anchor:middle;
font-size: #{datapoint_font_size}px; font-size: #{datapoint_font_size}px;
font-family: "Arial", sans-serif; font-family: "Arial", sans-serif;
font-weight: normal; font-weight: normal;
} }
/* key - MUST match fill styles */ /* key - MUST match fill styles */
.key1,.fill1{ .key1,.fill1{
fill: #ff0000; fill: #ff0000;
fill-opacity: 0.7; fill-opacity: 0.7;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key2,.fill2{ .key2,.fill2{
fill: #0000ff; fill: #0000ff;
fill-opacity: 0.7; fill-opacity: 0.7;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key3,.fill3{ .key3,.fill3{
fill-opacity: 0.7; fill-opacity: 0.7;
fill: #00ff00; fill: #00ff00;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key4,.fill4{ .key4,.fill4{
fill-opacity: 0.7; fill-opacity: 0.7;
fill: #ffcc00; fill: #ffcc00;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key5,.fill5{ .key5,.fill5{
fill-opacity: 0.7; fill-opacity: 0.7;
fill: #00ccff; fill: #00ccff;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key6,.fill6{ .key6,.fill6{
fill-opacity: 0.7; fill-opacity: 0.7;
fill: #ff00ff; fill: #ff00ff;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key7,.fill7{ .key7,.fill7{
fill-opacity: 0.7; fill-opacity: 0.7;
fill: #00ff99; fill: #00ff99;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key8,.fill8{ .key8,.fill8{
fill-opacity: 0.7; fill-opacity: 0.7;
fill: #ffff00; fill: #ffff00;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key9,.fill9{ .key9,.fill9{
fill-opacity: 0.7; fill-opacity: 0.7;
fill: #cc6666; fill: #cc6666;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key10,.fill10{ .key10,.fill10{
fill-opacity: 0.7; fill-opacity: 0.7;
fill: #663399; fill: #663399;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key11,.fill11{ .key11,.fill11{
fill-opacity: 0.7; fill-opacity: 0.7;
fill: #339900; fill: #339900;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
.key12,.fill12{ .key12,.fill12{
fill-opacity: 0.7; fill-opacity: 0.7;
fill: #9966FF; fill: #9966FF;
stroke: none; stroke: none;
stroke-width: 1px; stroke-width: 1px;
} }
EOL EOL
end end
end end
end end
end end
+500 -500
View File
File diff suppressed because it is too large Load Diff
+238 -238
View File
@@ -1,238 +1,238 @@
require 'SVG/Graph/Plot' require 'SVG/Graph/Plot'
module SVG module SVG
module Graph module Graph
# === For creating SVG plots of scalar temporal data # === For creating SVG plots of scalar temporal data
# #
# = Synopsis # = Synopsis
# #
# require 'SVG/Graph/TimeSeriess' # require 'SVG/Graph/TimeSeriess'
# #
# # Data sets are x,y pairs # # Data sets are x,y pairs
# data1 = ["6/17/72", 11, "1/11/72", 7, "4/13/04 17:31", 11, # data1 = ["6/17/72", 11, "1/11/72", 7, "4/13/04 17:31", 11,
# "9/11/01", 9, "9/1/85", 2, "9/1/88", 1, "1/15/95", 13] # "9/11/01", 9, "9/1/85", 2, "9/1/88", 1, "1/15/95", 13]
# data2 = ["8/1/73", 18, "3/1/77", 15, "10/1/98", 4, # data2 = ["8/1/73", 18, "3/1/77", 15, "10/1/98", 4,
# "5/1/02", 14, "3/1/95", 6, "8/1/91", 12, "12/1/87", 6, # "5/1/02", 14, "3/1/95", 6, "8/1/91", 12, "12/1/87", 6,
# "5/1/84", 17, "10/1/80", 12] # "5/1/84", 17, "10/1/80", 12]
# #
# graph = SVG::Graph::TimeSeries.new( { # graph = SVG::Graph::TimeSeries.new( {
# :width => 640, # :width => 640,
# :height => 480, # :height => 480,
# :graph_title => title, # :graph_title => title,
# :show_graph_title => true, # :show_graph_title => true,
# :no_css => true, # :no_css => true,
# :key => true, # :key => true,
# :scale_x_integers => true, # :scale_x_integers => true,
# :scale_y_integers => true, # :scale_y_integers => true,
# :min_x_value => 0, # :min_x_value => 0,
# :min_y_value => 0, # :min_y_value => 0,
# :show_data_labels => true, # :show_data_labels => true,
# :show_x_guidelines => true, # :show_x_guidelines => true,
# :show_x_title => true, # :show_x_title => true,
# :x_title => "Time", # :x_title => "Time",
# :show_y_title => true, # :show_y_title => true,
# :y_title => "Ice Cream Cones", # :y_title => "Ice Cream Cones",
# :y_title_text_direction => :bt, # :y_title_text_direction => :bt,
# :stagger_x_labels => true, # :stagger_x_labels => true,
# :x_label_format => "%m/%d/%y", # :x_label_format => "%m/%d/%y",
# }) # })
# #
# graph.add_data({ # graph.add_data({
# :data => projection # :data => projection
# :title => 'Projected', # :title => 'Projected',
# }) # })
# #
# graph.add_data({ # graph.add_data({
# :data => actual, # :data => actual,
# :title => 'Actual', # :title => 'Actual',
# }) # })
# #
# print graph.burn() # print graph.burn()
# #
# = Description # = Description
# #
# Produces a graph of temporal scalar data. # Produces a graph of temporal scalar data.
# #
# = Examples # = Examples
# #
# http://www.germane-software/repositories/public/SVG/test/timeseries.rb # http://www.germane-software/repositories/public/SVG/test/timeseries.rb
# #
# = Notes # = Notes
# #
# The default stylesheet handles upto 10 data sets, if you # The default stylesheet handles upto 10 data sets, if you
# use more you must create your own stylesheet and add the # use more you must create your own stylesheet and add the
# additional settings for the extra data sets. You will know # additional settings for the extra data sets. You will know
# if you go over 10 data sets as they will have no style and # if you go over 10 data sets as they will have no style and
# be in black. # be in black.
# #
# Unlike the other types of charts, data sets must contain x,y pairs: # Unlike the other types of charts, data sets must contain x,y pairs:
# #
# [ "12:30", 2 ] # A data set with 1 point: ("12:30",2) # [ "12:30", 2 ] # A data set with 1 point: ("12:30",2)
# [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and # [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and
# # ("14:20",6) # # ("14:20",6)
# #
# Note that multiple data sets within the same chart can differ in length, # Note that multiple data sets within the same chart can differ in length,
# and that the data in the datasets needn't be in order; they will be ordered # and that the data in the datasets needn't be in order; they will be ordered
# by the plot along the X-axis. # by the plot along the X-axis.
# #
# The dates must be parseable by ParseDate, but otherwise can be # The dates must be parseable by ParseDate, but otherwise can be
# any order of magnitude (seconds within the hour, or years) # any order of magnitude (seconds within the hour, or years)
# #
# = See also # = See also
# #
# * SVG::Graph::Graph # * SVG::Graph::Graph
# * SVG::Graph::BarHorizontal # * SVG::Graph::BarHorizontal
# * SVG::Graph::Bar # * SVG::Graph::Bar
# * SVG::Graph::Line # * SVG::Graph::Line
# * SVG::Graph::Pie # * SVG::Graph::Pie
# * SVG::Graph::Plot # * SVG::Graph::Plot
# #
# == Author # == Author
# #
# Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom> # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
# #
# Copyright 2004 Sean E. Russell # Copyright 2004 Sean E. Russell
# This software is available under the Ruby license[LICENSE.txt] # This software is available under the Ruby license[LICENSE.txt]
# #
class TimeSeries < Plot class TimeSeries < Plot
# In addition to the defaults set by Graph::initialize and # In addition to the defaults set by Graph::initialize and
# Plot::set_defaults, sets: # Plot::set_defaults, sets:
# [x_label_format] '%Y-%m-%d %H:%M:%S' # [x_label_format] '%Y-%m-%d %H:%M:%S'
# [popup_format] '%Y-%m-%d %H:%M:%S' # [popup_format] '%Y-%m-%d %H:%M:%S'
def set_defaults def set_defaults
super super
init_with( init_with(
#:max_time_span => '', #:max_time_span => '',
:x_label_format => '%Y-%m-%d %H:%M:%S', :x_label_format => '%Y-%m-%d %H:%M:%S',
:popup_format => '%Y-%m-%d %H:%M:%S' :popup_format => '%Y-%m-%d %H:%M:%S'
) )
end end
# The format string use do format the X axis labels. # The format string use do format the X axis labels.
# See Time::strformat # See Time::strformat
attr_accessor :x_label_format attr_accessor :x_label_format
# Use this to set the spacing between dates on the axis. The value # Use this to set the spacing between dates on the axis. The value
# must be of the form # must be of the form
# "\d+ ?(days|weeks|months|years|hours|minutes|seconds)?" # "\d+ ?(days|weeks|months|years|hours|minutes|seconds)?"
# #
# EG: # EG:
# #
# graph.timescale_divisions = "2 weeks" # graph.timescale_divisions = "2 weeks"
# #
# will cause the chart to try to divide the X axis up into segments of # will cause the chart to try to divide the X axis up into segments of
# two week periods. # two week periods.
attr_accessor :timescale_divisions attr_accessor :timescale_divisions
# The formatting used for the popups. See x_label_format # The formatting used for the popups. See x_label_format
attr_accessor :popup_format attr_accessor :popup_format
# Add data to the plot. # Add data to the plot.
# #
# d1 = [ "12:30", 2 ] # A data set with 1 point: ("12:30",2) # d1 = [ "12:30", 2 ] # A data set with 1 point: ("12:30",2)
# d2 = [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and # d2 = [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and
# # ("14:20",6) # # ("14:20",6)
# graph.add_data( # graph.add_data(
# :data => d1, # :data => d1,
# :title => 'One' # :title => 'One'
# ) # )
# graph.add_data( # graph.add_data(
# :data => d2, # :data => d2,
# :title => 'Two' # :title => 'Two'
# ) # )
# #
# Note that the data must be in time,value pairs, and that the date format # Note that the data must be in time,value pairs, and that the date format
# may be any date that is parseable by ParseDate. # may be any date that is parseable by ParseDate.
def add_data data def add_data data
@data = [] unless @data @data = [] unless @data
raise "No data provided by #{@data.inspect}" unless data[:data] and raise "No data provided by #{@data.inspect}" unless data[:data] and
data[:data].kind_of? Array data[:data].kind_of? Array
raise "Data supplied must be x,y pairs! "+ raise "Data supplied must be x,y pairs! "+
"The data provided contained an odd set of "+ "The data provided contained an odd set of "+
"data points" unless data[:data].length % 2 == 0 "data points" unless data[:data].length % 2 == 0
return if data[:data].length == 0 return if data[:data].length == 0
x = [] x = []
y = [] y = []
data[:data].each_index {|i| data[:data].each_index {|i|
if i%2 == 0 if i%2 == 0
t = DateTime.parse( data[:data][i] ).to_time t = DateTime.parse( data[:data][i] ).to_time
x << t.to_i x << t.to_i
else else
y << data[:data][i] y << data[:data][i]
end end
} }
sort( x, y ) sort( x, y )
data[:data] = [x,y] data[:data] = [x,y]
@data << data @data << data
end end
protected protected
def min_x_value=(value) def min_x_value=(value)
@min_x_value = DateTime.parse( value ).to_time @min_x_value = DateTime.parse( value ).to_time
end end
def format x, y def format x, y
Time.at( x ).strftime( popup_format ) Time.at( x ).strftime( popup_format )
end end
def get_x_labels def get_x_labels
get_x_values.collect { |v| Time.at(v).strftime( x_label_format ) } get_x_values.collect { |v| Time.at(v).strftime( x_label_format ) }
end end
private private
def get_x_values def get_x_values
rv = [] rv = []
min, max, scale_division = x_range min, max, scale_division = x_range
if timescale_divisions if timescale_divisions
timescale_divisions =~ /(\d+) ?(day|week|month|year|hour|minute|second)?/ timescale_divisions =~ /(\d+) ?(day|week|month|year|hour|minute|second)?/
division_units = $2 ? $2 : "day" division_units = $2 ? $2 : "day"
amount = $1.to_i amount = $1.to_i
if amount if amount
step = nil step = nil
case division_units case division_units
when "month" when "month"
cur = min cur = min
while cur < max while cur < max
rv << cur rv << cur
arr = Time.at( cur ).to_a arr = Time.at( cur ).to_a
arr[4] += amount arr[4] += amount
if arr[4] > 12 if arr[4] > 12
arr[5] += (arr[4] / 12).to_i arr[5] += (arr[4] / 12).to_i
arr[4] = (arr[4] % 12) arr[4] = (arr[4] % 12)
end end
cur = Time.local(*arr).to_i cur = Time.local(*arr).to_i
end end
when "year" when "year"
cur = min cur = min
while cur < max while cur < max
rv << cur rv << cur
arr = Time.at( cur ).to_a arr = Time.at( cur ).to_a
arr[5] += amount arr[5] += amount
cur = Time.local(*arr).to_i cur = Time.local(*arr).to_i
end end
when "week" when "week"
step = 7 * 24 * 60 * 60 * amount step = 7 * 24 * 60 * 60 * amount
when "day" when "day"
step = 24 * 60 * 60 * amount step = 24 * 60 * 60 * amount
when "hour" when "hour"
step = 60 * 60 * amount step = 60 * 60 * amount
when "minute" when "minute"
step = 60 * amount step = 60 * amount
when "second" when "second"
step = amount step = amount
end end
min.step( max, step ) {|v| rv << v} if step min.step( max, step ) {|v| rv << v} if step
return rv return rv
end end
end end
min.step( max, scale_division ) {|v| rv << v} min.step( max, scale_division ) {|v| rv << v}
return rv return rv
end end
end end
end end
end end
-4
View File
@@ -4002,10 +4002,6 @@ class TCPDF
@quote_page[@quote_count] = @page; @quote_page[@quote_count] = @page;
@quote_count += 1 @quote_count += 1
when 'br' when 'br'
if @tdbegin
@tdtext << "\n"
return
end
Ln(); Ln();
if (@li_spacer.length > 0) if (@li_spacer.length > 0)
+2 -8
View File
@@ -109,13 +109,6 @@ module Redmine
RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding)) RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding))
end end
def formatted_text(text)
html = Redmine::WikiFormatting.to_html(Setting.text_formatting, text)
# Strip {{toc}} tags
html.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i, '')
html
end
def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='') def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='')
Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link) Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link)
end end
@@ -127,7 +120,8 @@ module Redmine
def RDMwriteHTMLCell(w, h, x, y, txt='', attachments=[], border=0, ln=1, fill=0) def RDMwriteHTMLCell(w, h, x, y, txt='', attachments=[], border=0, ln=1, fill=0)
@attachments = attachments @attachments = attachments
writeHTMLCell(w, h, x, y, writeHTMLCell(w, h, x, y,
fix_text_encoding(formatted_text(txt)), fix_text_encoding(
Redmine::WikiFormatting.to_html(Setting.text_formatting, txt)),
border, ln, fill) border, ln, fill)
end end
+1 -1
View File
@@ -4,7 +4,7 @@ module Redmine
module VERSION #:nodoc: module VERSION #:nodoc:
MAJOR = 2 MAJOR = 2
MINOR = 2 MINOR = 2
TINY = 3 TINY = 1
# Branch values: # Branch values:
# * official release: nil # * official release: nil
@@ -69,7 +69,7 @@ module Redmine
l = 1 l = 1
started = false started = false
ended = false ended = false
text.scan(/(((?:.*?)(\A|\r?\n\s*\r?\n))(h(\d+)(#{A}#{C})\.(?::(\S+))?[ \t](.*?)$)|.*)/m).each do |all, content, lf, heading, level| text.scan(/(((?:.*?)(\A|\r?\n\s*\r?\n))(h(\d+)(#{A}#{C})\.(?::(\S+))? (.*?)$)|.*)/m).each do |all, content, lf, heading, level|
if heading.nil? if heading.nil?
if ended if ended
after << all after << all
-14
View File
@@ -99,18 +99,4 @@ wiki_content_versions_006:
version: 3 version: 3
author_id: 1 author_id: 1
comments: comments:
wiki_content_versions_007:
data: |-
h1. Page with an inline image
This is an inline image:
!logo.gif!
updated_on: 2007-03-08 00:18:07 +01:00
page_id: 4
wiki_content_id: 4
id: 7
version: 1
author_id: 1
comments:
@@ -132,7 +132,7 @@ class ContextMenusControllerTest < ActionController::TestCase
:attributes => {:href => "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=Foo"} :attributes => {:href => "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=Foo"}
assert_tag 'a', assert_tag 'a',
:content => 'none', :content => 'none',
:attributes => {:href => "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__"} :attributes => {:href => "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D="}
end end
def test_context_menu_should_not_include_null_value_for_required_custom_fields def test_context_menu_should_not_include_null_value_for_required_custom_fields
-14
View File
@@ -2914,20 +2914,6 @@ class IssuesControllerTest < ActionController::TestCase
assert_equal spent_hours_before + 2.5, issue.spent_hours assert_equal spent_hours_before + 2.5, issue.spent_hours
end end
def test_put_update_should_preserve_parent_issue_even_if_not_visible
parent = Issue.generate!(:project_id => 1, :is_private => true)
issue = Issue.generate!(:parent_issue_id => parent.id)
assert !parent.visible?(User.find(3))
@request.session[:user_id] = 3
get :edit, :id => issue.id
assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', parent.id.to_s
put :update, :id => issue.id, :issue => {:subject => 'New subject', :parent_issue_id => parent.id.to_s}
assert_response 302
assert_equal parent, issue.parent
end
def test_put_update_with_attachment_only def test_put_update_with_attachment_only
set_tmp_attachments_directory set_tmp_attachments_directory
-5
View File
@@ -187,11 +187,6 @@ class MyControllerTest < ActionController::TestCase
assert User.find(2).pref[:my_page_layout]['top'].include?('issuesreportedbyme') assert User.find(2).pref[:my_page_layout]['top'].include?('issuesreportedbyme')
end end
def test_add_invalid_block_should_redirect
post :add_block, :block => 'invalid'
assert_redirected_to '/my/page_layout'
end
def test_remove_block def test_remove_block
post :remove_block, :block => 'issuesassignedtome' post :remove_block, :block => 'issuesassignedtome'
assert_redirected_to '/my/page_layout' assert_redirected_to '/my/page_layout'
-13
View File
@@ -75,19 +75,6 @@ class WikiControllerTest < ActionController::TestCase
assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation', :text => /Current version/ assert_select 'a[href=?]', '/projects/ecookbook/wiki/CookBook_documentation', :text => /Current version/
end end
def test_show_old_version_with_attachments
page = WikiPage.find(4)
assert page.attachments.any?
content = page.content
content.text = "update"
content.save!
get :show, :project_id => 'ecookbook', :id => page.title, :version => '1'
assert_kind_of WikiContent::Version, assigns(:content)
assert_response :success
assert_template 'show'
end
def test_show_old_version_without_permission_should_be_denied def test_show_old_version_without_permission_should_be_denied
Role.anonymous.remove_permission! :view_wiki_edits Role.anonymous.remove_permission! :view_wiki_edits
-12
View File
@@ -118,16 +118,4 @@ module ObjectHelpers
board.save! board.save!
board board
end end
def Attachment.generate!(attributes={})
@generated_filename ||= 'testfile0'
@generated_filename.succ!
attributes = attributes.dup
attachment = Attachment.new(attributes)
attachment.container ||= Issue.find(1)
attachment.author ||= User.find(2)
attachment.filename = @generated_filename if attachment.filename.blank?
attachment.save!
attachment
end
end end
+3 -21
View File
@@ -347,15 +347,6 @@ RAW
to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" } to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
end end
def test_redmine_links_with_a_different_project_before_current_project
vp1 = Version.generate!(:project_id => 1, :name => '1.4.4')
vp3 = Version.generate!(:project_id => 3, :name => '1.4.4')
@project = Project.find(3)
assert_equal %(<p><a href="/versions/#{vp1.id}" class="version">1.4.4</a> <a href="/versions/#{vp3.id}" class="version">1.4.4</a></p>),
textilizable("ecookbook:version:1.4.4 version:1.4.4")
end
def test_escaped_redmine_links_should_not_be_parsed def test_escaped_redmine_links_should_not_be_parsed
to_test = [ to_test = [
'#3.', '#3.',
@@ -403,14 +394,14 @@ RAW
end end
def test_multiple_repositories_redmine_links def test_multiple_repositories_redmine_links
svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn_repo-1', :url => 'file:///foo/hg') svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg')
Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123') Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg') hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd') Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
changeset_link = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2}, changeset_link = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
:class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3') :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
svn_changeset_link = link_to('svn_repo-1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn_repo-1', :rev => 123}, svn_changeset_link = link_to('svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123},
:class => 'changeset', :title => '') :class => 'changeset', :title => '')
hg_changeset_link = link_to('hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'}, hg_changeset_link = link_to('hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
:class => 'changeset', :title => '') :class => 'changeset', :title => '')
@@ -420,7 +411,7 @@ RAW
to_test = { to_test = {
'r2' => changeset_link, 'r2' => changeset_link,
'svn_repo-1|r123' => svn_changeset_link, 'svn1|r123' => svn_changeset_link,
'invalid|r123' => 'invalid|r123', 'invalid|r123' => 'invalid|r123',
'commit:hg1|abcd' => hg_changeset_link, 'commit:hg1|abcd' => hg_changeset_link,
'commit:invalid|abcd' => 'commit:invalid|abcd', 'commit:invalid|abcd' => 'commit:invalid|abcd',
@@ -560,15 +551,6 @@ RAW
to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => Issue.find(3).attachments), "#{text} failed" } to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => Issue.find(3).attachments), "#{text} failed" }
end end
def test_attachment_link_should_link_to_latest_attachment
set_tmp_attachments_directory
a1 = Attachment.generate!(:filename => "test.txt", :created_on => 1.hour.ago)
a2 = Attachment.generate!(:filename => "test.txt")
assert_equal %(<p><a href="/attachments/download/#{a2.id}" class="attachment">test.txt</a></p>),
textilizable('attachment:test.txt', :attachments => [a1, a2])
end
def test_wiki_links def test_wiki_links
to_test = { to_test = {
'[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>', '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
@@ -419,20 +419,6 @@ STR
end end
end end
def test_get_section_should_support_headings_starting_with_a_tab
text = <<-STR
h1.\tHeading 1
Content 1
h1. Heading 2
Content 2
STR
assert_match /\Ah1.\tHeading 1\s+Content 1\z/, @formatter.new(text).get_section(1).first
end
private private
def assert_html_output(to_test, expect_paragraph = true) def assert_html_output(to_test, expect_paragraph = true)