Compare commits
25
Commits
1.2.2
...
1.2-stable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e6d1b6c35 | ||
|
|
5c4b618bce | ||
|
|
2f87b3bae3 | ||
|
|
1848fcd91e | ||
|
|
40a14cafbc | ||
|
|
f252108f30 | ||
|
|
6d57380712 | ||
|
|
00d1b230e0 | ||
|
|
d19a0b70e0 | ||
|
|
535b5ccf2b | ||
|
|
a441667b8d | ||
|
|
d4246334e0 | ||
|
|
c8c48a7816 | ||
|
|
fa894328bb | ||
|
|
63e2e52dd3 | ||
|
|
ff75a959e0 | ||
|
|
5d1388db02 | ||
|
|
3e4d1fb04f | ||
|
|
2858979b69 | ||
|
|
be09ba039b | ||
|
|
a5adecb40c | ||
|
|
f328373ffd | ||
|
|
367ad46911 | ||
|
|
909cb9ab78 | ||
|
|
8c2869f521 |
@@ -48,9 +48,8 @@ class ContextMenusController < ApplicationController
|
|||||||
@projects = @time_entries.collect(&:project).compact.uniq
|
@projects = @time_entries.collect(&:project).compact.uniq
|
||||||
@project = @projects.first if @projects.size == 1
|
@project = @projects.first if @projects.size == 1
|
||||||
@activities = TimeEntryActivity.shared.active
|
@activities = TimeEntryActivity.shared.active
|
||||||
@can = {:edit => User.current.allowed_to?(:log_time, @projects),
|
@can = {:edit => User.current.allowed_to?(:edit_time_entries, @projects),
|
||||||
:update => User.current.allowed_to?(:log_time, @projects),
|
:delete => User.current.allowed_to?(:edit_time_entries, @projects)
|
||||||
:delete => User.current.allowed_to?(:log_time, @projects)
|
|
||||||
}
|
}
|
||||||
@back = back_url
|
@back = back_url
|
||||||
render :layout => false
|
render :layout => false
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ class SysController < ActionController::Base
|
|||||||
|
|
||||||
def projects
|
def projects
|
||||||
p = Project.active.has_module(:repository).find(:all, :include => :repository, :order => 'identifier')
|
p = Project.active.has_module(:repository).find(:all, :include => :repository, :order => 'identifier')
|
||||||
render :xml => p.to_xml(:include => :repository)
|
# extra_info attribute from repository breaks activeresource client
|
||||||
|
render :xml => p.to_xml(:only => [:id, :identifier, :name, :is_public, :status], :include => {:repository => {:only => [:id, :url]}})
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_project_repository
|
def create_project_repository
|
||||||
@@ -31,7 +32,7 @@ class SysController < ActionController::Base
|
|||||||
logger.info "Repository for #{project.name} was reported to be created by #{request.remote_ip}."
|
logger.info "Repository for #{project.name} was reported to be created by #{request.remote_ip}."
|
||||||
project.repository = Repository.factory(params[:vendor], params[:repository])
|
project.repository = Repository.factory(params[:vendor], params[:repository])
|
||||||
if project.repository && project.repository.save
|
if project.repository && project.repository.save
|
||||||
render :xml => project.repository, :status => 201
|
render :xml => project.repository.to_xml(:only => [:id, :url]), :status => 201
|
||||||
else
|
else
|
||||||
render :nothing => true, :status => 422
|
render :nothing => true, :status => 422
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ protected
|
|||||||
when 'float'
|
when 'float'
|
||||||
begin; Kernel.Float(value); rescue; errors.add(:value, :invalid) end
|
begin; Kernel.Float(value); rescue; errors.add(:value, :invalid) end
|
||||||
when 'date'
|
when 'date'
|
||||||
errors.add(:value, :not_a_date) unless value =~ /^\d{4}-\d{2}-\d{2}$/
|
errors.add(:value, :not_a_date) unless value =~ /^\d{4}-\d{2}-\d{2}$/ && begin; value.to_date; rescue; false end
|
||||||
when 'list'
|
when 'list'
|
||||||
errors.add(:value, :inclusion) unless custom_field.possible_values.include?(value)
|
errors.add(:value, :inclusion) unless custom_field.possible_values.include?(value)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -809,6 +809,7 @@ class Project < ActiveRecord::Base
|
|||||||
new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria")
|
new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria")
|
||||||
new_query.sort_criteria = query.sort_criteria if query.sort_criteria
|
new_query.sort_criteria = query.sort_criteria if query.sort_criteria
|
||||||
new_query.project = self
|
new_query.project = self
|
||||||
|
new_query.user_id = query.user_id
|
||||||
self.queries << new_query
|
self.queries << new_query
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ class Version < ActiveRecord::Base
|
|||||||
user.allowed_to?(:view_issues, self.project)
|
user.allowed_to?(:view_issues, self.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Version files have same visibility as project files
|
||||||
|
def attachments_visible?(*args)
|
||||||
|
project.present? && project.attachments_visible?(*args)
|
||||||
|
end
|
||||||
|
|
||||||
def start_date
|
def start_date
|
||||||
@start_date ||= fixed_issues.minimum('start_date')
|
@start_date ||= fixed_issues.minimum('start_date')
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,10 +15,10 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<% @activities.each do |u| -%>
|
<% @activities.each do |u| -%>
|
||||||
<li><%= context_menu_link u.name, {:controller => 'timelog', :action => 'bulk_edit', :ids => @time_entries.collect(&:id), :time_entry => {'activity_id' => u}, :back_url => @back}, :method => :post,
|
<li><%= context_menu_link u.name, {:controller => 'timelog', :action => 'bulk_edit', :ids => @time_entries.collect(&:id), :time_entry => {'activity_id' => u}, :back_url => @back}, :method => :post,
|
||||||
:selected => (@time_entry && u == @time_entry.activity), :disabled => !@can[:update] %></li>
|
:selected => (@time_entry && u == @time_entry.activity), :disabled => !@can[:edit] %></li>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
<li><%= context_menu_link l(:label_nobody), {:controller => 'timelog', :action => 'bulk_edit', :ids => @time_entries.collect(&:id), :time_entry => {'activity_id' => 'none'}, :back_url => @back}, :method => :post,
|
<li><%= context_menu_link l(:label_nobody), {:controller => 'timelog', :action => 'bulk_edit', :ids => @time_entries.collect(&:id), :time_entry => {'activity_id' => 'none'}, :back_url => @back}, :method => :post,
|
||||||
:selected => (@time_entry && @time_entry.activity.nil?), :disabled => !@can[:update] %></li>
|
:selected => (@time_entry && @time_entry.activity.nil?), :disabled => !@can[:edit] %></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
<p><em><%= format_time(document.updated_on) %></em></p>
|
<p><em><%= format_time(document.updated_on) %></em></p>
|
||||||
|
|
||||||
<div class="wiki">
|
<div class="wiki">
|
||||||
<%= textilizable(truncate_lines(document.description)) %>
|
<%= textilizable(truncate_lines(document.description), :object => document) %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<%= render :partial => (@edit_allowed ? 'form' : 'form_update'), :locals => {:f => f} %>
|
<%= render :partial => (@edit_allowed ? 'form' : 'form_update'), :locals => {:f => f} %>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if authorize_for('timelog', 'edit') %>
|
<% if User.current.allowed_to?(:log_time, @project) %>
|
||||||
<fieldset class="tabular"><legend><%= l(:button_log_time) %></legend>
|
<fieldset class="tabular"><legend><%= l(:button_log_time) %></legend>
|
||||||
<% fields_for :time_entry, @time_entry, { :builder => TabularFormBuilder, :lang => current_language} do |time_entry| %>
|
<% fields_for :time_entry, @time_entry, { :builder => TabularFormBuilder, :lang => current_language} do |time_entry| %>
|
||||||
<div class="splitcontentleft">
|
<div class="splitcontentleft">
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% unless @hours.empty? %>
|
<% unless @hours.empty? %>
|
||||||
|
<div class="autoscroll">
|
||||||
<table class="list" id="time-report">
|
<table class="list" id="time-report">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -59,6 +60,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<% other_formats_links do |f| %>
|
<% other_formats_links do |f| %>
|
||||||
<%= f.link_to 'CSV', :url => params %>
|
<%= f.link_to 'CSV', :url => params %>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<% form_tag({}) do -%>
|
<% form_tag({}) do -%>
|
||||||
<%= hidden_field_tag 'back_url', url_for(params) %>
|
<%= hidden_field_tag 'back_url', url_for(params) %>
|
||||||
|
<div class="autoscroll">
|
||||||
<table class="list time-entries">
|
<table class="list time-entries">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -48,6 +49,7 @@
|
|||||||
<% end -%>
|
<% end -%>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
|
|
||||||
<%= context_menu time_entries_context_menu_path %>
|
<%= context_menu time_entries_context_menu_path %>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<h2><%= @page.pretty_title %></h2>
|
<h2><%= @page.pretty_title %></h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<%= l(:label_version) %> <%= link_to @annotate.content.version, :action => 'show', :id => @page.title, :version => @annotate.content.version %>
|
<%= l(:label_version) %> <%= link_to @annotate.content.version, :action => 'show', :project_id => @project, :id => @page.title, :version => @annotate.content.version %>
|
||||||
<em>(<%= @annotate.content.author ? @annotate.content.author.name : "anonyme" %>, <%= format_time(@annotate.content.updated_on) %>)</em>
|
<em>(<%= @annotate.content.author ? @annotate.content.author.name : "anonyme" %>, <%= format_time(@annotate.content.updated_on) %>)</em>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,8 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% other_formats_links do |f| %>
|
<% other_formats_links do |f| %>
|
||||||
<%= f.link_to 'HTML', :url => {:id => @page.title, :version => @content.version} %>
|
<%= f.link_to 'HTML', :url => {:id => @page.title, :version => params[:version]} %>
|
||||||
<%= f.link_to 'TXT', :url => {:id => @page.title, :version => @content.version} %>
|
<%= f.link_to 'TXT', :url => {:id => @page.title, :version => params[:version]} %>
|
||||||
<% end if User.current.allowed_to?(:export_wiki_pages, @project) %>
|
<% end if User.current.allowed_to?(:export_wiki_pages, @project) %>
|
||||||
|
|
||||||
<% content_for :header_tags do %>
|
<% content_for :header_tags do %>
|
||||||
|
|||||||
@@ -368,7 +368,7 @@ bg:
|
|||||||
setting_issue_done_ratio_issue_status: Използване на състоянието на задачите
|
setting_issue_done_ratio_issue_status: Използване на състоянието на задачите
|
||||||
setting_start_of_week: Първи ден на седмицата
|
setting_start_of_week: Първи ден на седмицата
|
||||||
setting_rest_api_enabled: Разрешаване на REST web сървис
|
setting_rest_api_enabled: Разрешаване на REST web сървис
|
||||||
setting_cache_formatted_text: Cache formatted text
|
setting_cache_formatted_text: Кещиране на форматираните текстове
|
||||||
setting_default_notification_option: Подразбиращ се начин за известяване
|
setting_default_notification_option: Подразбиращ се начин за известяване
|
||||||
setting_commit_logtime_enabled: Разрешаване на отчитането на работното време
|
setting_commit_logtime_enabled: Разрешаване на отчитането на работното време
|
||||||
setting_commit_logtime_activity_id: Дейност при отчитане на работното време
|
setting_commit_logtime_activity_id: Дейност при отчитане на работното време
|
||||||
|
|||||||
+42
-41
@@ -91,7 +91,6 @@ zh:
|
|||||||
gb: "GB"
|
gb: "GB"
|
||||||
tb: "TB"
|
tb: "TB"
|
||||||
|
|
||||||
|
|
||||||
# Used in array.to_sentence.
|
# Used in array.to_sentence.
|
||||||
support:
|
support:
|
||||||
array:
|
array:
|
||||||
@@ -241,7 +240,7 @@ zh:
|
|||||||
field_is_default: 默认值
|
field_is_default: 默认值
|
||||||
field_tracker: 跟踪
|
field_tracker: 跟踪
|
||||||
field_subject: 主题
|
field_subject: 主题
|
||||||
field_due_date: 完成日期
|
field_due_date: 计划完成日期
|
||||||
field_assigned_to: 指派给
|
field_assigned_to: 指派给
|
||||||
field_priority: 优先级
|
field_priority: 优先级
|
||||||
field_fixed_version: 目标版本
|
field_fixed_version: 目标版本
|
||||||
@@ -273,7 +272,7 @@ zh:
|
|||||||
field_attr_mail: 邮件属性
|
field_attr_mail: 邮件属性
|
||||||
field_onthefly: 即时用户生成
|
field_onthefly: 即时用户生成
|
||||||
field_start_date: 开始日期
|
field_start_date: 开始日期
|
||||||
field_done_ratio: % 完成
|
field_done_ratio: "% 完成"
|
||||||
field_auth_source: 认证模式
|
field_auth_source: 认证模式
|
||||||
field_hide_mail: 隐藏我的邮件地址
|
field_hide_mail: 隐藏我的邮件地址
|
||||||
field_comments: 注释
|
field_comments: 注释
|
||||||
@@ -402,7 +401,7 @@ zh:
|
|||||||
permission_manage_files: 管理文件
|
permission_manage_files: 管理文件
|
||||||
permission_view_files: 查看文件
|
permission_view_files: 查看文件
|
||||||
permission_manage_wiki: 管理Wiki
|
permission_manage_wiki: 管理Wiki
|
||||||
permission_rename_wiki_pages: 重命名Wiki页面
|
permission_rename_wiki_pages: 重定向/重命名Wiki页面
|
||||||
permission_delete_wiki_pages: 删除Wiki页面
|
permission_delete_wiki_pages: 删除Wiki页面
|
||||||
permission_view_wiki_pages: 查看Wiki
|
permission_view_wiki_pages: 查看Wiki
|
||||||
permission_view_wiki_edits: 查看Wiki历史记录
|
permission_view_wiki_edits: 查看Wiki历史记录
|
||||||
@@ -832,7 +831,7 @@ zh:
|
|||||||
button_archive: 存档
|
button_archive: 存档
|
||||||
button_unarchive: 取消存档
|
button_unarchive: 取消存档
|
||||||
button_reset: 重置
|
button_reset: 重置
|
||||||
button_rename: 重命名
|
button_rename: 重命名/重定向
|
||||||
button_change_password: 修改密码
|
button_change_password: 修改密码
|
||||||
button_copy: 复制
|
button_copy: 复制
|
||||||
button_copy_and_follow: 复制并转到新问题
|
button_copy_and_follow: 复制并转到新问题
|
||||||
@@ -910,8 +909,8 @@ zh:
|
|||||||
text_wiki_page_destroy_children: 删除子页面及其所有下级页面
|
text_wiki_page_destroy_children: 删除子页面及其所有下级页面
|
||||||
text_wiki_page_reassign_children: 将子页面的上级页面设置为
|
text_wiki_page_reassign_children: 将子页面的上级页面设置为
|
||||||
text_own_membership_delete_confirmation: 你正在删除你现有的某些或全部权限,如果这样做了你可能将会再也无法编辑该项目了。你确定要继续吗?
|
text_own_membership_delete_confirmation: 你正在删除你现有的某些或全部权限,如果这样做了你可能将会再也无法编辑该项目了。你确定要继续吗?
|
||||||
text_zoom_in: Zoom in
|
text_zoom_in: 放大
|
||||||
text_zoom_out: Zoom out
|
text_zoom_out: 缩小
|
||||||
|
|
||||||
default_role_manager: 管理人员
|
default_role_manager: 管理人员
|
||||||
default_role_developer: 开发人员
|
default_role_developer: 开发人员
|
||||||
@@ -940,42 +939,44 @@ zh:
|
|||||||
enumeration_activities: 活动(时间跟踪)
|
enumeration_activities: 活动(时间跟踪)
|
||||||
enumeration_system_activity: 系统活动
|
enumeration_system_activity: 系统活动
|
||||||
|
|
||||||
field_warn_on_leaving_unsaved: Warn me when leaving a page with unsaved text
|
field_warn_on_leaving_unsaved: 当离开未保存内容的页面时,提示我
|
||||||
text_warn_on_leaving_unsaved: The current page contains unsaved text that will be lost if you leave this page.
|
text_warn_on_leaving_unsaved: 若离开当前页面,则该页面内未保存的内容将丢失。
|
||||||
label_my_queries: My custom queries
|
label_my_queries: 我的自定义查询
|
||||||
text_journal_changed_no_detail: "%{label} updated"
|
text_journal_changed_no_detail: "%{label} 已更新。"
|
||||||
label_news_comment_added: Comment added to a news
|
label_news_comment_added: 添加到新闻的评论
|
||||||
button_expand_all: Expand all
|
button_expand_all: 展开所有
|
||||||
button_collapse_all: Collapse all
|
button_collapse_all: 合拢所有
|
||||||
label_additional_workflow_transitions_for_assignee: Additional transitions allowed when the user is the assignee
|
label_additional_workflow_transitions_for_assignee: 当用户是问题的分配对象时所允许的问题状态转换
|
||||||
label_additional_workflow_transitions_for_author: Additional transitions allowed when the user is the author
|
label_additional_workflow_transitions_for_author: 当用户是问题作者时所允许的问题状态转换
|
||||||
label_bulk_edit_selected_time_entries: Bulk edit selected time entries
|
label_bulk_edit_selected_time_entries: 批量修改选定的时间条目
|
||||||
text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies)?
|
text_time_entries_destroy_confirmation: 是否确定要删除选定的时间条目?
|
||||||
label_role_anonymous: Anonymous
|
label_role_anonymous: Anonymous
|
||||||
label_role_non_member: Non member
|
label_role_non_member: Non member
|
||||||
label_issue_note_added: Note added
|
label_issue_note_added: 问题备注已添加
|
||||||
label_issue_status_updated: Status updated
|
label_issue_status_updated: 问题状态更新
|
||||||
label_issue_priority_updated: Priority updated
|
label_issue_priority_updated: 问题优先级更新
|
||||||
label_issues_visibility_own: Issues created by or assigned to the user
|
label_issues_visibility_own: 创建或分配给用户的问题
|
||||||
field_issues_visibility: Issues visibility
|
field_issues_visibility: 问题可见
|
||||||
label_issues_visibility_all: All issues
|
label_issues_visibility_all: 全部问题
|
||||||
permission_set_own_issues_private: Set own issues public or private
|
permission_set_own_issues_private: 设置自己的问题为公开或私有
|
||||||
field_is_private: Private
|
field_is_private: 私有
|
||||||
permission_set_issues_private: Set issues public or private
|
permission_set_issues_private: 设置问题为公开或私有
|
||||||
label_issues_visibility_public: All non private issues
|
label_issues_visibility_public: 全部非私有问题
|
||||||
text_issues_destroy_descendants_confirmation: This will also delete %{count} subtask(s).
|
text_issues_destroy_descendants_confirmation: 此操作同时会删除 %{count} 个子任务。
|
||||||
|
|
||||||
field_commit_logs_encoding: 提交注释的编码
|
field_commit_logs_encoding: 提交注释的编码
|
||||||
field_scm_path_encoding: Path encoding
|
field_scm_path_encoding: 路径编码
|
||||||
text_scm_path_encoding_note: "Default: UTF-8"
|
text_scm_path_encoding_note: "默认: UTF-8"
|
||||||
field_path_to_repository: Path to repository
|
field_path_to_repository: 库路径
|
||||||
field_root_directory: Root directory
|
field_root_directory: 根目录
|
||||||
field_cvs_module: Module
|
field_cvs_module: CVS Module
|
||||||
field_cvsroot: CVSROOT
|
field_cvsroot: CVSROOT
|
||||||
text_git_repository_note: Bare and local repository (e.g. /gitrepo, c:\gitrepo)
|
text_mercurial_repository_note: 本地库 (e.g. /hgrepo, c:\hgrepo)
|
||||||
text_mercurial_repository_note: Local repository (e.g. /hgrepo, c:\hgrepo)
|
text_scm_command: 命令
|
||||||
text_scm_command: Command
|
text_scm_command_version: 版本
|
||||||
text_scm_command_version: Version
|
label_git_report_last_commit: 报告最后一次文件/目录提交
|
||||||
label_git_report_last_commit: Report last commit for files and directories
|
text_scm_config: 您可以在config/configuration.yml中配置您的SCM命令。 请在编辑后,重启Redmine应用。
|
||||||
text_scm_config: You can configure your scm commands in config/configuration.yml. Please restart the application after editing it.
|
text_scm_command_not_available: Scm命令不可用。 请检查管理面板的配置。
|
||||||
text_scm_command_not_available: Scm command is not available. Please check settings on the administration panel.
|
|
||||||
|
text_git_repository_note: 库中无内容。(e.g. /gitrepo, c:\gitrepo)
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,27 @@ Redmine - project management software
|
|||||||
Copyright (C) 2006-2011 Jean-Philippe Lang
|
Copyright (C) 2006-2011 Jean-Philippe Lang
|
||||||
http://www.redmine.org/
|
http://www.redmine.org/
|
||||||
|
|
||||||
|
== 2011-12-10 v1.2.3
|
||||||
|
|
||||||
|
* Defect #8707: Reposman: wrong constant name
|
||||||
|
* Defect #8809: Table in timelog report overflows
|
||||||
|
* Defect #9055: Version files in Files module cannot be downloaded if issue tracking is disabled
|
||||||
|
* Defect #9137: db:encrypt fails to handle repositories with blank password
|
||||||
|
* Defect #9394: Custom date field only validating on regex and not a valid date
|
||||||
|
* Defect #9405: Any user with :log_time permission can edit time entries via context menu
|
||||||
|
* Defect #9448: The attached images are not shown in documents
|
||||||
|
* Defect #9520: Copied private query not visible after project copy
|
||||||
|
* Defect #9552: Error when reading ciphered text from the database without cipher key configured
|
||||||
|
* Defect #9566: Redmine.pm considers all projects private when login_required is enabled
|
||||||
|
* Defect #9567: Redmine.pm potential security issue with cache credential enabled and subversion
|
||||||
|
* Defect #9577: Deleting a subtasks doesn't update parent's rgt & lft values
|
||||||
|
* Defect #9597: Broken version links in wiki annotate history
|
||||||
|
* Defect #9682: Wiki HTML Export only useful when Access history is accessible
|
||||||
|
* Defect #9737: Custom values deleted before issue submit
|
||||||
|
* Defect #9741: calendar-hr.js (Croatian) is not UTF-8
|
||||||
|
* Patch #9558: Simplified Chinese translation for 1.2.2 updated
|
||||||
|
* Patch #9695: Bulgarian translation (r7942)
|
||||||
|
|
||||||
== 2011-11-11 v1.2.2
|
== 2011-11-11 v1.2.2
|
||||||
|
|
||||||
* Defect #3276: Incorrect handling of anchors in Wiki to HTML export
|
* Defect #3276: Incorrect handling of anchors in Wiki to HTML export
|
||||||
|
|||||||
+15
-12
@@ -149,15 +149,16 @@ sub RedmineDSN {
|
|||||||
$self->{RedmineDSN} = $arg;
|
$self->{RedmineDSN} = $arg;
|
||||||
my $query = "SELECT
|
my $query = "SELECT
|
||||||
hashed_password, salt, auth_source_id, permissions
|
hashed_password, salt, auth_source_id, permissions
|
||||||
FROM members, projects, users, roles, member_roles
|
FROM projects, users, roles
|
||||||
WHERE
|
WHERE
|
||||||
projects.id=members.project_id
|
users.login=?
|
||||||
AND member_roles.member_id=members.id
|
AND projects.identifier=?
|
||||||
AND users.id=members.user_id
|
|
||||||
AND roles.id=member_roles.role_id
|
|
||||||
AND users.status=1
|
AND users.status=1
|
||||||
AND login=?
|
AND (
|
||||||
AND identifier=? ";
|
roles.id IN (SELECT member_roles.role_id FROM members, member_roles WHERE members.user_id = users.id AND members.project_id = projects.id AND members.id = member_roles.member_id)
|
||||||
|
OR
|
||||||
|
(roles.builtin=1 AND cast(projects.is_public as CHAR) IN ('t', '1'))
|
||||||
|
) ";
|
||||||
$self->{RedmineQuery} = trim($query);
|
$self->{RedmineQuery} = trim($query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,10 +329,12 @@ sub is_member {
|
|||||||
|
|
||||||
my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
|
my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
|
||||||
|
|
||||||
|
my $access_mode = defined $read_only_methods{$r->method} ? "R" : "W";
|
||||||
|
|
||||||
my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
|
my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
|
||||||
my $usrprojpass;
|
my $usrprojpass;
|
||||||
if ($cfg->{RedmineCacheCredsMax}) {
|
if ($cfg->{RedmineCacheCredsMax}) {
|
||||||
$usrprojpass = $cfg->{RedmineCacheCreds}->get($redmine_user.":".$project_id);
|
$usrprojpass = $cfg->{RedmineCacheCreds}->get($redmine_user.":".$project_id.":".$access_mode);
|
||||||
return 1 if (defined $usrprojpass and ($usrprojpass eq $pass_digest));
|
return 1 if (defined $usrprojpass and ($usrprojpass eq $pass_digest));
|
||||||
}
|
}
|
||||||
my $query = $cfg->{RedmineQuery};
|
my $query = $cfg->{RedmineQuery};
|
||||||
@@ -344,7 +347,7 @@ sub is_member {
|
|||||||
unless ($auth_source_id) {
|
unless ($auth_source_id) {
|
||||||
my $method = $r->method;
|
my $method = $r->method;
|
||||||
my $salted_password = Digest::SHA1::sha1_hex($salt.$pass_digest);
|
my $salted_password = Digest::SHA1::sha1_hex($salt.$pass_digest);
|
||||||
if ($hashed_password eq $salted_password && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
|
if ($hashed_password eq $salted_password && (($access_mode eq "R" && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
|
||||||
$ret = 1;
|
$ret = 1;
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
@@ -363,7 +366,7 @@ sub is_member {
|
|||||||
filter => "(".$rowldap[6]."=%s)"
|
filter => "(".$rowldap[6]."=%s)"
|
||||||
);
|
);
|
||||||
my $method = $r->method;
|
my $method = $r->method;
|
||||||
$ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
|
$ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && (($access_mode eq "R" && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
|
||||||
|
|
||||||
}
|
}
|
||||||
$sthldap->finish();
|
$sthldap->finish();
|
||||||
@@ -377,10 +380,10 @@ sub is_member {
|
|||||||
|
|
||||||
if ($cfg->{RedmineCacheCredsMax} and $ret) {
|
if ($cfg->{RedmineCacheCredsMax} and $ret) {
|
||||||
if (defined $usrprojpass) {
|
if (defined $usrprojpass) {
|
||||||
$cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id, $pass_digest);
|
$cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id.":".$access_mode, $pass_digest);
|
||||||
} else {
|
} else {
|
||||||
if ($cfg->{RedmineCacheCredsCount} < $cfg->{RedmineCacheCredsMax}) {
|
if ($cfg->{RedmineCacheCredsCount} < $cfg->{RedmineCacheCredsMax}) {
|
||||||
$cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id, $pass_digest);
|
$cfg->{RedmineCacheCreds}->set($redmine_user.":".$project_id.":".$access_mode, $pass_digest);
|
||||||
$cfg->{RedmineCacheCredsCount}++;
|
$cfg->{RedmineCacheCredsCount}++;
|
||||||
} else {
|
} else {
|
||||||
$cfg->{RedmineCacheCreds}->clear();
|
$cfg->{RedmineCacheCreds}->clear();
|
||||||
|
|||||||
+3
-3
@@ -88,10 +88,10 @@ Redmine::AccessControl.map do |map|
|
|||||||
end
|
end
|
||||||
|
|
||||||
map.project_module :time_tracking do |map|
|
map.project_module :time_tracking do |map|
|
||||||
map.permission :log_time, {:timelog => [:new, :create, :edit, :update, :bulk_edit, :bulk_update]}, :require => :loggedin
|
map.permission :log_time, {:timelog => [:new, :create]}, :require => :loggedin
|
||||||
map.permission :view_time_entries, :timelog => [:index, :show], :time_entry_reports => [:report]
|
map.permission :view_time_entries, :timelog => [:index, :show], :time_entry_reports => [:report]
|
||||||
map.permission :edit_time_entries, {:timelog => [:new, :create, :edit, :update, :destroy, :bulk_edit, :bulk_update]}, :require => :member
|
map.permission :edit_time_entries, {:timelog => [:edit, :update, :destroy, :bulk_edit, :bulk_update]}, :require => :member
|
||||||
map.permission :edit_own_time_entries, {:timelog => [:new, :create, :edit, :update, :destroy,:bulk_edit, :bulk_update]}, :require => :loggedin
|
map.permission :edit_own_time_entries, {:timelog => [:edit, :update, :destroy,:bulk_edit, :bulk_update]}, :require => :loggedin
|
||||||
map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member
|
map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ module Redmine
|
|||||||
|
|
||||||
class << self
|
class << self
|
||||||
def encrypt_text(text)
|
def encrypt_text(text)
|
||||||
if cipher_key.blank?
|
if cipher_key.blank? || text.blank?
|
||||||
text
|
text
|
||||||
else
|
else
|
||||||
c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
|
c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
|
||||||
@@ -39,6 +39,10 @@ module Redmine
|
|||||||
|
|
||||||
def decrypt_text(text)
|
def decrypt_text(text)
|
||||||
if text && match = text.match(/\Aaes-256-cbc:(.+)\Z/)
|
if text && match = text.match(/\Aaes-256-cbc:(.+)\Z/)
|
||||||
|
if cipher_key.blank?
|
||||||
|
logger.error "Attempt to decrypt a ciphered text with no cipher key configured in config/configuration.yml" if logger
|
||||||
|
return text
|
||||||
|
end
|
||||||
text = match[1]
|
text = match[1]
|
||||||
c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
|
c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
|
||||||
e, iv = text.split("--").map {|s| Base64.decode64(s)}
|
e, iv = text.split("--").map {|s| Base64.decode64(s)}
|
||||||
@@ -56,6 +60,10 @@ module Redmine
|
|||||||
key = Redmine::Configuration['database_cipher_key'].to_s
|
key = Redmine::Configuration['database_cipher_key'].to_s
|
||||||
key.blank? ? nil : Digest::SHA256.hexdigest(key)
|
key.blank? ? nil : Digest::SHA256.hexdigest(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def logger
|
||||||
|
RAILS_DEFAULT_LOGGER
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ module Redmine
|
|||||||
module VERSION #:nodoc:
|
module VERSION #:nodoc:
|
||||||
MAJOR = 1
|
MAJOR = 1
|
||||||
MINOR = 2
|
MINOR = 2
|
||||||
TINY = 2
|
TINY = 3
|
||||||
|
|
||||||
# Branch values:
|
# Branch values:
|
||||||
# * official release: nil
|
# * official release: nil
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ Calendar._FD = 0;
|
|||||||
Calendar._MN = new Array
|
Calendar._MN = new Array
|
||||||
("Sijecanj",
|
("Sijecanj",
|
||||||
"Veljaca",
|
"Veljaca",
|
||||||
"Ožujak",
|
"Ožujak",
|
||||||
"Travanj",
|
"Travanj",
|
||||||
"Svibanj",
|
"Svibanj",
|
||||||
"Lipanj",
|
"Lipanj",
|
||||||
@@ -66,7 +66,7 @@ Calendar._MN = new Array
|
|||||||
Calendar._SMN = new Array
|
Calendar._SMN = new Array
|
||||||
("Sij",
|
("Sij",
|
||||||
"Velj",
|
"Velj",
|
||||||
"Ožu",
|
"Ožu",
|
||||||
"Tra",
|
"Tra",
|
||||||
"Svi",
|
"Svi",
|
||||||
"Lip",
|
"Lip",
|
||||||
@@ -100,7 +100,7 @@ Calendar._TT["ABOUT_TIME"] = "\n\n" +
|
|||||||
|
|
||||||
Calendar._TT["PREV_YEAR"] = "Prethodna godina (hold for menu)";
|
Calendar._TT["PREV_YEAR"] = "Prethodna godina (hold for menu)";
|
||||||
Calendar._TT["PREV_MONTH"] = "Prethodni mjesec (hold for menu)";
|
Calendar._TT["PREV_MONTH"] = "Prethodni mjesec (hold for menu)";
|
||||||
Calendar._TT["GO_TODAY"] = "Na današnji dan";
|
Calendar._TT["GO_TODAY"] = "Na današnji dan";
|
||||||
Calendar._TT["NEXT_MONTH"] = "Naredni mjesec (hold for menu)";
|
Calendar._TT["NEXT_MONTH"] = "Naredni mjesec (hold for menu)";
|
||||||
Calendar._TT["NEXT_YEAR"] = "Naredna godina (hold for menu)";
|
Calendar._TT["NEXT_YEAR"] = "Naredna godina (hold for menu)";
|
||||||
Calendar._TT["SEL_DATE"] = "Odaberite datum";
|
Calendar._TT["SEL_DATE"] = "Odaberite datum";
|
||||||
@@ -109,7 +109,7 @@ Calendar._TT["PART_TODAY"] = " (Danas)";
|
|||||||
|
|
||||||
// the following is to inform that "%s" is to be the first day of week
|
// the following is to inform that "%s" is to be the first day of week
|
||||||
// %s will be replaced with the day name.
|
// %s will be replaced with the day name.
|
||||||
Calendar._TT["DAY_FIRST"] = "Prikaži %s prvo";
|
Calendar._TT["DAY_FIRST"] = "Prikaži %s prvo";
|
||||||
|
|
||||||
// This may be locale-dependent. It specifies the week-end days, as an array
|
// This may be locale-dependent. It specifies the week-end days, as an array
|
||||||
// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1
|
// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1
|
||||||
|
|||||||
Vendored
+1
-1
@@ -102,7 +102,7 @@ attachments_009:
|
|||||||
container_type: Version
|
container_type: Version
|
||||||
container_id: 1
|
container_id: 1
|
||||||
downloads: 0
|
downloads: 0
|
||||||
disk_filename: 060719210727_version_file.zip
|
disk_filename: 060719210727_archive.zip
|
||||||
digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
|
digest: b91e08d0cf966d5c6ff411bd8c4cc3a2
|
||||||
id: 9
|
id: 9
|
||||||
filesize: 452
|
filesize: 452
|
||||||
|
|||||||
@@ -104,6 +104,12 @@ class AttachmentsControllerTest < ActionController::TestCase
|
|||||||
assert_equal 'application/x-ruby', @response.content_type
|
assert_equal 'application/x-ruby', @response.content_type
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_download_version_file_with_issue_tracking_disabled
|
||||||
|
Project.find(1).disable_module! :issue_tracking
|
||||||
|
get :download, :id => 9
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
def test_download_should_assign_content_type_if_blank
|
def test_download_should_assign_content_type_if_blank
|
||||||
Attachment.find(4).update_attribute(:content_type, '')
|
Attachment.find(4).update_attribute(:content_type, '')
|
||||||
|
|
||||||
@@ -123,6 +129,7 @@ class AttachmentsControllerTest < ActionController::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_destroy_issue_attachment
|
def test_destroy_issue_attachment
|
||||||
|
set_tmp_attachments_directory
|
||||||
issue = Issue.find(3)
|
issue = Issue.find(3)
|
||||||
@request.session[:user_id] = 2
|
@request.session[:user_id] = 2
|
||||||
|
|
||||||
@@ -139,6 +146,7 @@ class AttachmentsControllerTest < ActionController::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_destroy_wiki_page_attachment
|
def test_destroy_wiki_page_attachment
|
||||||
|
set_tmp_attachments_directory
|
||||||
@request.session[:user_id] = 2
|
@request.session[:user_id] = 2
|
||||||
assert_difference 'Attachment.count', -1 do
|
assert_difference 'Attachment.count', -1 do
|
||||||
post :destroy, :id => 3
|
post :destroy, :id => 3
|
||||||
@@ -147,6 +155,7 @@ class AttachmentsControllerTest < ActionController::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_destroy_project_attachment
|
def test_destroy_project_attachment
|
||||||
|
set_tmp_attachments_directory
|
||||||
@request.session[:user_id] = 2
|
@request.session[:user_id] = 2
|
||||||
assert_difference 'Attachment.count', -1 do
|
assert_difference 'Attachment.count', -1 do
|
||||||
post :destroy, :id => 8
|
post :destroy, :id => 8
|
||||||
@@ -155,6 +164,7 @@ class AttachmentsControllerTest < ActionController::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_destroy_version_attachment
|
def test_destroy_version_attachment
|
||||||
|
set_tmp_attachments_directory
|
||||||
@request.session[:user_id] = 2
|
@request.session[:user_id] = 2
|
||||||
assert_difference 'Attachment.count', -1 do
|
assert_difference 'Attachment.count', -1 do
|
||||||
post :destroy, :id => 9
|
post :destroy, :id => 9
|
||||||
@@ -163,8 +173,11 @@ class AttachmentsControllerTest < ActionController::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_destroy_without_permission
|
def test_destroy_without_permission
|
||||||
post :destroy, :id => 3
|
set_tmp_attachments_directory
|
||||||
assert_redirected_to '/login?back_url=http%3A%2F%2Ftest.host%2Fattachments%2Fdestroy%2F3'
|
assert_no_difference 'Attachment.count' do
|
||||||
|
delete :destroy, :id => 3
|
||||||
|
end
|
||||||
|
assert_response 302
|
||||||
assert Attachment.find_by_id(3)
|
assert Attachment.find_by_id(3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -115,4 +115,23 @@ class ContextMenusControllerTest < ActionController::TestCase
|
|||||||
assert_template 'context_menu'
|
assert_template 'context_menu'
|
||||||
assert_equal [1], assigns(:issues).collect(&:id)
|
assert_equal [1], assigns(:issues).collect(&:id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_time_entries_context_menu
|
||||||
|
@request.session[:user_id] = 2
|
||||||
|
get :time_entries, :ids => [1, 2]
|
||||||
|
assert_response :success
|
||||||
|
assert_template 'time_entries'
|
||||||
|
assert_tag 'a', :content => 'Edit'
|
||||||
|
assert_no_tag 'a', :content => 'Edit', :attributes => {:class => /disabled/}
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_time_entries_context_menu_without_edit_permission
|
||||||
|
@request.session[:user_id] = 2
|
||||||
|
Role.find_by_name('Manager').remove_permission! :edit_time_entries
|
||||||
|
|
||||||
|
get :time_entries, :ids => [1, 2]
|
||||||
|
assert_response :success
|
||||||
|
assert_template 'time_entries'
|
||||||
|
assert_tag 'a', :content => 'Edit', :attributes => {:class => /disabled/}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -872,6 +872,22 @@ class IssuesControllerTest < ActionController::TestCase
|
|||||||
assert_equal Issue.find(1), assigns(:issue)
|
assert_equal Issue.find(1), assigns(:issue)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
|
||||||
|
@request.session[:user_id] = 2
|
||||||
|
Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
|
||||||
|
|
||||||
|
get :edit, :id => 1
|
||||||
|
assert_tag 'input', :attributes => {:name => 'time_entry[hours]'}
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
|
||||||
|
@request.session[:user_id] = 2
|
||||||
|
Role.find_by_name('Manager').remove_permission! :log_time
|
||||||
|
|
||||||
|
get :edit, :id => 1
|
||||||
|
assert_no_tag 'input', :attributes => {:name => 'time_entry[hours]'}
|
||||||
|
end
|
||||||
|
|
||||||
def test_get_edit_with_params
|
def test_get_edit_with_params
|
||||||
@request.session[:user_id] = 2
|
@request.session[:user_id] = 2
|
||||||
get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
|
get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
|
||||||
|
|||||||
@@ -39,7 +39,10 @@ class SysControllerTest < ActionController::TestCase
|
|||||||
assert_equal 'application/xml', @response.content_type
|
assert_equal 'application/xml', @response.content_type
|
||||||
with_options :tag => 'projects' do |test|
|
with_options :tag => 'projects' do |test|
|
||||||
test.assert_tag :children => { :count => Project.active.has_module(:repository).count }
|
test.assert_tag :children => { :count => Project.active.has_module(:repository).count }
|
||||||
|
test.assert_tag 'project', :child => {:tag => 'identifier', :sibling => {:tag => 'is-public'}}
|
||||||
end
|
end
|
||||||
|
assert_no_tag 'extra-info'
|
||||||
|
assert_no_tag 'extra_info'
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_create_project_repository
|
def test_create_project_repository
|
||||||
@@ -49,10 +52,19 @@ class SysControllerTest < ActionController::TestCase
|
|||||||
:vendor => 'Subversion',
|
:vendor => 'Subversion',
|
||||||
:repository => { :url => 'file:///create/project/repository/subproject2'}
|
:repository => { :url => 'file:///create/project/repository/subproject2'}
|
||||||
assert_response :created
|
assert_response :created
|
||||||
|
assert_equal 'application/xml', @response.content_type
|
||||||
|
|
||||||
r = Project.find(4).repository
|
r = Project.find(4).repository
|
||||||
assert r.is_a?(Repository::Subversion)
|
assert r.is_a?(Repository::Subversion)
|
||||||
assert_equal 'file:///create/project/repository/subproject2', r.url
|
assert_equal 'file:///create/project/repository/subproject2', r.url
|
||||||
|
|
||||||
|
assert_tag 'repository-subversion',
|
||||||
|
:child => {
|
||||||
|
:tag => 'id', :content => r.id.to_s,
|
||||||
|
:sibling => {:tag => 'url', :content => r.url}
|
||||||
|
}
|
||||||
|
assert_no_tag 'extra-info'
|
||||||
|
assert_no_tag 'extra_info'
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_fetch_changesets
|
def test_fetch_changesets
|
||||||
|
|||||||
@@ -115,6 +115,18 @@ class TimelogControllerTest < ActionController::TestCase
|
|||||||
assert_equal 3, t.user_id
|
assert_equal 3, t.user_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_create_without_log_time_permission_should_be_denied
|
||||||
|
@request.session[:user_id] = 2
|
||||||
|
Role.find_by_name('Manager').remove_permission! :log_time
|
||||||
|
post :create, :project_id => 1,
|
||||||
|
:time_entry => {:activity_id => '11',
|
||||||
|
:issue_id => '',
|
||||||
|
:spent_on => '2008-03-14',
|
||||||
|
:hours => '7.3'}
|
||||||
|
|
||||||
|
assert_response 403
|
||||||
|
end
|
||||||
|
|
||||||
def test_update
|
def test_update
|
||||||
entry = TimeEntry.find(1)
|
entry = TimeEntry.find(1)
|
||||||
assert_equal 1, entry.issue_id
|
assert_equal 1, entry.issue_id
|
||||||
@@ -161,6 +173,9 @@ class TimelogControllerTest < ActionController::TestCase
|
|||||||
|
|
||||||
def test_bulk_update_on_different_projects
|
def test_bulk_update_on_different_projects
|
||||||
@request.session[:user_id] = 2
|
@request.session[:user_id] = 2
|
||||||
|
# makes user a manager on the other project
|
||||||
|
Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
|
||||||
|
|
||||||
# update time entry activity
|
# update time entry activity
|
||||||
post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
|
post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
|
||||||
|
|
||||||
@@ -203,6 +218,14 @@ class TimelogControllerTest < ActionController::TestCase
|
|||||||
assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
|
assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_post_bulk_update_without_edit_permission_should_be_denied
|
||||||
|
@request.session[:user_id] = 2
|
||||||
|
Role.find_by_name('Manager').remove_permission! :edit_time_entries
|
||||||
|
post :bulk_update, :ids => [1,2]
|
||||||
|
|
||||||
|
assert_response 403
|
||||||
|
end
|
||||||
|
|
||||||
def test_destroy
|
def test_destroy
|
||||||
@request.session[:user_id] = 2
|
@request.session[:user_id] = 2
|
||||||
delete :destroy, :id => 1
|
delete :destroy, :id => 1
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ class WikiControllerTest < ActionController::TestCase
|
|||||||
:content => 'Page with an inline image' } }
|
:content => 'Page with an inline image' } }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_export_link
|
||||||
|
Role.anonymous.add_permission! :export_wiki_pages
|
||||||
|
get :show, :project_id => 'ecookbook'
|
||||||
|
assert_response :success
|
||||||
|
assert_tag 'a', :attributes => {:href => '/projects/ecookbook/wiki/CookBook_documentation.txt'}
|
||||||
|
end
|
||||||
|
|
||||||
def test_show_page_with_name
|
def test_show_page_with_name
|
||||||
get :show, :project_id => 1, :id => 'Another_page'
|
get :show, :project_id => 1, :id => 'Another_page'
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ class CustomValueTest < ActiveSupport::TestCase
|
|||||||
assert v.valid?
|
assert v.valid?
|
||||||
v.value = 'abc'
|
v.value = 'abc'
|
||||||
assert !v.valid?
|
assert !v.valid?
|
||||||
|
v.value = '1975-07-33'
|
||||||
|
assert !v.valid?
|
||||||
v.value = '1975-07-14'
|
v.value = '1975-07-14'
|
||||||
assert v.valid?
|
assert v.valid?
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -223,6 +223,20 @@ class IssueNestedSetTest < ActiveSupport::TestCase
|
|||||||
assert_equal [issue1.id, 2, 3], [issue4.root_id, issue4.lft, issue4.rgt]
|
assert_equal [issue1.id, 2, 3], [issue4.root_id, issue4.lft, issue4.rgt]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_destroy_child_should_update_parent
|
||||||
|
issue = create_issue!
|
||||||
|
child1 = create_issue!(:parent_issue_id => issue.id)
|
||||||
|
child2 = create_issue!(:parent_issue_id => issue.id)
|
||||||
|
|
||||||
|
issue.reload
|
||||||
|
assert_equal [issue.id, 1, 6], [issue.root_id, issue.lft, issue.rgt]
|
||||||
|
|
||||||
|
child2.reload.destroy
|
||||||
|
|
||||||
|
issue.reload
|
||||||
|
assert_equal [issue.id, 1, 4], [issue.root_id, issue.lft, issue.rgt]
|
||||||
|
end
|
||||||
|
|
||||||
def test_destroy_parent_issue_updated_during_children_destroy
|
def test_destroy_parent_issue_updated_during_children_destroy
|
||||||
parent = create_issue!
|
parent = create_issue!
|
||||||
create_issue!(:start_date => Date.today, :parent_issue_id => parent.id)
|
create_issue!(:start_date => Date.today, :parent_issue_id => parent.id)
|
||||||
|
|||||||
@@ -232,6 +232,22 @@ class IssueTest < ActiveSupport::TestCase
|
|||||||
assert_equal custom_value.id, issue.custom_value_for(field).id
|
assert_equal custom_value.id, issue.custom_value_for(field).id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
|
||||||
|
issue = Issue.new(:project_id => 1)
|
||||||
|
issue.attributes = {:tracker_id => 1, :author_id => 1, :status_id => 1, :subject => 'Test', :custom_field_values => {'2' => 'Test'}}
|
||||||
|
issue.save!
|
||||||
|
|
||||||
|
assert !Tracker.find(2).custom_field_ids.include?(2)
|
||||||
|
|
||||||
|
issue = Issue.find(issue.id)
|
||||||
|
issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
|
||||||
|
|
||||||
|
issue = Issue.find(issue.id)
|
||||||
|
custom_value = issue.custom_value_for(2)
|
||||||
|
assert_not_nil custom_value
|
||||||
|
assert_equal 'Test', custom_value.value
|
||||||
|
end
|
||||||
|
|
||||||
def test_assigning_tracker_id_should_reload_custom_fields_values
|
def test_assigning_tracker_id_should_reload_custom_fields_values
|
||||||
issue = Issue.new(:project => Project.find(1))
|
issue = Issue.new(:project => Project.find(1))
|
||||||
assert issue.custom_field_values.empty?
|
assert issue.custom_field_values.empty?
|
||||||
|
|||||||
@@ -43,6 +43,14 @@ class Redmine::CipheringTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_blank_password_should_be_clear
|
||||||
|
Redmine::Configuration.with 'database_cipher_key' => 'secret' do
|
||||||
|
r = Repository::Subversion.generate!(:password => '')
|
||||||
|
assert_equal '', r.password
|
||||||
|
assert_equal '', r.read_attribute(:password)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_unciphered_password_should_be_readable
|
def test_unciphered_password_should_be_readable
|
||||||
Redmine::Configuration.with 'database_cipher_key' => nil do
|
Redmine::Configuration.with 'database_cipher_key' => nil do
|
||||||
r = Repository::Subversion.generate!(:password => 'clear')
|
r = Repository::Subversion.generate!(:password => 'clear')
|
||||||
@@ -54,6 +62,20 @@ class Redmine::CipheringTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_ciphered_password_with_no_cipher_key_configured_should_be_returned_ciphered
|
||||||
|
Redmine::Configuration.with 'database_cipher_key' => 'secret' do
|
||||||
|
r = Repository::Subversion.generate!(:password => 'clear')
|
||||||
|
end
|
||||||
|
|
||||||
|
Redmine::Configuration.with 'database_cipher_key' => '' do
|
||||||
|
r = Repository.first(:order => 'id DESC')
|
||||||
|
# password can not be deciphered
|
||||||
|
assert_nothing_raised do
|
||||||
|
assert r.password.match(/\Aaes-256-cbc:.+\Z/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_encrypt_all
|
def test_encrypt_all
|
||||||
Repository.delete_all
|
Repository.delete_all
|
||||||
Redmine::Configuration.with 'database_cipher_key' => nil do
|
Redmine::Configuration.with 'database_cipher_key' => nil do
|
||||||
|
|||||||
@@ -888,6 +888,7 @@ class ProjectTest < ActiveSupport::TestCase
|
|||||||
assert query
|
assert query
|
||||||
assert_equal @project, query.project
|
assert_equal @project, query.project
|
||||||
end
|
end
|
||||||
|
assert_equal @source_project.queries.map(&:user_id).sort, @project.queries.map(&:user_id).sort
|
||||||
end
|
end
|
||||||
|
|
||||||
should "copy versions" do
|
should "copy versions" do
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ module Redmine
|
|||||||
custom_field_values.each do |custom_value|
|
custom_field_values.each do |custom_value|
|
||||||
custom_value.value = values[custom_value.custom_field_id.to_s] if values.has_key?(custom_value.custom_field_id.to_s)
|
custom_value.value = values[custom_value.custom_field_id.to_s] if values.has_key?(custom_value.custom_field_id.to_s)
|
||||||
end if values.is_a?(Hash)
|
end if values.is_a?(Hash)
|
||||||
self.custom_values = custom_field_values
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_field_values
|
def custom_field_values
|
||||||
@@ -92,6 +91,7 @@ module Redmine
|
|||||||
end
|
end
|
||||||
|
|
||||||
def save_custom_field_values
|
def save_custom_field_values
|
||||||
|
self.custom_values = custom_field_values
|
||||||
custom_field_values.each(&:save)
|
custom_field_values.each(&:save)
|
||||||
@custom_field_values_changed = false
|
@custom_field_values_changed = false
|
||||||
@custom_field_values = nil
|
@custom_field_values = nil
|
||||||
|
|||||||
@@ -162,9 +162,10 @@ module CollectiveIdea #:nodoc:
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Rebuilds the left & rights if unset or invalid. Also very useful for converting from acts_as_tree.
|
# Rebuilds the left & rights if unset or invalid. Also very useful for converting from acts_as_tree.
|
||||||
def rebuild!
|
def rebuild!(force=false)
|
||||||
# Don't rebuild a valid tree.
|
# Don't rebuild a valid tree.
|
||||||
return true if valid?
|
# valid? doesn't strictly validate the tree
|
||||||
|
return true if !force && valid?
|
||||||
|
|
||||||
scope = lambda{|node|}
|
scope = lambda{|node|}
|
||||||
if acts_as_nested_set_options[:scope]
|
if acts_as_nested_set_options[:scope]
|
||||||
@@ -444,7 +445,7 @@ module CollectiveIdea #:nodoc:
|
|||||||
# Prunes a branch off of the tree, shifting all of the elements on the right
|
# Prunes a branch off of the tree, shifting all of the elements on the right
|
||||||
# back to the left so the counts still work.
|
# back to the left so the counts still work.
|
||||||
def prune_from_tree
|
def prune_from_tree
|
||||||
return if right.nil? || left.nil? || leaf? || !self.class.exists?(id)
|
return if right.nil? || left.nil? || !self.class.exists?(id)
|
||||||
|
|
||||||
self.class.base_class.transaction do
|
self.class.base_class.transaction do
|
||||||
reload_nested_set
|
reload_nested_set
|
||||||
|
|||||||
Reference in New Issue
Block a user