Compare commits
34 Commits
Author | SHA1 | Date |
---|---|---|
Felix Schäfer | 4424841784 | |
Felix Schäfer | 2b8ec7c80f | |
Felix Schäfer | 066f616210 | |
Felix Schäfer | 8ea58b6fd5 | |
Holger Just | bfdc43ba73 | |
Holger Just | 2b909243ee | |
Holger Just | b7a82ac691 | |
Holger Just | 964d19cc57 | |
Holger Just | dd945c78c3 | |
Holger Just | 41e349888b | |
Holger Just | dca36c222a | |
Holger Just | 524ef942d9 | |
Holger Just | e06dd303db | |
Holger Just | e2bc4e905a | |
Holger Just | 6ece1687de | |
Holger Just | 6d87b8b297 | |
Holger Just | bd509a4008 | |
Holger Just | b0ec4c140d | |
Holger Just | e178f1ce9c | |
Holger Just | c3d3bec47f | |
Holger Just | 8d56d32774 | |
Holger Just | 4456440535 | |
Holger Just | f959b9bdb9 | |
Holger Just | 9d32e68ec0 | |
Jean-Philippe Lang | 80289c5a70 | |
Jean-Philippe Lang | 902c624b47 | |
Jean-Philippe Lang | aee7d7315b | |
Jean-Philippe Lang | 1f10817444 | |
Jean-Philippe Lang | ea3ff66b8e | |
Jean-Philippe Lang | ee99b2de03 | |
Jean-Philippe Lang | 4c322d379e | |
Jean-Philippe Lang | f12b9fca08 | |
Jean-Philippe Lang | 296b3173ef | |
Jean-Philippe Lang | c651ba1a98 |
|
@ -4,7 +4,6 @@
|
|||
/config/configuration.yml
|
||||
/config/database.yml
|
||||
/config/email.yml
|
||||
/config/setup_load_paths.rb
|
||||
/config/initializers/session_store.rb
|
||||
/coverage
|
||||
/db/*.db
|
||||
|
@ -29,6 +28,3 @@ doc/app
|
|||
/Gemfile.lock
|
||||
/Gemfile.local
|
||||
/.rvmrc*
|
||||
/*.iml
|
||||
/.idea
|
||||
.rbx
|
||||
|
|
59
.travis.yml
59
.travis.yml
|
@ -1,59 +0,0 @@
|
|||
language: ruby
|
||||
rvm:
|
||||
- 1.8.7
|
||||
- 1.9.2
|
||||
- 1.9.3
|
||||
- rbx-18mode
|
||||
env:
|
||||
- "TEST_SUITE=units RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
|
||||
- "TEST_SUITE=units RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite"
|
||||
- "TEST_SUITE=units RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite"
|
||||
- "TEST_SUITE=units RAILS_ENV=test DB=sqlite BUNDLE_WITHOUT=rmagick:mysql:mysql2:postgres"
|
||||
- "TEST_SUITE=functionals RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
|
||||
- "TEST_SUITE=functionals RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite"
|
||||
- "TEST_SUITE=functionals RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite"
|
||||
- "TEST_SUITE=functionals RAILS_ENV=test DB=sqlite BUNDLE_WITHOUT=rmagick:mysql:mysql2:postgres"
|
||||
- "TEST_SUITE=integration RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
|
||||
- "TEST_SUITE=integration RAILS_ENV=test DB=mysql2 BUNDLE_WITHOUT=rmagick:mysql:postgres:sqlite"
|
||||
- "TEST_SUITE=integration RAILS_ENV=test DB=postgres BUNDLE_WITHOUT=rmagick:mysql:mysql2:sqlite"
|
||||
- "TEST_SUITE=integration RAILS_ENV=test DB=sqlite BUNDLE_WITHOUT=rmagick:mysql:mysql2:postgres"
|
||||
matrix:
|
||||
exclude:
|
||||
- rvm: 1.9.2
|
||||
env: "TEST_SUITE=units RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
|
||||
- rvm: 1.9.2
|
||||
env: "TEST_SUITE=functionals RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
|
||||
- rvm: 1.9.2
|
||||
env: "TEST_SUITE=integration RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
|
||||
- rvm: 1.9.3
|
||||
env: "TEST_SUITE=units RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
|
||||
- rvm: 1.9.3
|
||||
env: "TEST_SUITE=functionals RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
|
||||
- rvm: 1.9.3
|
||||
env: "TEST_SUITE=integration RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
|
||||
- rvm: rbx-18mode
|
||||
env: "TEST_SUITE=units RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
|
||||
- rvm: rbx-18mode
|
||||
env: "TEST_SUITE=functionals RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
|
||||
- rvm: rbx-18mode
|
||||
env: "TEST_SUITE=integration RAILS_ENV=test DB=mysql BUNDLE_WITHOUT=rmagick:mysql2:postgres:sqlite"
|
||||
allow_failures:
|
||||
- rvm: rbx-18mode
|
||||
before_install:
|
||||
- "sudo apt-get update -qq"
|
||||
- "sudo apt-get --no-install-recommends install bzr cvs git mercurial subversion"
|
||||
before_script:
|
||||
- "rvm rubygems 1.8.25" # Rubygems 2.0.x fails with Rails 2.3
|
||||
- "rake ci:travis:prepare"
|
||||
- "rm -rf tmp/test/darcs_repository" # Don't test Darcs on Travis. It breaks there :(
|
||||
script: "bundle exec rake test:$TEST_SUITE"
|
||||
branches:
|
||||
only:
|
||||
- unstable
|
||||
- master
|
||||
- stable
|
||||
- /^stable-.*$/
|
||||
- /^release-.*$/
|
||||
notifications:
|
||||
email: false
|
||||
irc: "irc.freenode.org#chiliproject"
|
|
@ -1,33 +0,0 @@
|
|||
For the impatient: [report][cpo_new-issue], confirm, claim,
|
||||
[fork][gh_chiliproject], [branch][cpo_contribute-code-branch],
|
||||
[write][cpo_code-standards], [test][cpo_code-review], push.
|
||||
|
||||
The short version:
|
||||
|
||||
* Make sure the issue you are working on is reported and confirmed, add a note
|
||||
to the issue to claim your intention to work on it.
|
||||
* Fork [ChiliProject on GitHub][gh_chiliproject]
|
||||
* Create a new branch from `master` with a descriptive name prefixed by the
|
||||
issue ID (Example: `123-change_background_from_black_to_blue`).
|
||||
* Make changes according to our [Code Standards][cpo_code-standards].
|
||||
1. Be sure to include tests as necessary.
|
||||
1. Make sure to not break existing tests.
|
||||
1. Please try to make sure your code is going to pass a [Code
|
||||
Review][cpo_code-review] prior to submitting the patch. If in doubt, just ask.
|
||||
* Either upload your branch to GitHub and send a pull request to the
|
||||
ChiliProject repository, or attach a patch to the issue on ChiliProject. If
|
||||
you send a pull request on GitHub, remember to link to the pull request in the
|
||||
issue you create on ChiliProject and to link to the issue on ChiliProject in
|
||||
the pull request on GitHub.
|
||||
* Make sure you watch the corresponding issue in case any discussion arises or
|
||||
improvements are needed.
|
||||
|
||||
The long version is on the [Contribute Code][cpo_contribute-code] page.
|
||||
|
||||
|
||||
[cpo_new-issue]: https://www.chiliproject.org/projects/chiliproject/issues/new
|
||||
[cpo_contribute-code-branch]: https://www.chiliproject.org/projects/chiliproject/wiki/Contribute_Code#Branch
|
||||
[cpo_contribute-code]: https://www.chiliproject.org/projects/chiliproject/wiki/Contribute_Code
|
||||
[cpo_code-standards]: https://www.chiliproject.org/projects/chiliproject/wiki/Code_Standards
|
||||
[cpo_code-review]: https://www.chiliproject.org/projects/chiliproject/wiki/Code_Review
|
||||
[gh_chiliproject]: https://github.com/chiliproject/chiliproject
|
27
Gemfile
27
Gemfile
|
@ -1,32 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
source "https://rubygems.org"
|
||||
source :rubygems
|
||||
|
||||
gem "rails", "2.3.18"
|
||||
gem "rails", "2.3.16"
|
||||
|
||||
gem "json", "~> 1.7.7"
|
||||
gem "coderay", "~> 1.0.0"
|
||||
gem "coderay", "~> 0.9.7"
|
||||
gem "i18n", "~> 0.4.2"
|
||||
gem "rubytree", "~> 0.5.2", :require => 'tree'
|
||||
gem "rdoc", ">= 2.4.2"
|
||||
gem "liquid", "~> 2.3.0"
|
||||
gem "acts-as-taggable-on", "= 2.1.0"
|
||||
gem 'gravatarify', '~> 3.0.0'
|
||||
# Needed only on RUBY_VERSION = 1.8, ruby 1.9+ compatible interpreters should bring their csv
|
||||
gem "fastercsv", "~> 1.5.0", :platforms => [:ruby_18, :jruby, :mingw_18]
|
||||
gem "tzinfo", "~> 0.3.31" # Fixes #903. Not required for Rails >= 3.2
|
||||
|
||||
group :test do
|
||||
gem 'shoulda', '~> 2.10.3'
|
||||
# Shoulda doesn't work nice on 1.9.3 and seems to need test-unit explicitely…
|
||||
gem 'test-unit', :platforms => [:mri_19]
|
||||
gem 'edavis10-object_daddy', :require => 'object_daddy'
|
||||
gem 'mocha', '0.12.1'
|
||||
# capybara 2 drops ruby 1.8.7 compatibility
|
||||
gem 'capybara', '< 2.0.0'
|
||||
gem 'mocha'
|
||||
gem 'capybara'
|
||||
end
|
||||
|
||||
group :ldap do
|
||||
gem "net-ldap", '~> 0.3.1'
|
||||
gem "net-ldap", '~> 0.2.2'
|
||||
end
|
||||
|
||||
group :openid do
|
||||
|
@ -57,13 +48,13 @@ end
|
|||
# orders of magnitude compared to their native counterparts. You have been
|
||||
# warned.
|
||||
|
||||
platforms :mri, :mingw, :rbx do
|
||||
platforms :mri, :mingw do
|
||||
group :mysql2 do
|
||||
gem "mysql2", "~> 0.2.7"
|
||||
end
|
||||
|
||||
group :postgres do
|
||||
gem "pg"
|
||||
gem "pg", "~> 0.9.0"
|
||||
# gem "postgres-pr"
|
||||
end
|
||||
end
|
||||
|
@ -79,7 +70,7 @@ platforms :mri_18, :mingw_18 do
|
|||
end
|
||||
end
|
||||
|
||||
platforms :mri_19, :mingw_19, :rbx do
|
||||
platforms :mri_19, :mingw_19 do
|
||||
group :sqlite do
|
||||
gem "sqlite3"
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
= ChiliProject {<img src="https://travis-ci.org/chiliproject/chiliproject.png?branch=master" />}[http://travis-ci.org/chiliproject/chiliproject]
|
||||
= ChiliProject
|
||||
|
||||
ChiliProject is a web based project management system. It supports your team throughout the complete project life cycle, from setting up and discussing a project plan, over tracking issues and reporting work progress to collaboratively sharing knowledge.
|
||||
|
||||
|
|
2
Rakefile
2
Rakefile
|
@ -8,5 +8,3 @@ require 'rake/testtask'
|
|||
require 'rdoc/task'
|
||||
|
||||
require 'tasks/rails'
|
||||
# Load rake tasks from plugins in chiliproject_plugins
|
||||
Dir["#{RAILS_ROOT}/vendor/chiliproject_plugins/*/lib/tasks/**/*.rake"].sort.each { |ext| load ext }
|
||||
|
|
|
@ -37,7 +37,7 @@ class AccountController < ApplicationController
|
|||
def lost_password
|
||||
redirect_to(home_url) && return unless Setting.lost_password?
|
||||
if params[:token]
|
||||
@token = Token.find_by_action_and_value("recovery", params[:token].to_s)
|
||||
@token = Token.find_by_action_and_value("recovery", params[:token])
|
||||
redirect_to(home_url) && return unless @token and !@token.expired?
|
||||
@user = @token.user
|
||||
if request.post?
|
||||
|
@ -53,7 +53,7 @@ class AccountController < ApplicationController
|
|||
return
|
||||
else
|
||||
if request.post?
|
||||
user = User.find_by_mail(params[:mail].to_s)
|
||||
user = User.find_by_mail(params[:mail])
|
||||
# user not found in db
|
||||
(flash.now[:error] = l(:notice_account_unknown_email); return) unless user
|
||||
# user uses an external authentification
|
||||
|
@ -109,7 +109,7 @@ class AccountController < ApplicationController
|
|||
# Token based account activation
|
||||
def activate
|
||||
redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
|
||||
token = Token.find_by_action_and_value('register', params[:token].to_s)
|
||||
token = Token.find_by_action_and_value('register', params[:token])
|
||||
redirect_to(home_url) && return unless token and !token.expired?
|
||||
user = token.user
|
||||
redirect_to(home_url) && return unless user.registered?
|
||||
|
|
|
@ -33,7 +33,7 @@ class ActivitiesController < ApplicationController
|
|||
:with_subprojects => @with_subprojects,
|
||||
:author => @author)
|
||||
@activity.scope_select {|t| !params["show_#{t}"].nil?}
|
||||
@activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty? unless params[:set_filter]
|
||||
@activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
|
||||
|
||||
events = @activity.events(@date_from, @date_to)
|
||||
|
||||
|
|
|
@ -19,10 +19,6 @@ class AdminController < ApplicationController
|
|||
|
||||
include SortHelper
|
||||
|
||||
menu_item :projects, :only => [:projects]
|
||||
menu_item :plugins, :only => [:plugins]
|
||||
menu_item :info, :only => [:info]
|
||||
|
||||
def index
|
||||
@no_configuration_data = Redmine::DefaultData::Loader::no_data?
|
||||
end
|
||||
|
@ -77,7 +73,7 @@ class AdminController < ApplicationController
|
|||
def info
|
||||
@db_adapter_name = ActiveRecord::Base.connection.adapter_name
|
||||
@checklist = [
|
||||
[:text_default_administrator_account_changed, !User.find_by_login("admin").try(:check_password?, "admin")],
|
||||
[:text_default_administrator_account_changed, User.find(:first, :conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?],
|
||||
[:text_file_repository_writable, File.writable?(Attachment.storage_path)],
|
||||
[:text_plugin_assets_writable, File.writable?(Engines.public_directory)],
|
||||
[:text_rmagick_available, Object.const_defined?(:Magick)]
|
||||
|
|
|
@ -31,6 +31,18 @@ class ApplicationController < ActionController::Base
|
|||
cookies.delete(:autologin)
|
||||
end
|
||||
|
||||
# Remove broken cookie after upgrade from 0.8.x (#4292)
|
||||
# See https://rails.lighthouseapp.com/projects/8994/tickets/3360
|
||||
# TODO: remove it when Rails is fixed
|
||||
before_filter :delete_broken_cookies
|
||||
def delete_broken_cookies
|
||||
if cookies['_chiliproject_session'] && cookies['_chiliproject_session'] !~ /--/
|
||||
cookies.delete '_chiliproject_session'
|
||||
redirect_to home_path
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# FIXME: Remove this when all of Rack and Rails have learned how to
|
||||
# properly use encodings
|
||||
before_filter :params_filter
|
||||
|
@ -52,16 +64,7 @@ class ApplicationController < ActionController::Base
|
|||
before_filter :user_setup, :check_if_login_required, :set_localization
|
||||
filter_parameter_logging :password
|
||||
|
||||
# FIXME: This doesn't work with Rails >= 3.0 anymore
|
||||
# Possible workaround: https://github.com/rails/rails/issues/671#issuecomment-1780159
|
||||
rescue_from ActionController::RoutingError, :with => proc{
|
||||
# manually apply basic before_filters which aren't applied by default here
|
||||
user_setup
|
||||
check_if_login_required
|
||||
set_localization
|
||||
|
||||
render_404
|
||||
}
|
||||
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
|
||||
|
||||
include Redmine::Search::Controller
|
||||
include Redmine::MenuManager::MenuController
|
||||
|
@ -72,6 +75,8 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def user_setup
|
||||
# Check the settings cache for each request
|
||||
Setting.check_cache
|
||||
# Find the current user
|
||||
User.current = find_current_user
|
||||
end
|
||||
|
@ -89,11 +94,11 @@ class ApplicationController < ActionController::Base
|
|||
user
|
||||
elsif params[:format] == 'atom' && params[:key] && accept_key_auth_actions.include?(params[:action])
|
||||
# RSS key authentication does not start a session
|
||||
User.find_by_rss_key(params[:key].to_s)
|
||||
User.find_by_rss_key(params[:key])
|
||||
elsif Setting.rest_api_enabled? && api_request?
|
||||
if (key = api_key_from_request) && accept_key_auth_actions.include?(params[:action])
|
||||
# Use API key
|
||||
User.find_by_api_key(key.to_s)
|
||||
User.find_by_api_key(key)
|
||||
else
|
||||
# HTTP Basic, either username/password or API key/random
|
||||
authenticate_with_http_basic do |username, password|
|
||||
|
@ -330,6 +335,13 @@ class ApplicationController < ActionController::Base
|
|||
request.xhr? ? false : 'base'
|
||||
end
|
||||
|
||||
def invalid_authenticity_token
|
||||
if api_request?
|
||||
logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)."
|
||||
end
|
||||
render_error "Invalid form authenticity token."
|
||||
end
|
||||
|
||||
def render_feed(items, options={})
|
||||
@items = items || []
|
||||
@items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
#++
|
||||
|
||||
class AutoCompletesController < ApplicationController
|
||||
before_filter :find_project, :only => :issues
|
||||
before_filter :require_admin, :only => :projects
|
||||
before_filter :find_project
|
||||
|
||||
def issues
|
||||
@issues = []
|
||||
|
@ -34,38 +33,6 @@ class AutoCompletesController < ApplicationController
|
|||
render :layout => false
|
||||
end
|
||||
|
||||
def users
|
||||
if params[:remove_group_members].present?
|
||||
@group = Group.find(params[:remove_group_members])
|
||||
@removed_users = @group.users
|
||||
end
|
||||
|
||||
if params[:remove_watchers].present? && params[:klass].present?
|
||||
watcher_class = params[:klass].constantize
|
||||
if watcher_class.included_modules.include?(Redmine::Acts::Watchable) # check class is a watching class
|
||||
@object = watcher_class.find(params[:remove_watchers])
|
||||
@removed_users = @object.watcher_users
|
||||
end
|
||||
end
|
||||
|
||||
@removed_users ||= []
|
||||
|
||||
if params[:include_groups]
|
||||
user_finder = Principal
|
||||
else
|
||||
user_finder = User
|
||||
end
|
||||
|
||||
@users = user_finder.active.like(params[:q]).find(:all, :limit => 100) - @removed_users
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
def projects
|
||||
@principal = Principal.find(params[:id])
|
||||
@projects = Project.active.like(params[:q]).find(:all, :limit => 100) - @principal.projects
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_project
|
||||
|
|
|
@ -21,10 +21,7 @@ class CommentsController < ApplicationController
|
|||
|
||||
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
|
||||
def create
|
||||
raise Unauthorized unless @news.commentable?
|
||||
|
||||
@comment = Comment.new
|
||||
@comment.safe_attributes = params[:comment]
|
||||
@comment = Comment.new(params[:comment])
|
||||
@comment.author = User.current
|
||||
if @news.comments << @comment
|
||||
flash[:notice] = l(:label_comment_added)
|
||||
|
|
|
@ -45,34 +45,21 @@ class DocumentsController < ApplicationController
|
|||
def new
|
||||
@document = @project.documents.build
|
||||
@document.safe_attributes = params[:document]
|
||||
if request.post?
|
||||
if User.current.allowed_to?(:add_document_watchers, @project) && params[:document]['watcher_user_ids'].present?
|
||||
@document.watcher_user_ids = params[:document]['watcher_user_ids']
|
||||
end
|
||||
|
||||
if @document.save
|
||||
if request.post? && @document.save
|
||||
attachments = Attachment.attach_files(@document, params[:attachments])
|
||||
render_attachment_warning_if_needed(@document)
|
||||
flash[:notice] = l(:notice_successful_create)
|
||||
redirect_to :action => 'index', :project_id => @project
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@categories = DocumentCategory.all
|
||||
|
||||
if request.post?
|
||||
if User.current.allowed_to?(:add_document_watchers, @project) && params[:document]['watcher_user_ids'].present?
|
||||
@document.watcher_user_ids = params[:document]['watcher_user_ids']
|
||||
end
|
||||
|
||||
if @document.update_attributes(params[:document])
|
||||
if request.post? and @document.update_attributes(params[:document])
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
redirect_to :action => 'show', :id => @document
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@document.destroy
|
||||
|
@ -83,12 +70,7 @@ class DocumentsController < ApplicationController
|
|||
attachments = Attachment.attach_files(@document, params[:attachments])
|
||||
render_attachment_warning_if_needed(@document)
|
||||
|
||||
if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
|
||||
# TODO: refactor
|
||||
@document.recipients.each do |recipient|
|
||||
Mailer.deliver_attachments_added(attachments[:files], recipient)
|
||||
end
|
||||
end
|
||||
Mailer.deliver_attachments_added(attachments[:files]) if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added')
|
||||
redirect_to :action => 'show', :id => @document
|
||||
end
|
||||
|
||||
|
|
|
@ -42,11 +42,7 @@ class FilesController < ApplicationController
|
|||
render_attachment_warning_if_needed(container)
|
||||
|
||||
if !attachments.empty? && !attachments[:files].blank? && Setting.notified_events.include?('file_added')
|
||||
# TODO: refactor
|
||||
recipients = attachments[:files].first.container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail}
|
||||
recipients.each do |recipient|
|
||||
Mailer.deliver_attachments_added(attachments[:files], recipient)
|
||||
end
|
||||
Mailer.deliver_attachments_added(attachments[:files])
|
||||
end
|
||||
redirect_to project_files_path(@project)
|
||||
end
|
||||
|
|
|
@ -126,19 +126,16 @@ class GroupsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def autocomplete_for_user
|
||||
@group = Group.find(params[:id])
|
||||
@users = User.active.not_in_group(@group).like(params[:q]).all(:limit => 100)
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
def edit_membership
|
||||
@group = Group.find(params[:id])
|
||||
|
||||
if params[:project_ids] # Multiple memberships, one per project
|
||||
params[:project_ids].each do |project_id|
|
||||
@membership = Member.edit_membership(params[:membership_id], (params[:membership]|| {}).merge(:project_id => project_id), @group)
|
||||
@membership.save if request.post?
|
||||
end
|
||||
else # Single membership
|
||||
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
|
||||
@membership.save if request.post?
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
if @membership.valid?
|
||||
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
require 'diff'
|
||||
|
||||
class JournalsController < ApplicationController
|
||||
before_filter :find_journal, :only => [:edit, :diff]
|
||||
before_filter :find_issue, :only => [:new]
|
||||
|
@ -86,22 +84,6 @@ class JournalsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def diff
|
||||
if valid_field?(params[:field])
|
||||
from = @journal.changes[params[:field]][0]
|
||||
to = @journal.changes[params[:field]][1]
|
||||
|
||||
@diff = Redmine::Helpers::Diff.new(to, from)
|
||||
@issue = @journal.journaled
|
||||
respond_to do |format|
|
||||
format.html { }
|
||||
format.js { render :layout => false }
|
||||
end
|
||||
else
|
||||
render_404
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_journal
|
||||
|
@ -118,9 +100,4 @@ class JournalsController < ApplicationController
|
|||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
|
||||
# Is this a valid field for diff'ing?
|
||||
def valid_field?(field)
|
||||
field.to_s.strip == "description"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
class LdapAuthSourcesController < AuthSourcesController
|
||||
|
||||
menu_item :ldap_authentication, :only => [:index]
|
||||
protected
|
||||
|
||||
def auth_source_class
|
||||
|
|
|
@ -107,7 +107,7 @@ class MessagesController < ApplicationController
|
|||
content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
|
||||
content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
|
||||
render(:update) { |page|
|
||||
page << "$('message_subject').value = \"#{subject}\";"
|
||||
page << "$('reply_subject').value = \"#{subject}\";"
|
||||
page.<< "$('message_content').value = \"#{content}\";"
|
||||
page.show 'reply'
|
||||
page << "Form.Element.focus('message_content');"
|
||||
|
|
|
@ -20,7 +20,6 @@ class QueriesController < ApplicationController
|
|||
def new
|
||||
@query = Query.new(params[:query])
|
||||
@query.project = params[:query_is_for_all] ? nil : @project
|
||||
@query.display_subprojects = params[:display_subprojects] if params[:display_subprojects].present?
|
||||
@query.user = User.current
|
||||
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
|
||||
|
||||
|
@ -43,7 +42,6 @@ class QueriesController < ApplicationController
|
|||
@query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v]) if params[:fields] || params[:f]
|
||||
@query.attributes = params[:query]
|
||||
@query.project = nil if params[:query_is_for_all]
|
||||
@query.display_subprojects = params[:display_subprojects] if params[:display_subprojects].present?
|
||||
@query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
|
||||
@query.group_by ||= params[:group_by]
|
||||
@query.column_names = params[:c] if params[:c]
|
||||
|
|
|
@ -72,7 +72,8 @@ class TimeEntryReportsController < ApplicationController
|
|||
@periods = []
|
||||
# Date#at_beginning_of_ not supported in Rails 1.2.x
|
||||
date_from = @from.to_time
|
||||
while date_from <= @to.to_time
|
||||
# 100 columns max
|
||||
while date_from <= @to.to_time && @periods.length < 100
|
||||
case @columns
|
||||
when 'year'
|
||||
@periods << "#{date_from.year}"
|
||||
|
@ -160,9 +161,6 @@ class TimeEntryReportsController < ApplicationController
|
|||
@available_criterias = { 'project' => {:sql => "#{TimeEntry.table_name}.project_id",
|
||||
:klass => Project,
|
||||
:label => :label_project},
|
||||
'status' => {:sql => "#{Issue.table_name}.status_id",
|
||||
:klass => IssueStatus,
|
||||
:label => :field_status},
|
||||
'version' => {:sql => "#{Issue.table_name}.fixed_version_id",
|
||||
:klass => Version,
|
||||
:label => :label_version},
|
||||
|
|
|
@ -197,16 +197,8 @@ class UsersController < ApplicationController
|
|||
|
||||
|
||||
def edit_membership
|
||||
if params[:project_ids] # Multiple memberships, one per project
|
||||
params[:project_ids].each do |project_id|
|
||||
@membership = Member.edit_membership(params[:membership_id], (params[:membership] || {}).merge(:project_id => project_id), @user)
|
||||
@membership.save if request.post?
|
||||
end
|
||||
else # Single membership
|
||||
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
|
||||
@membership.save if request.post?
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
if @membership.valid?
|
||||
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
|
||||
|
|
|
@ -16,7 +16,6 @@ class WatchersController < ApplicationController
|
|||
before_filter :find_project
|
||||
before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
|
||||
before_filter :authorize, :only => [:new, :destroy]
|
||||
before_filter :authorize_access_to_object, :only => [:new, :destroy]
|
||||
|
||||
verify :method => :post,
|
||||
:only => [ :watch, :unwatch ],
|
||||
|
@ -35,12 +34,9 @@ class WatchersController < ApplicationController
|
|||
end
|
||||
|
||||
def new
|
||||
params[:user_ids].each do |user_id|
|
||||
@watcher = Watcher.new((params[:watcher] || {}).merge({:user_id => user_id}))
|
||||
@watcher = Watcher.new(params[:watcher])
|
||||
@watcher.watchable = @watched
|
||||
@watcher.save if request.post?
|
||||
end if params[:user_ids].present?
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to :back }
|
||||
format.js do
|
||||
|
@ -54,7 +50,7 @@ class WatchersController < ApplicationController
|
|||
end
|
||||
|
||||
def destroy
|
||||
@watched.set_watcher(Principal.find(params[:user_id]), false) if request.post?
|
||||
@watched.set_watcher(User.find(params[:user_id]), false) if request.post?
|
||||
respond_to do |format|
|
||||
format.html { redirect_to :back }
|
||||
format.js do
|
||||
|
@ -98,24 +94,4 @@ private
|
|||
rescue ::ActionController::RedirectBackError
|
||||
render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true
|
||||
end
|
||||
|
||||
def authorize_access_to_object
|
||||
permission = ''
|
||||
case params[:action]
|
||||
when 'new'
|
||||
permission << 'add_'
|
||||
when 'destroy'
|
||||
permission << 'delete_'
|
||||
end
|
||||
|
||||
# Ends up like: :delete_wiki_page_watchers
|
||||
permission << "#{@watched.class.name.underscore}_watchers"
|
||||
|
||||
if User.current.allowed_to?(permission.to_sym, @project)
|
||||
return true
|
||||
else
|
||||
deny_access
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
#-- encoding: UTF-8
|
||||
#-- copyright
|
||||
# ChiliProject is a project management system.
|
||||
#
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
class BaseDrop < Liquid::Drop
|
||||
def initialize(object)
|
||||
@object = object unless object.respond_to?(:visible?) && !object.visible?
|
||||
end
|
||||
|
||||
# Defines a Liquid method on the drop that is allowed to call the
|
||||
# Ruby method directly. Best used for attributes.
|
||||
#
|
||||
# Based on Module#liquid_methods
|
||||
def self.allowed_methods(*allowed_methods)
|
||||
class_eval do
|
||||
allowed_methods.each do |sym|
|
||||
define_method sym do
|
||||
if @object.respond_to?(:public_send)
|
||||
@object.public_send(sym) rescue nil
|
||||
else
|
||||
@object.send(sym) rescue nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,79 +0,0 @@
|
|||
#-- encoding: UTF-8
|
||||
#-- copyright
|
||||
# ChiliProject is a project management system.
|
||||
#
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
class IssueDrop < BaseDrop
|
||||
allowed_methods :id
|
||||
allowed_methods :subject
|
||||
allowed_methods :description
|
||||
allowed_methods :project
|
||||
allowed_methods :tracker
|
||||
allowed_methods :status
|
||||
allowed_methods :due_date
|
||||
allowed_methods :category
|
||||
allowed_methods :assigned_to
|
||||
allowed_methods :priority
|
||||
allowed_methods :fixed_version
|
||||
allowed_methods :author
|
||||
allowed_methods :created_on
|
||||
allowed_methods :updated_on
|
||||
allowed_methods :start_date
|
||||
allowed_methods :done_ratio
|
||||
allowed_methods :estimated_hours
|
||||
allowed_methods :parent
|
||||
|
||||
def custom_field(name)
|
||||
return '' unless name.present?
|
||||
custom_field = IssueCustomField.find_by_name(name.strip)
|
||||
return '' unless custom_field.present?
|
||||
custom_value = @object.custom_value_for(custom_field)
|
||||
if custom_value.present?
|
||||
return custom_value.value
|
||||
else
|
||||
return ''
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: both required, method_missing for Ruby and before_method for Liquid
|
||||
|
||||
# Allows accessing custom fields by their name:
|
||||
#
|
||||
# - issue.the_name_of_player => CustomField(:name => "The name Of Player")
|
||||
#
|
||||
def before_method(method_sym)
|
||||
if custom_field_with_matching_name = has_custom_field_with_matching_name?(method_sym)
|
||||
custom_field(custom_field_with_matching_name.name)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
# Allows accessing custom fields by their name:
|
||||
#
|
||||
# - issue.the_name_of_player => CustomField(:name => "The name Of Player")
|
||||
#
|
||||
def method_missing(method_sym, *arguments, &block)
|
||||
if custom_field_with_matching_name = has_custom_field_with_matching_name?(method_sym)
|
||||
custom_field(custom_field_with_matching_name.name)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def has_custom_field_with_matching_name?(method_sym)
|
||||
custom_field_with_matching_name = @object.available_custom_fields.detect {|custom_field|
|
||||
custom_field.name.downcase.underscore.gsub(' ','_') == method_sym.to_s
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
#-- encoding: UTF-8
|
||||
#-- copyright
|
||||
# ChiliProject is a project management system.
|
||||
#
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
class IssueStatusDrop < BaseDrop
|
||||
allowed_methods :name
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
#-- encoding: UTF-8
|
||||
#-- copyright
|
||||
# ChiliProject is a project management system.
|
||||
#
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
class PrincipalDrop < BaseDrop
|
||||
allowed_methods :name
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
#-- encoding: UTF-8
|
||||
#-- copyright
|
||||
# ChiliProject is a project management system.
|
||||
#
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
class ProjectDrop < BaseDrop
|
||||
allowed_methods :name, :identifier
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
#-- encoding: UTF-8
|
||||
#-- copyright
|
||||
# ChiliProject is a project management system.
|
||||
#
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
class TrackerDrop < BaseDrop
|
||||
allowed_methods :name
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
#-- encoding: UTF-8
|
||||
#-- copyright
|
||||
# ChiliProject is a project management system.
|
||||
#
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
class WikiPageDrop < BaseDrop
|
||||
allowed_methods :title
|
||||
end
|
|
@ -16,8 +16,9 @@ require 'forwardable'
|
|||
require 'cgi'
|
||||
|
||||
module ApplicationHelper
|
||||
include Redmine::WikiFormatting::Macros::Definitions
|
||||
include Redmine::I18n
|
||||
include Gravatarify::Helper
|
||||
include GravatarHelper::PublicMethods
|
||||
|
||||
extend Forwardable
|
||||
def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
|
||||
|
@ -223,15 +224,17 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
# Renders the project quick-jump box
|
||||
def render_project_jump_box(projects = [], html_options = {})
|
||||
projects ||= User.current.memberships.collect(&:project).compact.uniq
|
||||
def render_project_jump_box
|
||||
projects = User.current.memberships.collect(&:project).compact.uniq
|
||||
if projects.any?
|
||||
# option_tags = content_tag :option, l(:label_jump_to_a_project), :value => ""
|
||||
option_tags = (content_tag :option, "", :value => "" )
|
||||
option_tags << project_tree_options_for_select(projects, :selected => @project) do |p|
|
||||
s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
|
||||
"<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
|
||||
'<option value="" disabled="disabled">---</option>'
|
||||
s << project_tree_options_for_select(projects, :selected => @project) do |p|
|
||||
{ :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
|
||||
end
|
||||
select_tag "", option_tags, html_options.merge({ :onchange => "if (this.value != \'\') { window.location = this.value; }" })
|
||||
s << '</select>'
|
||||
s
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -285,15 +288,7 @@ module ApplicationHelper
|
|||
def principals_check_box_tags(name, principals)
|
||||
s = ''
|
||||
principals.sort.each do |principal|
|
||||
s << "<label style='display:block;'>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
|
||||
end
|
||||
s
|
||||
end
|
||||
|
||||
def projects_check_box_tags(name, projects)
|
||||
s = ''
|
||||
projects.each do |project|
|
||||
s << "<label>#{ check_box_tag name, project.id, false } #{h project}</label>\n"
|
||||
s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
|
||||
end
|
||||
s
|
||||
end
|
||||
|
@ -394,9 +389,7 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def page_header_title
|
||||
if @page_header_title.present?
|
||||
h(@page_header_title)
|
||||
elsif @project.nil? || @project.new_record?
|
||||
if @project.nil? || @project.new_record?
|
||||
h(Setting.app_title)
|
||||
else
|
||||
b = []
|
||||
|
@ -436,9 +429,8 @@ module ApplicationHelper
|
|||
css << 'theme-' + theme.name
|
||||
end
|
||||
|
||||
css << 'project-' + @project.id.to_s if @project.present?
|
||||
css << 'controller-' + params[:controller] if params[:controller]
|
||||
css << 'action-' + params[:action] if params[:action]
|
||||
css << 'controller-' + params[:controller]
|
||||
css << 'action-' + params[:action]
|
||||
css.join(' ')
|
||||
end
|
||||
|
||||
|
@ -455,55 +447,23 @@ module ApplicationHelper
|
|||
case args.size
|
||||
when 1
|
||||
obj = options[:object]
|
||||
input_text = args.shift
|
||||
text = args.shift
|
||||
when 2
|
||||
obj = args.shift
|
||||
attr = args.shift
|
||||
input_text = obj.send(attr).to_s
|
||||
text = obj.send(attr).to_s
|
||||
else
|
||||
raise ArgumentError, 'invalid arguments to textilizable'
|
||||
end
|
||||
return '' if input_text.blank?
|
||||
return '' if text.blank?
|
||||
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
|
||||
only_path = options.delete(:only_path) == false ? false : true
|
||||
|
||||
begin
|
||||
text = ChiliProject::Liquid::Legacy.run_macros(input_text)
|
||||
liquid_template = ChiliProject::Liquid::Template.parse(text)
|
||||
liquid_variables = get_view_instance_variables_for_liquid
|
||||
liquid_variables.merge!({'current_user' => User.current})
|
||||
liquid_variables.merge!({'toc' => '{{toc}}'}) # Pass toc through to replace later
|
||||
liquid_variables.merge!(ChiliProject::Liquid::Variables.all)
|
||||
|
||||
# Pass :view in a register so this view (with helpers) can be used inside of a tag
|
||||
text = liquid_template.render(liquid_variables, :registers => {:view => self, :object => obj, :attribute => attr})
|
||||
|
||||
# Add Liquid errors to the log
|
||||
if Rails.logger && Rails.logger.debug?
|
||||
msg = ""
|
||||
liquid_template.errors.each do |exception|
|
||||
msg << "[Liquid Error] #{exception.message}\n:\n#{exception.backtrace.join("\n")}"
|
||||
msg << "\n\n"
|
||||
end
|
||||
Rails.logger.debug msg
|
||||
end
|
||||
rescue Liquid::SyntaxError => exception
|
||||
msg = "[Liquid Syntax Error] #{exception.message}"
|
||||
if Rails.logger && Rails.logger.debug?
|
||||
log_msg = "#{msg}\n"
|
||||
log_msg << exception.backtrace.collect{ |str| " #{str}" }.join("\n")
|
||||
log_msg << "\n\n"
|
||||
Rails.logger.debug log_msg
|
||||
end
|
||||
|
||||
# Skip Liquid if there is a syntax error
|
||||
text = content_tag(:div, msg, :class => "flash error")
|
||||
text << h(input_text)
|
||||
end
|
||||
text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
|
||||
|
||||
@parsed_headings = []
|
||||
text = parse_non_pre_blocks(text) do |text|
|
||||
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings, :parse_relative_urls].each do |method_name|
|
||||
[:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
|
||||
send method_name, text, project, obj, attr, only_path, options
|
||||
end
|
||||
end
|
||||
|
@ -544,41 +504,6 @@ module ApplicationHelper
|
|||
parsed
|
||||
end
|
||||
|
||||
RELATIVE_LINK_RE = %r{
|
||||
<a
|
||||
(?:
|
||||
(\shref=
|
||||
(?: # the href and link
|
||||
(?:'(\/[^>]+?)')|
|
||||
(?:"(\/[^>]+?)")
|
||||
)
|
||||
)|
|
||||
[^>]
|
||||
)*
|
||||
>
|
||||
[^<]*?<\/a> # content and closing link tag.
|
||||
}x unless const_defined?(:RELATIVE_LINK_RE)
|
||||
|
||||
def parse_relative_urls(text, project, obj, attr, only_path, options)
|
||||
return if only_path
|
||||
text.gsub!(RELATIVE_LINK_RE) do |m|
|
||||
href, relative_url = $1, $2 || $3
|
||||
next m unless href.present?
|
||||
if defined?(request) && request.present?
|
||||
# we have a request!
|
||||
protocol, host_with_port = request.protocol, request.host_with_port
|
||||
elsif @controller
|
||||
# use the same methods as url_for in the Mailer
|
||||
url_opts = @controller.class.default_url_options
|
||||
next m unless url_opts && url_opts[:protocol] && url_opts[:host]
|
||||
protocol, host_with_port = "#{url_opts[:protocol]}://", url_opts[:host]
|
||||
else
|
||||
next m
|
||||
end
|
||||
m.sub href, " href=\"#{protocol}#{host_with_port}#{relative_url}\""
|
||||
end
|
||||
end
|
||||
|
||||
def parse_inline_attachments(text, project, obj, attr, only_path, options)
|
||||
# when using an image link, try to use an attachment, if possible
|
||||
if options[:attachments] || (obj && obj.respond_to?(:attachments))
|
||||
|
@ -787,7 +712,7 @@ module ApplicationHelper
|
|||
end
|
||||
end
|
||||
|
||||
TOC_RE = /<p>\{%\s*toc(_right|_left)?\s*%\}<\/p>/i unless const_defined?(:TOC_RE)
|
||||
TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
|
||||
|
||||
# Renders the TOC with given headings
|
||||
def replace_toc(text, headings)
|
||||
|
@ -795,14 +720,10 @@ module ApplicationHelper
|
|||
if headings.empty?
|
||||
''
|
||||
else
|
||||
toc_class = 'toc'
|
||||
toc_class << ' right' if $1 == '_right'
|
||||
toc_class << ' left' if $1 == '_left'
|
||||
|
||||
out = "<fieldset class=\"header_collapsible collapsible #{toc_class}\">"
|
||||
out << "<legend onclick=\"toggleFieldset(this);\"><span>#{l(:label_toc)}</span></legend>"
|
||||
out << "<div>"
|
||||
out << "<ul class=\"toc\"><li>"
|
||||
div_class = 'toc'
|
||||
div_class << ' right' if $1 == '>'
|
||||
div_class << ' left' if $1 == '<'
|
||||
out = "<ul class=\"#{div_class}\"><li>"
|
||||
root = headings.map(&:first).min
|
||||
current = root
|
||||
started = false
|
||||
|
@ -820,7 +741,6 @@ module ApplicationHelper
|
|||
end
|
||||
out << '</li></ul>' * (current - root)
|
||||
out << '</li></ul>'
|
||||
out << '</div></fieldset>'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -852,7 +772,7 @@ module ApplicationHelper
|
|||
def back_url_hidden_field_tag
|
||||
back_url = params[:back_url] || request.env['HTTP_REFERER']
|
||||
back_url = CGI.unescape(back_url.to_s)
|
||||
hidden_field_tag('back_url', CGI.escape(back_url), :id => nil) unless back_url.blank?
|
||||
hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
|
||||
end
|
||||
|
||||
def check_all_links(form_name)
|
||||
|
@ -868,10 +788,12 @@ module ApplicationHelper
|
|||
pcts << (100 - pcts[1] - pcts[0])
|
||||
width = options[:width] || '100px;'
|
||||
legend = options[:legend] || ''
|
||||
content_tag('div',
|
||||
content_tag('div', '', :style => "width: #{pcts[0]}%;", :class => 'closed ui-progressbar-value ui-widget-header ui-corner-left') +
|
||||
content_tag('div', '', :style => "width: #{pcts[1]}%;", :class => 'done ui-progressbar-value ui-widget-header'),
|
||||
:class => 'progress ui-progressbar ui-widget ui-widget-content ui-corner-all', :style => "width: #{width};") +
|
||||
content_tag('table',
|
||||
content_tag('tr',
|
||||
(pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : '') +
|
||||
(pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : '') +
|
||||
(pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : '')
|
||||
), :class => 'progress', :style => "width: #{width};") +
|
||||
content_tag('p', legend, :class => 'pourcent')
|
||||
end
|
||||
|
||||
|
@ -884,7 +806,7 @@ module ApplicationHelper
|
|||
def context_menu(url)
|
||||
unless @context_menu_included
|
||||
content_for :header_tags do
|
||||
javascript_include_tag('context_menu.jquery') +
|
||||
javascript_include_tag('context_menu') +
|
||||
stylesheet_link_tag('context_menu')
|
||||
end
|
||||
if l(:direction) == 'rtl'
|
||||
|
@ -894,7 +816,7 @@ module ApplicationHelper
|
|||
end
|
||||
@context_menu_included = true
|
||||
end
|
||||
javascript_tag "jQuery(document).ContextMenu('#{ url_for(url) }')"
|
||||
javascript_tag "new ContextMenu('#{ url_for(url) }')"
|
||||
end
|
||||
|
||||
def context_menu_link(name, url, options={})
|
||||
|
@ -914,25 +836,33 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def calendar_for(field_id)
|
||||
javascript_tag("jQuery('##{field_id}').datepicker(datepickerSettings)")
|
||||
include_calendar_headers_tags
|
||||
image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
|
||||
javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
|
||||
end
|
||||
|
||||
def jquery_datepicker_settings
|
||||
start_of_week = Setting.start_of_week.to_s
|
||||
start_of_week_string = start_of_week.present? ? "firstDay: '#{start_of_week}', " : ''
|
||||
script = javascript_tag("var datepickerSettings = {" +
|
||||
start_of_week_string +
|
||||
"showOn: 'both', " +
|
||||
"buttonImage: '" + path_to_image('/images/calendar.png') + "', " +
|
||||
"buttonImageOnly: true, " +
|
||||
"showButtonPanel: true, " +
|
||||
"dateFormat: 'yy-mm-dd' " +
|
||||
"}")
|
||||
unless current_language == :en
|
||||
jquery_locale = l("jquery.ui", :default => current_language.to_s)
|
||||
script << javascript_include_tag("libs/ui/i18n/jquery.ui.datepicker-#{jquery_locale}.js")
|
||||
def include_calendar_headers_tags
|
||||
unless @calendar_headers_tags_included
|
||||
@calendar_headers_tags_included = true
|
||||
content_for :header_tags do
|
||||
start_of_week = case Setting.start_of_week.to_i
|
||||
when 1
|
||||
'Calendar._FD = 1;' # Monday
|
||||
when 7
|
||||
'Calendar._FD = 0;' # Sunday
|
||||
when 6
|
||||
'Calendar._FD = 6;' # Saturday
|
||||
else
|
||||
'' # use language
|
||||
end
|
||||
|
||||
javascript_include_tag('calendar/calendar') +
|
||||
javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
|
||||
javascript_tag(start_of_week) +
|
||||
javascript_include_tag('calendar/calendar-setup') +
|
||||
stylesheet_link_tag('calendar')
|
||||
end
|
||||
end
|
||||
script
|
||||
end
|
||||
|
||||
def content_for(name, content = nil, &block)
|
||||
|
@ -945,29 +875,10 @@ module ApplicationHelper
|
|||
(@has_content && @has_content[name]) || false
|
||||
end
|
||||
|
||||
# Returns the gravatar image tag for the given email
|
||||
# +email+ is a string with an email address
|
||||
def gravatar(email, options={})
|
||||
gravatarify_options = {}
|
||||
gravatarify_options[:secure] = options.delete :ssl
|
||||
[:default, :size, :rating, :filetype].each {|key| gravatarify_options[key] = options.delete key}
|
||||
# Default size is 50x50 px
|
||||
gravatarify_options[:size] ||= 50
|
||||
options[:class] ||= 'gravatar'
|
||||
gravatarify_options[:html] = options
|
||||
gravatar_tag email, gravatarify_options
|
||||
end
|
||||
|
||||
# Returns the avatar image tag for the given +user+ if avatars are enabled
|
||||
# +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
|
||||
def avatar(user, options = { })
|
||||
if Setting.gravatar_enabled?
|
||||
if user.is_a?(Group)
|
||||
size = options[:size] || 50
|
||||
size = "#{size}x#{size}" # image_tag uses WxH
|
||||
options[:class] ||= 'gravatar'
|
||||
return image_tag("group.png", options.merge(:size => size))
|
||||
end
|
||||
options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
|
||||
email = nil
|
||||
if user.respond_to?(:mail)
|
||||
|
@ -987,7 +898,6 @@ module ApplicationHelper
|
|||
unless User.current.pref.warn_on_leaving_unsaved == '0'
|
||||
tags << "\n" + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
|
||||
end
|
||||
tags << jquery_datepicker_settings
|
||||
tags
|
||||
end
|
||||
|
||||
|
@ -1025,72 +935,6 @@ module ApplicationHelper
|
|||
end
|
||||
end
|
||||
|
||||
# Expands the current menu item using JavaScript based on the params
|
||||
def expand_current_menu
|
||||
current_menu_class =
|
||||
case
|
||||
when params[:controller] == "timelog"
|
||||
"reports"
|
||||
when params[:controller] == 'projects' && params[:action] == 'changelog'
|
||||
"reports"
|
||||
when params[:controller] == 'issues' && ['calendar','gantt'].include?(params[:action])
|
||||
"reports"
|
||||
when params[:controller] == 'projects' && params[:action] == 'roadmap'
|
||||
'roadmap'
|
||||
when params[:controller] == 'versions' && params[:action] == 'show'
|
||||
'roadmap'
|
||||
when params[:controller] == 'projects' && params[:action] == 'settings'
|
||||
'settings'
|
||||
when params[:controller] == 'contracts' || params[:controller] == 'deliverables'
|
||||
'contracts'
|
||||
else
|
||||
params[:controller]
|
||||
end
|
||||
|
||||
|
||||
javascript_tag("jQuery.menu_expand({ menuItem: '.#{current_menu_class}' });")
|
||||
end
|
||||
|
||||
# Menu items for the main top menu
|
||||
def main_top_menu_items
|
||||
split_top_menu_into_main_or_more_menus[:main]
|
||||
end
|
||||
|
||||
# Menu items for the more top menu
|
||||
def more_top_menu_items
|
||||
split_top_menu_into_main_or_more_menus[:more]
|
||||
end
|
||||
|
||||
def help_menu_item
|
||||
split_top_menu_into_main_or_more_menus[:help]
|
||||
end
|
||||
|
||||
# Split the :top_menu into separate :main and :more items
|
||||
def split_top_menu_into_main_or_more_menus
|
||||
unless @top_menu_split
|
||||
items_for_main_level = []
|
||||
items_for_more_level = []
|
||||
help_menu = nil
|
||||
menu_items_for(:top_menu) do |item|
|
||||
if item.name == :home || item.name == :my_page
|
||||
items_for_main_level << item
|
||||
elsif item.name == :help
|
||||
help_menu = item
|
||||
elsif item.name == :projects
|
||||
# Remove, present in layout
|
||||
else
|
||||
items_for_more_level << item
|
||||
end
|
||||
end
|
||||
@top_menu_split = {
|
||||
:main => items_for_main_level,
|
||||
:more => items_for_more_level,
|
||||
:help => help_menu
|
||||
}
|
||||
end
|
||||
@top_menu_split
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def wiki_helper
|
||||
|
@ -1102,20 +946,4 @@ module ApplicationHelper
|
|||
def link_to_content_update(text, url_params = {}, html_options = {})
|
||||
link_to(text, url_params, html_options)
|
||||
end
|
||||
|
||||
def get_view_instance_variables_for_liquid
|
||||
internal_variables = %w{
|
||||
@output_buffer @cookies @helpers @real_format @assigns_added @assigns
|
||||
@view_paths @controller
|
||||
}
|
||||
self.instance_variables.collect(&:to_s).reject do |ivar|
|
||||
ivar.match(/^@_/) || # Rails "internal" variables: @_foo
|
||||
ivar.match(/^@template/) ||
|
||||
internal_variables.include?(ivar)
|
||||
end.inject({}) do |acc,ivar|
|
||||
acc[ivar.sub('@','')] = instance_variable_get(ivar)
|
||||
acc
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -36,7 +36,8 @@ module CustomFieldsHelper
|
|||
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
|
||||
case field_format.try(:edit_as)
|
||||
when "date"
|
||||
date_field_tag(field_name, custom_value.value, :id => field_id, :size => 10)
|
||||
text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
|
||||
calendar_for(field_id)
|
||||
when "text"
|
||||
text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
|
||||
when "bool"
|
||||
|
@ -70,7 +71,8 @@ module CustomFieldsHelper
|
|||
field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
|
||||
case field_format.try(:edit_as)
|
||||
when "date"
|
||||
date_field_tag(field_name, '', :id => field_id, :size => 10)
|
||||
text_field_tag(field_name, '', :id => field_id, :size => 10) +
|
||||
calendar_for(field_id)
|
||||
when "text"
|
||||
text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
|
||||
when "bool"
|
||||
|
|
|
@ -52,14 +52,13 @@ module IssuesHelper
|
|||
"<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}"
|
||||
end
|
||||
|
||||
# TODO: deprecate and/or remove
|
||||
def render_issue_subject_with_tree(issue)
|
||||
s = ''
|
||||
ancestors = issue.root? ? [] : issue.ancestors.all
|
||||
ancestors.each do |ancestor|
|
||||
s << '<div>' + content_tag('h2', link_to_issue(ancestor))
|
||||
s << '<div>' + content_tag('p', link_to_issue(ancestor))
|
||||
end
|
||||
s << '<div class="subject">' + content_tag('h2', h(issue.subject))
|
||||
s << '<div>' + content_tag('h3', h(issue.subject))
|
||||
s << '</div>' * (ancestors.size + 1)
|
||||
s
|
||||
end
|
||||
|
@ -79,22 +78,6 @@ module IssuesHelper
|
|||
s
|
||||
end
|
||||
|
||||
def render_parents_and_subtree(issue)
|
||||
return if issue.leaf? && !issue.parent
|
||||
s = '<form><table id="issue_tree" class="list">'
|
||||
issue_list(issue.self_and_ancestors.sort_by(&:lft) + issue.descendants.sort_by(&:lft)) do |el, level|
|
||||
s << content_tag('tr',
|
||||
content_tag('td', check_box_tag("ids[]", el.id, false, :id => nil), :class => 'checkbox') +
|
||||
content_tag('td', link_to_issue(el, :truncate => 60), :class => 'subject') +
|
||||
content_tag('td', h(el.status)) +
|
||||
content_tag('td', link_to_user(el.assigned_to)) +
|
||||
content_tag('td', progress_bar(el.done_ratio, :width => '80px')),
|
||||
:class => "issue issue-#{el.id} #{"self" if el == issue} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
|
||||
end
|
||||
s << '</table></form>'
|
||||
s
|
||||
end
|
||||
|
||||
def render_custom_fields_rows(issue)
|
||||
return if issue.custom_field_values.empty?
|
||||
ordered_values = []
|
||||
|
|
|
@ -27,34 +27,24 @@ module JournalsHelper
|
|||
|
||||
def render_journal(model, journal, options = {})
|
||||
return "" if journal.initial?
|
||||
|
||||
journal_classes = journal.css_classes
|
||||
journal_content = render_journal_details(journal, :label_updated_time_by, model, options)
|
||||
|
||||
avatar = avatar(journal.user, :size => "40")
|
||||
unless avatar.blank?
|
||||
profile_wrap = content_tag("div", avatar, {:class => "profile-wrap"}, false)
|
||||
journal_content = profile_wrap + journal_content
|
||||
journal_classes << " has-avatar"
|
||||
journal_content = render_journal_details(journal, :label_updated_time_by)
|
||||
journal_content += render_notes(model, journal, options) unless journal.notes.blank?
|
||||
content_tag "div", journal_content, { :id => "change-#{journal.id}", :class => journal.css_classes }
|
||||
end
|
||||
|
||||
content_tag("div", journal_content, :id => "change-#{journal.id}", :class => journal_classes)
|
||||
end
|
||||
|
||||
# This renders a journal entry with a header and details
|
||||
def render_journal_details(journal, header_label = :label_updated_time_by, model=nil, options={})
|
||||
# This renders a journal entry wiht a header and details
|
||||
def render_journal_details(journal, header_label = :label_updated_time_by)
|
||||
header = <<-HTML
|
||||
<h4>
|
||||
<div class="journal-link" style="float:right;">#{link_to "##{journal.anchor}", :anchor => "note-#{journal.anchor}"}</div>
|
||||
#{authoring journal.created_at, journal.user, :label => header_label}
|
||||
<div style="float:right;">#{link_to "##{journal.anchor}", :anchor => "note-#{journal.anchor}"}</div>
|
||||
#{avatar(journal.user, :size => "24")}
|
||||
#{content_tag('a', '', :name => "note-#{journal.anchor}")}
|
||||
#{authoring journal.created_at, journal.user, :label => header_label}
|
||||
</h4>
|
||||
HTML
|
||||
|
||||
header << render_notes(model, journal, options) unless journal.notes.blank?
|
||||
|
||||
if journal.details.any?
|
||||
details = content_tag "ul", :class => "journal-attributes details" do
|
||||
details = content_tag "ul", :class => "details" do
|
||||
journal.details.collect do |detail|
|
||||
if d = journal.render_detail(detail)
|
||||
content_tag("li", d)
|
||||
|
@ -63,7 +53,7 @@ module JournalsHelper
|
|||
end
|
||||
end
|
||||
|
||||
content_tag "div", "#{header}#{details}", :class => "journal-details"
|
||||
content_tag("div", "#{header}#{details}", :id => "change-#{journal.id}", :class => "journal")
|
||||
end
|
||||
|
||||
def render_notes(model, journal, options={})
|
||||
|
|
|
@ -84,12 +84,11 @@ module QueriesHelper
|
|||
end
|
||||
end
|
||||
@query.group_by = params[:group_by]
|
||||
@query.display_subprojects = params[:display_subprojects] if params[:display_subprojects]
|
||||
@query.column_names = params[:c] || (params[:query] && params[:query][:column_names])
|
||||
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names, :display_subprojects => @query.display_subprojects}
|
||||
session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
|
||||
else
|
||||
@query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
|
||||
@query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names], :display_subprojects => session[:query][:display_subprojects])
|
||||
@query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
|
||||
@query.project = @project
|
||||
end
|
||||
end
|
||||
|
|
|
@ -89,12 +89,12 @@ class Changeset < ActiveRecord::Base
|
|||
# Attribute reader for committer that encodes the committer string to
|
||||
# the repository log encoding (e.g. UTF-8)
|
||||
def committer
|
||||
self.class.to_utf8(read_attribute(:committer), repository_encoding)
|
||||
self.class.to_utf8(read_attribute(:committer), repository.repo_log_encoding)
|
||||
end
|
||||
|
||||
def before_create
|
||||
self.committer = self.class.to_utf8(self.committer, repository_encoding)
|
||||
self.comments = self.class.normalize_comments(self.comments, repository_encoding)
|
||||
self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding)
|
||||
self.comments = self.class.normalize_comments(self.comments, repository.repo_log_encoding)
|
||||
self.user = repository.find_committer_user(self.committer)
|
||||
end
|
||||
|
||||
|
|
|
@ -13,11 +13,8 @@
|
|||
#++
|
||||
|
||||
class Comment < ActiveRecord::Base
|
||||
include Redmine::SafeAttributes
|
||||
belongs_to :commented, :polymorphic => true, :counter_cache => true
|
||||
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
|
||||
|
||||
validates_presence_of :commented, :author, :comments
|
||||
|
||||
safe_attributes 'comments'
|
||||
end
|
||||
|
|
|
@ -25,7 +25,6 @@ class Document < ActiveRecord::Base
|
|||
end)
|
||||
|
||||
acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
|
||||
acts_as_watchable
|
||||
|
||||
validates_presence_of :project, :title, :category
|
||||
validates_length_of :title, :maximum => 60
|
||||
|
@ -41,9 +40,7 @@ class Document < ActiveRecord::Base
|
|||
|
||||
def after_initialize
|
||||
if new_record?
|
||||
# FIXME: on Rails 3 use this instead
|
||||
# self.category ||= DocumentCategory.default
|
||||
self.category_id = DocumentCategory.default.id if self.category_id == 0
|
||||
self.category ||= DocumentCategory.default
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -54,10 +51,4 @@ class Document < ActiveRecord::Base
|
|||
end
|
||||
@updated_on
|
||||
end
|
||||
|
||||
def recipients
|
||||
mails = super # from acts_as_event
|
||||
mails += watcher_recipients
|
||||
mails.uniq
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,10 +14,6 @@
|
|||
|
||||
class DocumentObserver < ActiveRecord::Observer
|
||||
def after_create(document)
|
||||
if Setting.notified_events.include?('document_added')
|
||||
document.recipients.each do |recipient|
|
||||
Mailer.deliver_document_added(document, recipient)
|
||||
end
|
||||
end
|
||||
Mailer.deliver_document_added(document) if Setting.notified_events.include?('document_added')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,11 +22,6 @@ class Group < Principal
|
|||
validates_uniqueness_of :lastname, :case_sensitive => false
|
||||
validates_length_of :lastname, :maximum => 30
|
||||
|
||||
# Returns an array of all of the email addresses of the group's users
|
||||
def mails
|
||||
users.collect(&:mail)
|
||||
end
|
||||
|
||||
def to_s
|
||||
lastname.to_s
|
||||
end
|
||||
|
@ -48,9 +43,4 @@ class Group < Principal
|
|||
:conditions => ["#{Member.table_name}.user_id = ? AND #{MemberRole.table_name}.inherited_from IN (?)", user.id, member.member_role_ids]).each(&:destroy)
|
||||
end
|
||||
end
|
||||
|
||||
def self.human_attribute_name(attribute_name)
|
||||
attribute_name = "name" if attribute_name == "lastname"
|
||||
super(attribute_name)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -103,10 +103,6 @@ class Issue < ActiveRecord::Base
|
|||
(usr || User.current).allowed_to?(:view_issues, self.project)
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
IssueDrop.new(self)
|
||||
end
|
||||
|
||||
def after_initialize
|
||||
if new_record?
|
||||
# set default values for new records only
|
||||
|
@ -368,7 +364,7 @@ class Issue < ActiveRecord::Base
|
|||
def attachment_removed(obj)
|
||||
init_journal(User.current)
|
||||
create_journal
|
||||
last_journal.update_attribute(:changes, {"attachments_" + obj.id.to_s => [obj.filename, nil]})
|
||||
last_journal.update_attribute(:changes, {"attachments_" + obj.id.to_s => [obj.filename, nil]}.to_yaml)
|
||||
end
|
||||
|
||||
# Return true if the issue is closed, otherwise false
|
||||
|
@ -707,15 +703,6 @@ class Issue < ActiveRecord::Base
|
|||
projects
|
||||
end
|
||||
|
||||
# Overrides Redmine::Acts::Journalized::Permissions
|
||||
#
|
||||
# The default assumption is that journals have the same permissions
|
||||
# as the journaled object, issue notes have separate permissions though
|
||||
def journal_editable_by?(journal, user)
|
||||
return true if journal.user == user && user.allowed_to?(:edit_own_issue_notes, project)
|
||||
user.allowed_to? :edit_issue_notes, project
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_nested_set_attributes
|
||||
|
|
|
@ -17,9 +17,7 @@ class IssueObserver < ActiveRecord::Observer
|
|||
|
||||
def after_create(issue)
|
||||
if self.send_notification
|
||||
(issue.recipients + issue.watcher_recipients).uniq.each do |recipient|
|
||||
Mailer.deliver_issue_add(issue, recipient)
|
||||
end
|
||||
Mailer.deliver_issue_add(issue) if Setting.notified_events.include?('issue_added')
|
||||
end
|
||||
clear_notification
|
||||
end
|
||||
|
|
|
@ -28,10 +28,6 @@ class IssueStatus < ActiveRecord::Base
|
|||
IssueStatus.update_all("is_default=#{connection.quoted_false}", ['id <> ?', id]) if self.is_default?
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
IssueStatusDrop.new(self)
|
||||
end
|
||||
|
||||
# Returns the default status for new issues
|
||||
def self.default
|
||||
find(:first, :conditions =>["is_default=?", true])
|
||||
|
|
|
@ -76,7 +76,7 @@ class Journal < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def editable_by?(user)
|
||||
journaled.journal_editable_by?(self, user)
|
||||
journaled.journal_editable_by?(user)
|
||||
end
|
||||
|
||||
def details
|
||||
|
|
|
@ -27,15 +27,11 @@ class JournalObserver < ActiveRecord::Observer
|
|||
|
||||
if journal.initial?
|
||||
if Setting.notified_events.include?('wiki_content_added')
|
||||
(wiki_content.recipients + wiki_page.wiki.watcher_recipients).uniq.each do |recipient|
|
||||
Mailer.deliver_wiki_content_added(wiki_content, recipient)
|
||||
end
|
||||
Mailer.deliver_wiki_content_added(wiki_content)
|
||||
end
|
||||
else
|
||||
if Setting.notified_events.include?('wiki_content_updated')
|
||||
(wiki_content.recipients + wiki_page.wiki.watcher_recipients + wiki_page.watcher_recipients).uniq.each do |recipient|
|
||||
Mailer.deliver_wiki_content_updated(wiki_content, recipient)
|
||||
end
|
||||
Mailer.deliver_wiki_content_updated(wiki_content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -47,10 +43,7 @@ class JournalObserver < ActiveRecord::Observer
|
|||
(Setting.notified_events.include?('issue_note_added') && journal.notes.present?) ||
|
||||
(Setting.notified_events.include?('issue_status_updated') && journal.new_status.present?) ||
|
||||
(Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?)
|
||||
issue = journal.issue
|
||||
(issue.recipients + issue.watcher_recipients).uniq.each do |recipient|
|
||||
Mailer.deliver_issue_edit(journal, recipient)
|
||||
end
|
||||
Mailer.deliver_issue_edit(journal)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -69,7 +69,6 @@ class MailHandler < ActionMailer::Base
|
|||
else
|
||||
# Default behaviour, emails from unknown users are ignored
|
||||
logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]" if logger && logger.info
|
||||
Mailer.deliver_mail_handler_unauthorized_action(user, email.subject.to_s, :to => sender_email) if Setting.mail_handler_confirmation_on_failure
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
@ -103,15 +102,12 @@ class MailHandler < ActionMailer::Base
|
|||
rescue ActiveRecord::RecordInvalid => e
|
||||
# TODO: send a email to the user
|
||||
logger.error e.message if logger
|
||||
Mailer.deliver_mail_handler_missing_information(user, email.subject.to_s, e.message) if Setting.mail_handler_confirmation_on_failure
|
||||
false
|
||||
rescue MissingInformation => e
|
||||
logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
|
||||
Mailer.deliver_mail_handler_missing_information(user, email.subject.to_s, e.message) if Setting.mail_handler_confirmation_on_failure
|
||||
false
|
||||
rescue UnauthorizedAction => e
|
||||
logger.error "MailHandler: unauthorized attempt from #{user}" if logger
|
||||
Mailer.deliver_mail_handler_unauthorized_action(user, email.subject.to_s) if Setting.mail_handler_confirmation_on_failure
|
||||
false
|
||||
end
|
||||
|
||||
|
@ -145,7 +141,6 @@ class MailHandler < ActionMailer::Base
|
|||
issue.save!
|
||||
add_attachments(issue)
|
||||
logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
|
||||
Mailer.deliver_mail_handler_confirmation(issue, user, issue.subject) if Setting.mail_handler_confirmation_on_success
|
||||
issue
|
||||
end
|
||||
|
||||
|
@ -167,7 +162,6 @@ class MailHandler < ActionMailer::Base
|
|||
add_attachments(issue)
|
||||
issue.save!
|
||||
logger.info "MailHandler: issue ##{issue.id} updated by #{user}" if logger && logger.info
|
||||
Mailer.deliver_mail_handler_confirmation(issue.last_journal, user, email.subject) if Setting.mail_handler_confirmation_on_success
|
||||
issue.last_journal
|
||||
end
|
||||
|
||||
|
@ -196,7 +190,6 @@ class MailHandler < ActionMailer::Base
|
|||
reply.board = message.board
|
||||
message.children << reply
|
||||
add_attachments(reply)
|
||||
Mailer.deliver_mail_handler_confirmation(message, user, reply.subject) if Setting.mail_handler_confirmation_on_success
|
||||
reply
|
||||
else
|
||||
logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic" if logger && logger.info
|
||||
|
|
|
@ -30,19 +30,20 @@ class Mailer < ActionMailer::Base
|
|||
{ :host => h, :protocol => Setting.protocol }
|
||||
end
|
||||
|
||||
# Builds a tmail object used to email a recipient of the added issue.
|
||||
# Builds a tmail object used to email recipients of the added issue.
|
||||
#
|
||||
# Example:
|
||||
# issue_add(issue, 'user@example.com') => tmail object
|
||||
# Mailer.deliver_issue_add(issue, 'user@example.com') => sends an email to 'user@example.com'
|
||||
def issue_add(issue, recipient)
|
||||
# issue_add(issue) => tmail object
|
||||
# Mailer.deliver_issue_add(issue) => sends an email to issue recipients
|
||||
def issue_add(issue)
|
||||
redmine_headers 'Project' => issue.project.identifier,
|
||||
'Issue-Id' => issue.id,
|
||||
'Issue-Author' => issue.author.login,
|
||||
'Type' => "Issue"
|
||||
redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
|
||||
message_id issue
|
||||
recipients [recipient]
|
||||
recipients issue.recipients
|
||||
cc(issue.watcher_recipients - @recipients)
|
||||
subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
|
||||
body :issue => issue,
|
||||
:issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
|
||||
|
@ -52,9 +53,9 @@ class Mailer < ActionMailer::Base
|
|||
# Builds a tmail object used to email recipients of the edited issue.
|
||||
#
|
||||
# Example:
|
||||
# issue_edit(journal, 'user@example.com') => tmail object
|
||||
# Mailer.deliver_issue_edit(journal, 'user@example.com') => sends an email to issue recipients
|
||||
def issue_edit(journal, recipient)
|
||||
# issue_edit(journal) => tmail object
|
||||
# Mailer.deliver_issue_edit(journal) => sends an email to issue recipients
|
||||
def issue_edit(journal)
|
||||
issue = journal.journaled.reload
|
||||
redmine_headers 'Project' => issue.project.identifier,
|
||||
'Issue-Id' => issue.id,
|
||||
|
@ -64,7 +65,9 @@ class Mailer < ActionMailer::Base
|
|||
message_id journal
|
||||
references issue
|
||||
@author = journal.user
|
||||
recipients [recipient]
|
||||
recipients issue.recipients
|
||||
# Watchers in cc
|
||||
cc(issue.watcher_recipients - @recipients)
|
||||
s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] "
|
||||
s << "(#{issue.status.name}) " if journal.details['status_id']
|
||||
s << issue.subject
|
||||
|
@ -90,12 +93,12 @@ class Mailer < ActionMailer::Base
|
|||
# Builds a tmail object used to email users belonging to the added document's project.
|
||||
#
|
||||
# Example:
|
||||
# document_added(document, 'test@example.com') => tmail object
|
||||
# Mailer.deliver_document_added(document, 'test@example.com') => sends an email to the document's project recipients
|
||||
def document_added(document, recipient)
|
||||
# document_added(document) => tmail object
|
||||
# Mailer.deliver_document_added(document) => sends an email to the document's project recipients
|
||||
def document_added(document)
|
||||
redmine_headers 'Project' => document.project.identifier,
|
||||
'Type' => "Document"
|
||||
recipients [recipient]
|
||||
recipients document.recipients
|
||||
subject "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}"
|
||||
body :document => document,
|
||||
:document_url => url_for(:controller => 'documents', :action => 'show', :id => document)
|
||||
|
@ -107,7 +110,7 @@ class Mailer < ActionMailer::Base
|
|||
# Example:
|
||||
# attachments_added(attachments) => tmail object
|
||||
# Mailer.deliver_attachments_added(attachments) => sends an email to the project's recipients
|
||||
def attachments_added(attachments, recipient)
|
||||
def attachments_added(attachments)
|
||||
container = attachments.first.container
|
||||
added_to = ''
|
||||
added_to_url = ''
|
||||
|
@ -115,14 +118,16 @@ class Mailer < ActionMailer::Base
|
|||
when 'Project'
|
||||
added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container)
|
||||
added_to = "#{l(:label_project)}: #{container}"
|
||||
recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail}
|
||||
when 'Version'
|
||||
added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container.project)
|
||||
added_to = "#{l(:label_version)}: #{container.name}"
|
||||
recipients container.project.notified_users.select {|user| user.allowed_to?(:view_files, container.project)}.collect {|u| u.mail}
|
||||
when 'Document'
|
||||
added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id)
|
||||
added_to = "#{l(:label_document)}: #{container.title}"
|
||||
recipients container.recipients
|
||||
end
|
||||
recipients [recipient]
|
||||
redmine_headers 'Project' => container.project.identifier,
|
||||
'Type' => "Attachment"
|
||||
subject "[#{container.project.name}] #{l(:label_attachment_new)}"
|
||||
|
@ -137,11 +142,11 @@ class Mailer < ActionMailer::Base
|
|||
# Example:
|
||||
# news_added(news) => tmail object
|
||||
# Mailer.deliver_news_added(news) => sends an email to the news' project recipients
|
||||
def news_added(news, recipient)
|
||||
def news_added(news)
|
||||
redmine_headers 'Project' => news.project.identifier,
|
||||
'Type' => "News"
|
||||
message_id news
|
||||
recipients [recipient]
|
||||
recipients news.recipients
|
||||
subject "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
|
||||
body :news => news,
|
||||
:news_url => url_for(:controller => 'news', :action => 'show', :id => news)
|
||||
|
@ -171,13 +176,14 @@ class Mailer < ActionMailer::Base
|
|||
# Example:
|
||||
# message_posted(message) => tmail object
|
||||
# Mailer.deliver_message_posted(message) => sends an email to the recipients
|
||||
def message_posted(message, recipient)
|
||||
def message_posted(message)
|
||||
redmine_headers 'Project' => message.project.identifier,
|
||||
'Topic-Id' => (message.parent_id || message.id),
|
||||
'Type' => "Forum"
|
||||
message_id message
|
||||
references message.parent unless message.parent.nil?
|
||||
recipients [recipient]
|
||||
recipients(message.recipients)
|
||||
cc((message.root.watcher_recipients + message.board.watcher_recipients).uniq - @recipients)
|
||||
subject "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}"
|
||||
body :message => message,
|
||||
:message_url => url_for({ :controller => 'messages', :action => 'show', :board_id => message.board, :id => message.root, :r => message, :anchor => "message-#{message.id}" })
|
||||
|
@ -189,12 +195,13 @@ class Mailer < ActionMailer::Base
|
|||
# Example:
|
||||
# wiki_content_added(wiki_content) => tmail object
|
||||
# Mailer.deliver_wiki_content_added(wiki_content) => sends an email to the project's recipients
|
||||
def wiki_content_added(wiki_content, recipient)
|
||||
def wiki_content_added(wiki_content)
|
||||
redmine_headers 'Project' => wiki_content.project.identifier,
|
||||
'Wiki-Page-Id' => wiki_content.page.id,
|
||||
'Type' => "Wiki"
|
||||
message_id wiki_content
|
||||
recipients [recipient]
|
||||
recipients wiki_content.recipients
|
||||
cc(wiki_content.page.wiki.watcher_recipients - recipients)
|
||||
subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :id => wiki_content.page.pretty_title)}"
|
||||
body :wiki_content => wiki_content,
|
||||
:wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title)
|
||||
|
@ -206,12 +213,13 @@ class Mailer < ActionMailer::Base
|
|||
# Example:
|
||||
# wiki_content_updated(wiki_content) => tmail object
|
||||
# Mailer.deliver_wiki_content_updated(wiki_content) => sends an email to the project's recipients
|
||||
def wiki_content_updated(wiki_content, recipient)
|
||||
def wiki_content_updated(wiki_content)
|
||||
redmine_headers 'Project' => wiki_content.project.identifier,
|
||||
'Wiki-Page-Id' => wiki_content.page.id,
|
||||
'Type' => "Wiki"
|
||||
message_id wiki_content
|
||||
recipients [recipient]
|
||||
recipients wiki_content.recipients
|
||||
cc(wiki_content.page.wiki.watcher_recipients + wiki_content.page.watcher_recipients - recipients)
|
||||
subject "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_updated, :id => wiki_content.page.pretty_title)}"
|
||||
body :wiki_content => wiki_content,
|
||||
:wiki_content_url => url_for(:controller => 'wiki', :action => 'show', :project_id => wiki_content.project, :id => wiki_content.page.title),
|
||||
|
@ -285,44 +293,6 @@ class Mailer < ActionMailer::Base
|
|||
render_multipart('register', body)
|
||||
end
|
||||
|
||||
def mail_handler_confirmation(object, user, email_subject)
|
||||
recipients user.mail
|
||||
|
||||
case
|
||||
when object.is_a?(Issue)
|
||||
project = object.project.name
|
||||
url = url_for(:controller => 'issues', :action => 'show', :id => object.id)
|
||||
when object.is_a?(Journal)
|
||||
project = object.project.name
|
||||
url = url_for(:controller => 'issues', :action => 'show', :id => object.issue.id)
|
||||
when object.class == Message
|
||||
project = object.project.name
|
||||
url = url_for(object.event_url)
|
||||
else
|
||||
project = ''
|
||||
url = ''
|
||||
end
|
||||
|
||||
subject "[#{project}] #{l(:label_mail_handler_confirmation, :subject => email_subject)}"
|
||||
body(:object => object,
|
||||
:url => url)
|
||||
render_multipart('mail_handler_confirmation', body)
|
||||
end
|
||||
|
||||
def mail_handler_unauthorized_action(user, email_subject, options={})
|
||||
recipients options[:to] || user.mail
|
||||
subject l(:label_mail_handler_failure, :subject => email_subject)
|
||||
body({})
|
||||
render_multipart('mail_handler_unauthorized_action', body)
|
||||
end
|
||||
|
||||
def mail_handler_missing_information(user, email_subject, error_message)
|
||||
recipients user.mail
|
||||
subject l(:label_mail_handler_failure, :subject => email_subject)
|
||||
body({:errors => error_message.to_s})
|
||||
render_multipart('mail_handler_missing_information', body)
|
||||
end
|
||||
|
||||
def test(user)
|
||||
redmine_headers 'Type' => "Test"
|
||||
set_language_if_valid(user.language)
|
||||
|
@ -425,7 +395,7 @@ class Mailer < ActionMailer::Base
|
|||
# Removes the current user from the recipients and cc
|
||||
# if he doesn't want to receive notifications about what he does
|
||||
@author ||= User.current
|
||||
if @author && @author.mail && @author.pref[:no_self_notified]
|
||||
if @author.pref[:no_self_notified]
|
||||
recipients((recipients.is_a?(Array) ? recipients : [recipients]) - [@author.mail]) if recipients.present?
|
||||
cc((cc.is_a?(Array) ? cc : [cc]) - [@author.mail]) if cc.present?
|
||||
end
|
||||
|
@ -433,6 +403,13 @@ class Mailer < ActionMailer::Base
|
|||
notified_users = [recipients, cc].flatten.compact.uniq
|
||||
# Rails would log recipients only, not cc and bcc
|
||||
mylogger.info "Sending email notification to: #{notified_users.join(', ')}" if mylogger
|
||||
|
||||
# Blind carbon copy recipients
|
||||
if Setting.bcc_recipients?
|
||||
bcc(notified_users)
|
||||
recipients []
|
||||
cc []
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ class Message < ActiveRecord::Base
|
|||
|
||||
acts_as_watchable
|
||||
|
||||
attr_protected :locked, :sticky
|
||||
validates_presence_of :board, :subject, :content
|
||||
validates_length_of :subject, :maximum => 255
|
||||
|
||||
|
@ -50,7 +51,7 @@ class Message < ActiveRecord::Base
|
|||
:conditions => Project.allowed_to_condition(args.first || User.current, :view_messages) } }
|
||||
|
||||
safe_attributes 'subject', 'content'
|
||||
safe_attributes 'locked', 'sticky', 'board_id',
|
||||
safe_attributes 'locked', 'sticky',
|
||||
:if => lambda {|message, user|
|
||||
user.allowed_to?(:edit_messages, message.project)
|
||||
}
|
||||
|
@ -80,15 +81,9 @@ class Message < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def after_destroy
|
||||
parent.reset_last_reply_id! if parent
|
||||
board.reset_counters!
|
||||
end
|
||||
|
||||
def reset_last_reply_id!
|
||||
clid = children.present? ? children.last.id : nil
|
||||
self.update_attribute(:last_reply_id, clid)
|
||||
end
|
||||
|
||||
def sticky=(arg)
|
||||
write_attribute :sticky, (arg == true || arg.to_s == '1' ? 1 : 0)
|
||||
end
|
||||
|
|
|
@ -14,13 +14,6 @@
|
|||
|
||||
class MessageObserver < ActiveRecord::Observer
|
||||
def after_create(message)
|
||||
if Setting.notified_events.include?('message_posted')
|
||||
recipients = message.recipients
|
||||
recipients += message.root.watcher_recipients
|
||||
recipients += message.board.watcher_recipients
|
||||
recipients.uniq.each do |recipient|
|
||||
Mailer.deliver_message_posted(message, recipient)
|
||||
end
|
||||
end
|
||||
Mailer.deliver_message_posted(message) if Setting.notified_events.include?('message_posted')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,8 +22,7 @@ class News < ActiveRecord::Base
|
|||
validates_length_of :title, :maximum => 60
|
||||
validates_length_of :summary, :maximum => 255
|
||||
|
||||
acts_as_journalized :event_url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.journaled_id} },
|
||||
:event_description => :description
|
||||
acts_as_journalized :event_url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.journaled_id} }
|
||||
acts_as_searchable :columns => ["#{table_name}.title", "#{table_name}.summary", "#{table_name}.description"], :include => :project
|
||||
acts_as_watchable
|
||||
|
||||
|
@ -40,11 +39,6 @@ class News < ActiveRecord::Base
|
|||
!user.nil? && user.allowed_to?(:view_news, project)
|
||||
end
|
||||
|
||||
# Returns true if the news can be commented by user
|
||||
def commentable?(user=User.current)
|
||||
user.allowed_to?(:comment_news, project)
|
||||
end
|
||||
|
||||
# returns latest news for projects visible by user
|
||||
def self.latest(user = User.current, count = 5)
|
||||
find(:all, :limit => count, :conditions => Project.allowed_to_condition(user, :view_news), :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
|
||||
|
|
|
@ -14,10 +14,6 @@
|
|||
|
||||
class NewsObserver < ActiveRecord::Observer
|
||||
def after_create(news)
|
||||
if Setting.notified_events.include?('news_added')
|
||||
news.recipients.each do |recipient|
|
||||
Mailer.deliver_news_added(news, recipient)
|
||||
end
|
||||
end
|
||||
Mailer.deliver_news_added(news) if Setting.notified_events.include?('news_added')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,10 +31,6 @@ class Principal < ActiveRecord::Base
|
|||
|
||||
before_create :set_default_empty_values
|
||||
|
||||
def to_liquid
|
||||
PrincipalDrop.new(self)
|
||||
end
|
||||
|
||||
def name(formatter = nil)
|
||||
to_s
|
||||
end
|
||||
|
@ -48,82 +44,6 @@ class Principal < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def active?
|
||||
true
|
||||
end
|
||||
|
||||
def logged?
|
||||
true # TODO: should all principals default to logged or not?
|
||||
end
|
||||
|
||||
# Return true if the user is allowed to do the specified action on a specific context
|
||||
# Action can be:
|
||||
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
|
||||
# * a permission Symbol (eg. :edit_project)
|
||||
# Context can be:
|
||||
# * a project : returns true if user is allowed to do the specified action on this project
|
||||
# * a group of projects : returns true if user is allowed on every project
|
||||
# * nil with options[:global] set : check if user has at least one role allowed for this action,
|
||||
# or falls back to Non Member / Anonymous permissions depending if the user is logged
|
||||
def allowed_to?(action, context, options={})
|
||||
if context && context.is_a?(Project)
|
||||
# No action allowed on archived projects
|
||||
return false unless context.active?
|
||||
# No action allowed on disabled modules
|
||||
return false unless context.allows_to?(action)
|
||||
# Admin users are authorized for anything else
|
||||
return true if admin?
|
||||
|
||||
roles = roles_for_project(context)
|
||||
return false unless roles
|
||||
roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)}
|
||||
|
||||
elsif context && context.is_a?(Array)
|
||||
# Authorize if user is authorized on every element of the array
|
||||
context.map do |project|
|
||||
allowed_to?(action,project,options)
|
||||
end.inject do |memo,allowed|
|
||||
memo && allowed
|
||||
end
|
||||
elsif options[:global]
|
||||
# Admin users are always authorized
|
||||
return true if admin?
|
||||
|
||||
# authorize if user has at least one role that has this permission
|
||||
roles = memberships.collect {|m| m.roles}.flatten.uniq
|
||||
roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Is the user allowed to do the specified action on any project?
|
||||
# See allowed_to? for the actions and valid options.
|
||||
def allowed_to_globally?(action, options)
|
||||
allowed_to?(action, nil, options.reverse_merge(:global => true))
|
||||
end
|
||||
|
||||
# Return user's roles for project
|
||||
def roles_for_project(project)
|
||||
roles = []
|
||||
# No role on archived projects
|
||||
return roles unless project && project.active?
|
||||
if logged?
|
||||
# Find project membership
|
||||
membership = memberships.detect {|m| m.project_id == project.id}
|
||||
if membership
|
||||
roles = membership.roles
|
||||
else
|
||||
@role_non_member ||= Role.non_member
|
||||
roles << @role_non_member
|
||||
end
|
||||
else
|
||||
@role_anonymous ||= Role.anonymous
|
||||
roles << @role_anonymous
|
||||
end
|
||||
roles
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Make sure we don't try to insert NULL values (see #4632)
|
||||
|
|
|
@ -82,16 +82,6 @@ class Project < ActiveRecord::Base
|
|||
named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"}
|
||||
named_scope :all_public, { :conditions => { :is_public => true } }
|
||||
named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } }
|
||||
named_scope :like, lambda {|q|
|
||||
s = "%#{q.to_s.strip.downcase}%"
|
||||
{
|
||||
:conditions => ["LOWER(name) LIKE ?", s]
|
||||
}
|
||||
}
|
||||
|
||||
def to_liquid
|
||||
ProjectDrop.new(self)
|
||||
end
|
||||
|
||||
def initialize(attributes = nil)
|
||||
super
|
||||
|
@ -141,11 +131,6 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
# Is the project visible to the current user
|
||||
def visible?
|
||||
User.current.allowed_to?(:view_project, self)
|
||||
end
|
||||
|
||||
def self.allowed_to_condition(user, permission, options={})
|
||||
base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
|
||||
if perm = Redmine::AccessControl.permission(permission)
|
||||
|
@ -631,7 +616,7 @@ class Project < ActiveRecord::Base
|
|||
while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
|
||||
ancestors.pop
|
||||
end
|
||||
yield project, ancestors.size if block_given?
|
||||
yield project, ancestors.size
|
||||
ancestors << project
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,64 @@
|
|||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
class QueryColumn
|
||||
attr_accessor :name, :sortable, :groupable, :default_order
|
||||
include Redmine::I18n
|
||||
|
||||
def initialize(name, options={})
|
||||
self.name = name
|
||||
self.sortable = options[:sortable]
|
||||
self.groupable = options[:groupable] || false
|
||||
if groupable == true
|
||||
self.groupable = name.to_s
|
||||
end
|
||||
self.default_order = options[:default_order]
|
||||
@caption_key = options[:caption] || "field_#{name}"
|
||||
end
|
||||
|
||||
def caption
|
||||
l(@caption_key)
|
||||
end
|
||||
|
||||
# Returns true if the column is sortable, otherwise false
|
||||
def sortable?
|
||||
!sortable.nil?
|
||||
end
|
||||
|
||||
def value(issue)
|
||||
issue.send name
|
||||
end
|
||||
end
|
||||
|
||||
class QueryCustomFieldColumn < QueryColumn
|
||||
|
||||
def initialize(custom_field)
|
||||
self.name = "cf_#{custom_field.id}".to_sym
|
||||
self.sortable = custom_field.order_statement || false
|
||||
if %w(list date bool int).include?(custom_field.field_format)
|
||||
self.groupable = custom_field.order_statement
|
||||
end
|
||||
self.groupable ||= false
|
||||
@cf = custom_field
|
||||
end
|
||||
|
||||
def caption
|
||||
@cf.name
|
||||
end
|
||||
|
||||
def custom_field
|
||||
@cf
|
||||
end
|
||||
|
||||
def value(issue)
|
||||
cv = issue.custom_values.detect {|v| v.custom_field_id == @cf.id}
|
||||
cv && @cf.cast_value(cv.value)
|
||||
end
|
||||
end
|
||||
|
||||
class Query < ActiveRecord::Base
|
||||
class StatementInvalid < ::ActiveRecord::StatementInvalid
|
||||
end
|
||||
|
||||
belongs_to :project
|
||||
belongs_to :user
|
||||
|
@ -33,7 +90,6 @@ class Query < ActiveRecord::Base
|
|||
"*" => :label_all,
|
||||
">=" => :label_greater_or_equal,
|
||||
"<=" => :label_less_or_equal,
|
||||
"><" => :label_between,
|
||||
"<t+" => :label_in_less_than,
|
||||
">t+" => :label_in_more_than,
|
||||
"t+" => :label_in,
|
||||
|
@ -51,8 +107,8 @@ class Query < ActiveRecord::Base
|
|||
:list_status => [ "o", "=", "!", "c", "*" ],
|
||||
:list_optional => [ "=", "!", "!*", "*" ],
|
||||
:list_subprojects => [ "*", "!*", "=" ],
|
||||
:date => [ "=", ">=", "<=", "><", "<t+", ">t+", "t+", "t", "w", ">t-", "<t-", "t-" ],
|
||||
:date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "t-", "t", "w" ],
|
||||
:date => [ "<t+", ">t+", "t+", "t", "w", ">t-", "<t-", "t-" ],
|
||||
:date_past => [ ">t-", "<t-", "t-", "t", "w" ],
|
||||
:string => [ "=", "~", "!", "!~" ],
|
||||
:text => [ "~", "!~" ],
|
||||
:integer => [ "=", ">=", "<=", "!*", "*" ] }
|
||||
|
@ -83,7 +139,6 @@ class Query < ActiveRecord::Base
|
|||
def initialize(attributes = nil)
|
||||
super attributes
|
||||
self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
|
||||
self.display_subprojects ||= Setting.display_subprojects_issues?
|
||||
end
|
||||
|
||||
def after_initialize
|
||||
|
@ -190,7 +245,7 @@ class Query < ActiveRecord::Base
|
|||
|
||||
def add_filter(field, operator, values)
|
||||
# values must be an array
|
||||
return unless values.nil? || values.is_a?(Array)
|
||||
return unless values and values.is_a? Array # and !values.first.empty?
|
||||
# check if field is defined as an available filter
|
||||
if available_filters.has_key? field
|
||||
filter_options = available_filters[field]
|
||||
|
@ -199,7 +254,7 @@ class Query < ActiveRecord::Base
|
|||
# allowed_values = values & ([""] + (filter_options[:values] || []).collect {|val| val[1]})
|
||||
# filters[field] = {:operator => operator, :values => allowed_values } if (allowed_values.first and !allowed_values.first.empty?) or ["o", "c", "!*", "*", "t"].include? operator
|
||||
#end
|
||||
filters[field] = {:operator => operator, :values => (values || ['']) }
|
||||
filters[field] = {:operator => operator, :values => values }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -211,9 +266,9 @@ class Query < ActiveRecord::Base
|
|||
|
||||
# Add multiple filters using +add_filter+
|
||||
def add_filters(fields, operators, values)
|
||||
if fields.is_a?(Array) && operators.is_a?(Hash) && (values.nil? || values.is_a?(Hash))
|
||||
if fields.is_a?(Array) && operators.is_a?(Hash) && values.is_a?(Hash)
|
||||
fields.each do |field|
|
||||
add_filter(field, operators[field], values && values[field])
|
||||
add_filter(field, operators[field], values[field])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -222,10 +277,6 @@ class Query < ActiveRecord::Base
|
|||
filters and filters[field]
|
||||
end
|
||||
|
||||
def type_for(field)
|
||||
available_filters[field][:type] if available_filters.has_key?(field)
|
||||
end
|
||||
|
||||
def operator_for(field)
|
||||
has_filter?(field) ? filters[field][:operator] : nil
|
||||
end
|
||||
|
@ -234,10 +285,6 @@ class Query < ActiveRecord::Base
|
|||
has_filter?(field) ? filters[field][:values] : nil
|
||||
end
|
||||
|
||||
def value_for(field, index=0)
|
||||
(values_for(field) || [])[index]
|
||||
end
|
||||
|
||||
def label_for(field)
|
||||
label = available_filters[field][:name] if available_filters.has_key?(field)
|
||||
label ||= field.gsub(/\_id$/, "")
|
||||
|
@ -363,7 +410,7 @@ class Query < ActiveRecord::Base
|
|||
# all subprojects
|
||||
ids += project.descendants.collect(&:id)
|
||||
end
|
||||
elsif display_subprojects?
|
||||
elsif Setting.display_subprojects_issues?
|
||||
ids += project.descendants.collect(&:id)
|
||||
end
|
||||
project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',')
|
||||
|
@ -383,10 +430,8 @@ class Query < ActiveRecord::Base
|
|||
next unless v and !v.empty?
|
||||
operator = operator_for(field)
|
||||
|
||||
# "me" value substitution
|
||||
if %w(assigned_to_id author_id watcher_id).include?(field) ||
|
||||
# user custom fields
|
||||
available_filters.has_key?(field) && available_filters[field][:format] == 'user'
|
||||
# "me" value subsitution
|
||||
if %w(assigned_to_id author_id watcher_id).include?(field)
|
||||
v.push(User.current.logged? ? User.current.id.to_s : "0") if v.delete("me")
|
||||
end
|
||||
|
||||
|
@ -474,7 +519,7 @@ class Query < ActiveRecord::Base
|
|||
def issue_count
|
||||
Issue.count(:include => [:status, :project], :conditions => statement)
|
||||
rescue ::ActiveRecord::StatementInvalid => e
|
||||
raise Query::StatementInvalid.new(e.message)
|
||||
raise StatementInvalid.new(e.message)
|
||||
end
|
||||
|
||||
# Returns the issue count by group or nil if query is not grouped
|
||||
|
@ -494,7 +539,7 @@ class Query < ActiveRecord::Base
|
|||
end
|
||||
r
|
||||
rescue ::ActiveRecord::StatementInvalid => e
|
||||
raise Query::StatementInvalid.new(e.message)
|
||||
raise StatementInvalid.new(e.message)
|
||||
end
|
||||
|
||||
# Returns the issues
|
||||
|
@ -509,7 +554,7 @@ class Query < ActiveRecord::Base
|
|||
:limit => options[:limit],
|
||||
:offset => options[:offset]
|
||||
rescue ::ActiveRecord::StatementInvalid => e
|
||||
raise Query::StatementInvalid.new(e.message)
|
||||
raise StatementInvalid.new(e.message)
|
||||
end
|
||||
|
||||
# Returns the journals
|
||||
|
@ -521,7 +566,7 @@ class Query < ActiveRecord::Base
|
|||
:limit => options[:limit],
|
||||
:offset => options[:offset]
|
||||
rescue ::ActiveRecord::StatementInvalid => e
|
||||
raise Query::StatementInvalid.new(e.message)
|
||||
raise StatementInvalid.new(e.message)
|
||||
end
|
||||
|
||||
# Returns the versions
|
||||
|
@ -530,7 +575,7 @@ class Query < ActiveRecord::Base
|
|||
Version.find :all, :include => :project,
|
||||
:conditions => Query.merge_conditions(project_statement, options[:conditions])
|
||||
rescue ::ActiveRecord::StatementInvalid => e
|
||||
raise Query::StatementInvalid.new(e.message)
|
||||
raise StatementInvalid.new(e.message)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -540,16 +585,12 @@ class Query < ActiveRecord::Base
|
|||
sql = ''
|
||||
case operator
|
||||
when "="
|
||||
if [:date, :date_past].include?(type_for(field))
|
||||
sql = date_clause(db_table, db_field, (Date.parse(value.first) rescue nil), (Date.parse(value.first) rescue nil))
|
||||
else
|
||||
if value.any?
|
||||
if value.present?
|
||||
sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
|
||||
else
|
||||
# IN an empty set
|
||||
# empty set of allowed values produces no result
|
||||
sql = "0=1"
|
||||
end
|
||||
end
|
||||
when "!"
|
||||
if value.present?
|
||||
sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
|
||||
|
@ -564,58 +605,42 @@ class Query < ActiveRecord::Base
|
|||
sql = "#{db_table}.#{db_field} IS NOT NULL"
|
||||
sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter
|
||||
when ">="
|
||||
if [:date, :date_past].include?(type_for(field))
|
||||
sql = date_clause(db_table, db_field, (Date.parse(value.first) rescue nil), nil)
|
||||
else
|
||||
if is_custom_filter
|
||||
sql = "CAST(#{db_table}.#{db_field} AS decimal(60,3)) >= #{value.first.to_i}"
|
||||
sql = "#{db_table}.#{db_field} != '' AND CAST(#{db_table}.#{db_field} AS decimal(60,4)) >= #{value.first.to_f}"
|
||||
else
|
||||
sql = "#{db_table}.#{db_field} >= #{value.first.to_i}"
|
||||
end
|
||||
sql = "#{db_table}.#{db_field} >= #{value.first.to_f}"
|
||||
end
|
||||
when "<="
|
||||
if [:date, :date_past].include?(type_for(field))
|
||||
sql = date_clause(db_table, db_field, nil, (Date.parse(value.first) rescue nil))
|
||||
else
|
||||
if is_custom_filter
|
||||
sql = "CAST(#{db_table}.#{db_field} AS decimal(60,3)) <= #{value.first.to_i}"
|
||||
sql = "#{db_table}.#{db_field} != '' AND CAST(#{db_table}.#{db_field} AS decimal(60,4)) <= #{value.first.to_f}"
|
||||
else
|
||||
sql = "#{db_table}.#{db_field} <= #{value.first.to_i}"
|
||||
end
|
||||
end
|
||||
when "><"
|
||||
if [:date, :date_past].include?(type_for(field))
|
||||
sql = date_clause(db_table, db_field, (Date.parse(value[0]) rescue nil), (Date.parse(value[1]) rescue nil))
|
||||
else
|
||||
if is_custom_filter
|
||||
sql = "CAST(#{db_table}.#{db_field} AS decimal(60,3)) BETWEEN #{value[0].to_i} AND #{value[1].to_i}"
|
||||
else
|
||||
sql = "#{db_table}.#{db_field} BETWEEN #{value[0].to_i} AND #{value[1].to_i}"
|
||||
end
|
||||
sql = "#{db_table}.#{db_field} <= #{value.first.to_f}"
|
||||
end
|
||||
when "o"
|
||||
sql = "#{IssueStatus.table_name}.is_closed=#{connection.quoted_false}" if field == "status_id"
|
||||
when "c"
|
||||
sql = "#{IssueStatus.table_name}.is_closed=#{connection.quoted_true}" if field == "status_id"
|
||||
when ">t-"
|
||||
sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0)
|
||||
sql = date_range_clause(db_table, db_field, - value.first.to_i, 0)
|
||||
when "<t-"
|
||||
sql = relative_date_clause(db_table, db_field, nil, - value.first.to_i)
|
||||
sql = date_range_clause(db_table, db_field, nil, - value.first.to_i)
|
||||
when "t-"
|
||||
sql = relative_date_clause(db_table, db_field, - value.first.to_i, - value.first.to_i)
|
||||
sql = date_range_clause(db_table, db_field, - value.first.to_i, - value.first.to_i)
|
||||
when ">t+"
|
||||
sql = relative_date_clause(db_table, db_field, value.first.to_i, nil)
|
||||
sql = date_range_clause(db_table, db_field, value.first.to_i, nil)
|
||||
when "<t+"
|
||||
sql = relative_date_clause(db_table, db_field, 0, value.first.to_i)
|
||||
sql = date_range_clause(db_table, db_field, 0, value.first.to_i)
|
||||
when "t+"
|
||||
sql = relative_date_clause(db_table, db_field, value.first.to_i, value.first.to_i)
|
||||
sql = date_range_clause(db_table, db_field, value.first.to_i, value.first.to_i)
|
||||
when "t"
|
||||
sql = relative_date_clause(db_table, db_field, 0, 0)
|
||||
sql = date_range_clause(db_table, db_field, 0, 0)
|
||||
when "w"
|
||||
first_day_of_week = l(:general_first_day_of_week).to_i
|
||||
day_of_week = Date.today.cwday
|
||||
days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
|
||||
sql = relative_date_clause(db_table, db_field, - days_ago, - days_ago + 6)
|
||||
from = l(:general_first_day_of_week) == '7' ?
|
||||
# week starts on sunday
|
||||
((Date.today.cwday == 7) ? Time.now.at_beginning_of_day : Time.now.at_beginning_of_week - 1.day) :
|
||||
# week starts on monday (Rails default)
|
||||
Time.now.at_beginning_of_week
|
||||
sql = "#{db_table}.#{db_field} BETWEEN '%s' AND '%s'" % [connection.quoted_date(from), connection.quoted_date(from + 7.days)]
|
||||
when "~"
|
||||
sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
|
||||
when "!~"
|
||||
|
@ -642,32 +667,23 @@ class Query < ActiveRecord::Base
|
|||
options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 }
|
||||
when "user", "version"
|
||||
next unless project
|
||||
values = field.possible_values_options(project)
|
||||
if User.current.logged? && field.field_format == 'user'
|
||||
values.unshift ["<< #{l(:label_me)} >>", "me"]
|
||||
end
|
||||
options = { :type => :list_optional, :values => values, :order => 20}
|
||||
options = { :type => :list_optional, :values => field.possible_values_options(project), :order => 20}
|
||||
else
|
||||
options = { :type => :string, :order => 20 }
|
||||
end
|
||||
@available_filters["cf_#{field.id}"] = options.merge({ :name => field.name, :format => field.field_format })
|
||||
@available_filters["cf_#{field.id}"] = options.merge({ :name => field.name })
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a SQL clause for a date or datetime field.
|
||||
def date_clause(table, field, from, to)
|
||||
def date_range_clause(table, field, from, to)
|
||||
s = []
|
||||
if from
|
||||
s << ("#{table}.#{field} > '%s'" % [connection.quoted_date((from - 1).to_time.end_of_day)])
|
||||
s << ("#{table}.#{field} > '%s'" % [connection.quoted_date((Date.yesterday + from).to_time.end_of_day)])
|
||||
end
|
||||
if to
|
||||
s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date(to.to_time.end_of_day)])
|
||||
s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date((Date.today + to).to_time.end_of_day)])
|
||||
end
|
||||
s.join(' AND ')
|
||||
end
|
||||
|
||||
# Returns a SQL clause for a date or datetime field using relative dates.
|
||||
def relative_date_clause(table, field, days_from, days_to)
|
||||
date_clause(table, field, (days_from ? Date.today + days_from : nil), (days_to ? Date.today + days_to : nil))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
#-- encoding: UTF-8
|
||||
#-- copyright
|
||||
# ChiliProject is a project management system.
|
||||
#
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
class Query::StatementInvalid < ActiveRecord::StatementInvalid
|
||||
end
|
|
@ -1,42 +0,0 @@
|
|||
#-- encoding: UTF-8
|
||||
#-- copyright
|
||||
# ChiliProject is a project management system.
|
||||
#
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
class QueryColumn
|
||||
attr_accessor :name, :sortable, :groupable, :default_order
|
||||
include Redmine::I18n
|
||||
|
||||
def initialize(name, options={})
|
||||
self.name = name
|
||||
self.sortable = options[:sortable]
|
||||
self.groupable = options[:groupable] || false
|
||||
if groupable == true
|
||||
self.groupable = name.to_s
|
||||
end
|
||||
self.default_order = options[:default_order]
|
||||
@caption_key = options[:caption] || "field_#{name}"
|
||||
end
|
||||
|
||||
def caption
|
||||
l(@caption_key)
|
||||
end
|
||||
|
||||
# Returns true if the column is sortable, otherwise false
|
||||
def sortable?
|
||||
!sortable.nil?
|
||||
end
|
||||
|
||||
def value(issue)
|
||||
issue.send name
|
||||
end
|
||||
end
|
|
@ -1,40 +0,0 @@
|
|||
#-- encoding: UTF-8
|
||||
#-- copyright
|
||||
# ChiliProject is a project management system.
|
||||
#
|
||||
# Copyright (C) 2010-2013 the ChiliProject Team
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
class QueryCustomFieldColumn < QueryColumn
|
||||
|
||||
def initialize(custom_field)
|
||||
self.name = "cf_#{custom_field.id}".to_sym
|
||||
self.sortable = custom_field.order_statement || false
|
||||
if %w(list date bool int).include?(custom_field.field_format)
|
||||
self.groupable = custom_field.order_statement
|
||||
end
|
||||
self.groupable ||= false
|
||||
@cf = custom_field
|
||||
end
|
||||
|
||||
def caption
|
||||
@cf.name
|
||||
end
|
||||
|
||||
def custom_field
|
||||
@cf
|
||||
end
|
||||
|
||||
def value(issue)
|
||||
cv = issue.custom_values.detect {|v| v.custom_field_id == @cf.id}
|
||||
cv && @cf.cast_value(cv.value)
|
||||
end
|
||||
end
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
require_dependency 'redmine/scm/adapters/bazaar_adapter'
|
||||
require 'redmine/scm/adapters/bazaar_adapter'
|
||||
|
||||
class Repository::Bazaar < Repository
|
||||
attr_protected :root_url
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
require_dependency 'redmine/scm/adapters/cvs_adapter'
|
||||
require 'redmine/scm/adapters/cvs_adapter'
|
||||
require 'digest/sha1'
|
||||
|
||||
class Repository::Cvs < Repository
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
require_dependency 'redmine/scm/adapters/darcs_adapter'
|
||||
require 'redmine/scm/adapters/darcs_adapter'
|
||||
|
||||
class Repository::Darcs < Repository
|
||||
validates_presence_of :url, :log_encoding
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
require_dependency 'redmine/scm/adapters/filesystem_adapter'
|
||||
require 'redmine/scm/adapters/filesystem_adapter'
|
||||
|
||||
class Repository::Filesystem < Repository
|
||||
attr_protected :root_url
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
require_dependency 'redmine/scm/adapters/git_adapter'
|
||||
require 'redmine/scm/adapters/git_adapter'
|
||||
|
||||
class Repository::Git < Repository
|
||||
attr_protected :root_url
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
require_dependency 'redmine/scm/adapters/mercurial_adapter'
|
||||
require 'redmine/scm/adapters/mercurial_adapter'
|
||||
|
||||
class Repository::Mercurial < Repository
|
||||
# sort changesets by revision number
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# See doc/COPYRIGHT.rdoc for more details.
|
||||
#++
|
||||
|
||||
require_dependency 'redmine/scm/adapters/subversion_adapter'
|
||||
require 'redmine/scm/adapters/subversion_adapter'
|
||||
|
||||
class Repository::Subversion < Repository
|
||||
attr_protected :root_url
|
||||
|
|
|
@ -97,17 +97,13 @@ class Setting < ActiveRecord::Base
|
|||
|
||||
# Returns the value of the setting named name
|
||||
def self.[](name)
|
||||
if use_caching?
|
||||
Marshal.load(Rails.cache.fetch(self.cache_key(name)) {Marshal.dump(find_or_default(name).value)})
|
||||
else
|
||||
find_or_default(name).value
|
||||
end
|
||||
Marshal.load(Rails.cache.fetch("chiliproject/setting/#{name}") {Marshal.dump(find_or_default(name).value)})
|
||||
end
|
||||
|
||||
def self.[]=(name, v)
|
||||
setting = find_or_default(name)
|
||||
setting.value = (v ? v : "")
|
||||
Rails.cache.delete self.cache_key(name)
|
||||
Rails.cache.delete "chiliproject/setting/#{name}"
|
||||
setting.save
|
||||
setting.value
|
||||
end
|
||||
|
@ -141,42 +137,23 @@ class Setting < ActiveRecord::Base
|
|||
Object.const_defined?(:OpenID) && self[:openid].to_i > 0
|
||||
end
|
||||
|
||||
# Deprecation Warning: This method is no longer available. There is no
|
||||
# replacement.
|
||||
# Checks if settings have changed since the values were read
|
||||
# and clears the cache hash if it's the case
|
||||
# Called once per request
|
||||
def self.check_cache
|
||||
# DEPRECATED SINCE 3.0.0beta2
|
||||
ActiveSupport::Deprecation.warn "The Setting.check_cache method is " +
|
||||
"deprecated and will be removed in the future. There should be no " +
|
||||
"replacement for this functionality needed."
|
||||
settings_updated_on = Setting.maximum(:updated_on)
|
||||
cache_cleared_on = Rails.cache.read('chiliproject/setting-cleared_on')
|
||||
cache_cleared_on = cache_cleared_on ? Marshal.load(cache_cleared_on) : Time.now
|
||||
if settings_updated_on && cache_cleared_on <= settings_updated_on
|
||||
clear_cache
|
||||
end
|
||||
end
|
||||
|
||||
# Clears all of the Setting caches
|
||||
def self.clear_cache
|
||||
# DEPRECATED SINCE 3.0.0beta2
|
||||
ActiveSupport::Deprecation.warn "The Setting.clear_cache method is " +
|
||||
"deprecated and will be removed in the future. There should be no " +
|
||||
"replacement for this functionality needed. To sweep the whole " +
|
||||
"cache Rails.cache.clear may be used. To invalidate the Settings " +
|
||||
"only, you may use Setting.first.try(:touch)"
|
||||
end
|
||||
|
||||
# Temporarily deactivate settings caching in the block scope
|
||||
def self.uncached
|
||||
cache_setting = self.use_caching
|
||||
self.use_caching = false
|
||||
yield
|
||||
ensure
|
||||
self.use_caching = cache_setting
|
||||
end
|
||||
|
||||
# Check if Setting caching should be performed
|
||||
def self.use_caching?
|
||||
!Thread.current['chiliproject/settings/do_not_use_caching']
|
||||
end
|
||||
|
||||
# Dis-/En-able Setting caching. This is mainly intended to be used in tests
|
||||
def self.use_caching=(new_value)
|
||||
Thread.current['chiliproject/settings/do_not_use_caching'] = !new_value
|
||||
Rails.cache.delete_matched( /^chiliproject\/setting\/.+$/ )
|
||||
Rails.cache.write('chiliproject/setting-cleared_on', Marshal.dump(Time.now))
|
||||
logger.info 'Settings cache cleared.' if logger
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -185,13 +162,7 @@ private
|
|||
def self.find_or_default(name)
|
||||
name = name.to_s
|
||||
raise "There's no setting named #{name}" unless @@available_settings.has_key?(name)
|
||||
find_by_name(name) or new do |s|
|
||||
s.name = name
|
||||
s.value = @@available_settings[name]['default']
|
||||
end
|
||||
end
|
||||
|
||||
def self.cache_key(name)
|
||||
"chiliproject/setting/#{Setting.maximum(:updated_on).to_i}/#{name}"
|
||||
setting = find_by_name(name)
|
||||
setting ||= new(:name => name, :value => @@available_settings[name]['default']) if @@available_settings.has_key? name
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,10 +35,6 @@ class Tracker < ActiveRecord::Base
|
|||
name <=> tracker.name
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
TrackerDrop.new(self)
|
||||
end
|
||||
|
||||
def self.all
|
||||
find(:all, :order => 'position')
|
||||
end
|
||||
|
|
|
@ -64,9 +64,10 @@ class User < Principal
|
|||
validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
|
||||
# Login must contain lettres, numbers, underscores only
|
||||
validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
|
||||
validates_length_of :login, :firstname, :lastname, :maximum => 255
|
||||
validates_length_of :login, :maximum => 30
|
||||
validates_length_of :firstname, :lastname, :maximum => 30
|
||||
validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
|
||||
validates_length_of :mail, :maximum => 255, :allow_nil => true
|
||||
validates_length_of :mail, :maximum => 60, :allow_nil => true
|
||||
validates_confirmation_of :password, :allow_nil => true
|
||||
validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
|
||||
validates_inclusion_of :status, :in => [STATUS_ANONYMOUS, STATUS_ACTIVE, STATUS_REGISTERED, STATUS_LOCKED]
|
||||
|
@ -344,6 +345,27 @@ class User < Principal
|
|||
!logged?
|
||||
end
|
||||
|
||||
# Return user's roles for project
|
||||
def roles_for_project(project)
|
||||
roles = []
|
||||
# No role on archived projects
|
||||
return roles unless project && project.active?
|
||||
if logged?
|
||||
# Find project membership
|
||||
membership = memberships.detect {|m| m.project_id == project.id}
|
||||
if membership
|
||||
roles = membership.roles
|
||||
else
|
||||
@role_non_member ||= Role.non_member
|
||||
roles << @role_non_member
|
||||
end
|
||||
else
|
||||
@role_anonymous ||= Role.anonymous
|
||||
roles << @role_anonymous
|
||||
end
|
||||
roles
|
||||
end
|
||||
|
||||
# Return true if the user is a member of project
|
||||
def member_of?(project)
|
||||
!roles_for_project(project).detect {|role| role.member?}.nil?
|
||||
|
@ -366,6 +388,53 @@ class User < Principal
|
|||
@projects_by_role
|
||||
end
|
||||
|
||||
# Return true if the user is allowed to do the specified action on a specific context
|
||||
# Action can be:
|
||||
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
|
||||
# * a permission Symbol (eg. :edit_project)
|
||||
# Context can be:
|
||||
# * a project : returns true if user is allowed to do the specified action on this project
|
||||
# * a group of projects : returns true if user is allowed on every project
|
||||
# * nil with options[:global] set : check if user has at least one role allowed for this action,
|
||||
# or falls back to Non Member / Anonymous permissions depending if the user is logged
|
||||
def allowed_to?(action, context, options={})
|
||||
if context && context.is_a?(Project)
|
||||
# No action allowed on archived projects
|
||||
return false unless context.active?
|
||||
# No action allowed on disabled modules
|
||||
return false unless context.allows_to?(action)
|
||||
# Admin users are authorized for anything else
|
||||
return true if admin?
|
||||
|
||||
roles = roles_for_project(context)
|
||||
return false unless roles
|
||||
roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)}
|
||||
|
||||
elsif context && context.is_a?(Array)
|
||||
# Authorize if user is authorized on every element of the array
|
||||
context.map do |project|
|
||||
allowed_to?(action,project,options)
|
||||
end.inject do |memo,allowed|
|
||||
memo && allowed
|
||||
end
|
||||
elsif options[:global]
|
||||
# Admin users are always authorized
|
||||
return true if admin?
|
||||
|
||||
# authorize if user has at least one role that has this permission
|
||||
roles = memberships.collect {|m| m.roles}.flatten.uniq
|
||||
roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Is the user allowed to do the specified action on any project?
|
||||
# See allowed_to? for the actions and valid options.
|
||||
def allowed_to_globally?(action, options)
|
||||
allowed_to?(action, nil, options.reverse_merge(:global => true))
|
||||
end
|
||||
|
||||
safe_attributes 'login',
|
||||
'firstname',
|
||||
'lastname',
|
||||
|
@ -403,8 +472,6 @@ class User < Principal
|
|||
when 'only_my_events'
|
||||
if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
|
||||
true
|
||||
elsif object.respond_to?(:watched_by?) && object.watched_by?(self) # Make it clear that we always want to be notified about things we watch in this case
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
|
|
|
@ -27,7 +27,7 @@ class Version < ActiveRecord::Base
|
|||
validates_presence_of :name
|
||||
validates_uniqueness_of :name, :scope => [:project_id]
|
||||
validates_length_of :name, :maximum => 60
|
||||
validates_format_of :start_date, :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :not_a_date, :allow_nil => true
|
||||
validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :not_a_date, :allow_nil => true
|
||||
validates_inclusion_of :status, :in => VERSION_STATUSES
|
||||
validates_inclusion_of :sharing, :in => VERSION_SHARINGS
|
||||
|
||||
|
@ -37,7 +37,6 @@ class Version < ActiveRecord::Base
|
|||
|
||||
safe_attributes 'name',
|
||||
'description',
|
||||
'start_date',
|
||||
'effective_date',
|
||||
'due_date',
|
||||
'wiki_page_title',
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
class Watcher < ActiveRecord::Base
|
||||
belongs_to :watchable, :polymorphic => true
|
||||
belongs_to :user, :class_name => 'Principal', :foreign_key => 'user_id'
|
||||
belongs_to :user
|
||||
|
||||
validates_presence_of :user
|
||||
validates_uniqueness_of :user_id, :scope => [:watchable_type, :watchable_id]
|
||||
|
|
|
@ -98,7 +98,7 @@ class WikiContent < ActiveRecord::Base
|
|||
changes.delete("text")
|
||||
changes["data"] = hash[:text]
|
||||
changes["compression"] = hash[:compression]
|
||||
update_attribute(:changes, changes)
|
||||
update_attribute(:changes, changes.to_yaml)
|
||||
end
|
||||
|
||||
def text
|
||||
|
|
|
@ -53,10 +53,6 @@ class WikiPage < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
WikiPageDrop.new(self)
|
||||
end
|
||||
|
||||
def visible?(user=User.current)
|
||||
!user.nil? && user.allowed_to?(:view_wiki_pages, project)
|
||||
end
|
||||
|
|
|
@ -86,8 +86,8 @@ class Workflow < ActiveRecord::Base
|
|||
else
|
||||
transaction do
|
||||
delete_all :tracker_id => target_tracker.id, :role_id => target_role.id
|
||||
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, role_id, old_status_id, new_status_id, author, assignee)" +
|
||||
" SELECT #{target_tracker.id}, #{target_role.id}, old_status_id, new_status_id, author, assignee" +
|
||||
connection.insert "INSERT INTO #{Workflow.table_name} (tracker_id, role_id, old_status_id, new_status_id)" +
|
||||
" SELECT #{target_tracker.id}, #{target_role.id}, old_status_id, new_status_id" +
|
||||
" FROM #{Workflow.table_name}" +
|
||||
" WHERE tracker_id = #{source_tracker.id} AND role_id = #{source_role.id}"
|
||||
end
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
<div id="nav-login-content">
|
||||
<% form_tag({:controller => "account", :action=> "login"}) do %>
|
||||
<%= hidden_field_tag 'back_url', CGI.escape(request.url), :id => nil %>
|
||||
<table>
|
||||
<tr>
|
||||
<td><label for="username-pulldown"><%= l(:field_login) %></label></td>
|
||||
<td><label for="password-pulldown"><%= l(:field_password) %></label></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><%= text_field_tag 'username', nil, :tabindex => '1', :id => 'username-pulldown' %></td>
|
||||
<td><%= password_field_tag 'password', nil, :tabindex => '1', :id => 'password-pulldown' %></td>
|
||||
<td><input type="submit" name="login" value="<%=l(:button_login)%>" tabindex="1"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id = "optional_login_fields" style = "top = 10px; white-space:nowrap">
|
||||
<% if Setting.openid? %>
|
||||
<%= text_field_tag "openid_url", nil, :placeholder => l(:field_identity_url), :tabindex => '1' %>
|
||||
<% end %>
|
||||
|
||||
<% if Setting.autologin? %>
|
||||
<label for="autologin"><%= check_box_tag 'autologin', 1, false, :tabindex => 1 %> <%= l(:label_stay_logged_in) %></label>
|
||||
<% end %>
|
||||
|
||||
<% if Setting.lost_password? %>
|
||||
<%= link_to l(:label_password_lost), {:controller => 'account', :action => 'lost_password'}, :tabindex => 1 %>
|
||||
<% end %>
|
||||
|
||||
<% if !User.current.logged? && Setting.self_registration? %>
|
||||
<%= "|" if Setting.lost_password? %>
|
||||
<%= link_to l(:label_register), { :controller => 'account', :action => 'register' }, :tabindex => 1 %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
|
@ -5,7 +5,7 @@
|
|||
<table>
|
||||
<tr>
|
||||
<td align="right"><label for="username"><%=l(:field_login)%>:</label></td>
|
||||
<td align="left"><%= text_field_tag 'username', nil, :tabindex => '2' %></td>
|
||||
<td align="left"><%= text_field_tag 'username', nil, :tabindex => '1' %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right"><label for="password"><%=l(:field_password)%>:</label></td>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<h2><%=l(:label_register)%> <%=link_to l(:label_login_with_open_id_option), signin_url if Setting.openid? %></h2>
|
||||
|
||||
<% form_tag({:action => 'register'}, :class => "tabular", :autocomplete => :off) do %>
|
||||
<% form_tag({:action => 'register'}, :class => "tabular") do %>
|
||||
<%= error_messages_for 'user' %>
|
||||
|
||||
<div class="box">
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
<% content_for :sidebar do %>
|
||||
<% form_tag({}, :method => :get) do %>
|
||||
<h3><%= l(:label_activity) %></h3>
|
||||
<%= hidden_field_tag "set_filter", 1, :id => nil %>
|
||||
<p><% @activity.event_types.each do |t| %>
|
||||
<%= check_box_tag "show_#{t}", 1, @activity.scope.include?(t) %>
|
||||
<label for="show_<%=t%>"><%= link_to(l("label_#{t.singularize}_plural"), {"show_#{t}" => 1, :user_id => params[:user_id]})%></label>
|
||||
|
|
|
@ -1 +1,5 @@
|
|||
<div id="admin-menu">
|
||||
<ul>
|
||||
<%= render_menu :admin_menu %>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<h2><%=l(:label_administration)%></h2>
|
||||
|
||||
<div id="admin-index">
|
||||
<%= render :partial => 'no_data' if @no_configuration_data %>
|
||||
<%= render :partial => 'menu' %>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<%= projects_check_box_tags 'project_ids[]', @projects %>
|
|
@ -71,13 +71,3 @@
|
|||
<%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@project}: #{@board}") %>
|
||||
<%= stylesheet_link_tag 'scm' %>
|
||||
<% end %>
|
||||
|
||||
<% content_for :sidebar do %>
|
||||
<% if User.current.allowed_to?(:add_board_watchers, @project) ||
|
||||
(@board.watchers.present? && User.current.allowed_to?(:view_board_watchers, @project)) %>
|
||||
<div id="watchers">
|
||||
<%= render :partial => 'watchers/watchers', :locals => {:watched => @board} %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% end %>
|
||||
|
|
|
@ -1,55 +1,52 @@
|
|||
<ul class="menu">
|
||||
<ul>
|
||||
<%= call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back }) %>
|
||||
|
||||
<% if !@issue.nil? -%>
|
||||
<li class="edit"><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
|
||||
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
|
||||
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
|
||||
<% else %>
|
||||
<li class="edit"><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
|
||||
<li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
|
||||
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
|
||||
<% end %>
|
||||
|
||||
<% if @allowed_statuses.present? %>
|
||||
<li class="folder status">
|
||||
<a href="#" class="context_item" onclick="return false;"><%= l(:field_status) %></a>
|
||||
<li class="folder">
|
||||
<a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
|
||||
<ul>
|
||||
<% @statuses.each do |s| -%>
|
||||
<li><%= context_menu_link s.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {:status_id => s}, :back_url => @back}, :method => :post,
|
||||
:selected => (@issue && s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li>
|
||||
<% end -%>
|
||||
</ul>
|
||||
<div class="submenu"></div>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<% unless @trackers.nil? %>
|
||||
<li class="folder tracker">
|
||||
<a href="#" class="context_item"><%= l(:field_tracker) %></a>
|
||||
<li class="folder">
|
||||
<a href="#" class="submenu"><%= l(:field_tracker) %></a>
|
||||
<ul>
|
||||
<% @trackers.each do |t| -%>
|
||||
<li><%= context_menu_link t.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'tracker_id' => t}, :back_url => @back}, :method => :post,
|
||||
:selected => (@issue && t == @issue.tracker), :disabled => !@can[:edit] %></li>
|
||||
<% end -%>
|
||||
</ul>
|
||||
<div class="submenu"></div>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<li class="folder priority">
|
||||
<a href="#" class="context_item"><%= l(:field_priority) %></a>
|
||||
<li class="folder">
|
||||
<a href="#" class="submenu"><%= l(:field_priority) %></a>
|
||||
<ul>
|
||||
<% @priorities.each do |p| -%>
|
||||
<li><%= context_menu_link p.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'priority_id' => p}, :back_url => @back}, :method => :post,
|
||||
:selected => (@issue && p == @issue.priority), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li>
|
||||
<% end -%>
|
||||
</ul>
|
||||
<div class="submenu"></div>
|
||||
</li>
|
||||
|
||||
<% #TODO: allow editing versions when multiple projects %>
|
||||
<% unless @project.nil? || @project.shared_versions.open.empty? -%>
|
||||
<li class="folder fixed_version">
|
||||
<a href="#" class="context_item"><%= l(:field_fixed_version) %></a>
|
||||
<li class="folder">
|
||||
<a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
|
||||
<ul>
|
||||
<% @project.shared_versions.open.sort.each do |v| -%>
|
||||
<li><%= context_menu_link format_version_name(v), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'fixed_version_id' => v}, :back_url => @back}, :method => :post,
|
||||
|
@ -58,13 +55,11 @@
|
|||
<li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'fixed_version_id' => 'none'}, :back_url => @back}, :method => :post,
|
||||
:selected => (@issue && @issue.fixed_version.nil?), :disabled => !@can[:update] %></li>
|
||||
</ul>
|
||||
<div class="submenu"></div>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<% if @assignables.present? -%>
|
||||
<li class="folder assigned">
|
||||
<a href="#" class="context_item"><%= l(:field_assigned_to) %></a>
|
||||
<li class="folder">
|
||||
<a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
|
||||
<ul>
|
||||
<% @assignables.each do |u| -%>
|
||||
<li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => u}, :back_url => @back}, :method => :post,
|
||||
|
@ -73,13 +68,11 @@
|
|||
<li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => 'none'}, :back_url => @back}, :method => :post,
|
||||
:selected => (@issue && @issue.assigned_to.nil?), :disabled => !@can[:update] %></li>
|
||||
</ul>
|
||||
<div class="submenu"></div>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<% unless @project.nil? || @project.issue_categories.empty? -%>
|
||||
<li class="folder">
|
||||
<a href="#" class="context_item"><%= l(:field_category) %></a>
|
||||
<a href="#" class="submenu"><%= l(:field_category) %></a>
|
||||
<ul>
|
||||
<% @project.issue_categories.each do |u| -%>
|
||||
<li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'category_id' => u}, :back_url => @back}, :method => :post,
|
||||
|
@ -88,30 +81,28 @@
|
|||
<li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'category_id' => 'none'}, :back_url => @back}, :method => :post,
|
||||
:selected => (@issue && @issue.category.nil?), :disabled => !@can[:update] %></li>
|
||||
</ul>
|
||||
<div class="submenu"></div>
|
||||
</li>
|
||||
<% end -%>
|
||||
|
||||
<% if Issue.use_field_for_done_ratio? %>
|
||||
<li class="folder done_ratio">
|
||||
<a href="#" class="context_item"><%= l(:field_done_ratio) %></a>
|
||||
<li class="folder">
|
||||
<a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
|
||||
<ul>
|
||||
<% (0..10).map{|x|x*10}.each do |p| -%>
|
||||
<li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'done_ratio' => p}, :back_url => @back}, :method => :post,
|
||||
:selected => (@issue && p == @issue.done_ratio), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li>
|
||||
<% end -%>
|
||||
</ul>
|
||||
<div class="submenu"></div>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<% if !@issue.nil? %>
|
||||
<% if @can[:log_time] -%>
|
||||
<li class="log_time"><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue},
|
||||
:class => 'context_item' %></li>
|
||||
<li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue},
|
||||
:class => 'icon-time-add' %></li>
|
||||
<% end %>
|
||||
<% if User.current.logged? %>
|
||||
<li class="watch"><%= watcher_link(@issue, User.current) %></li>
|
||||
<li><%= watcher_link(@issue, User.current) %></li>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
|
@ -119,13 +110,12 @@
|
|||
<li><%= context_menu_link l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
|
||||
:class => 'icon-duplicate', :disabled => !@can[:copy] %></li>
|
||||
<% end %>
|
||||
|
||||
<li class="move"><%= context_menu_link l(:button_move), new_issue_move_path(:ids => @issues.collect(&:id)),
|
||||
:class => 'context_item', :disabled => !@can[:move] %></li>
|
||||
<li class="copy"><%= context_menu_link l(:button_copy), new_issue_move_path(:ids => @issues.collect(&:id), :copy_options => {:copy => 't'}),
|
||||
:class => 'context_item' %></li>
|
||||
<li class="delete"><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id)},
|
||||
:method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'context_item', :disabled => !@can[:delete] %></li>
|
||||
<li><%= context_menu_link l(:button_copy), new_issue_move_path(:ids => @issues.collect(&:id), :copy_options => {:copy => 't'}),
|
||||
:class => 'icon-copy', :disabled => !@can[:move] %></li>
|
||||
<li><%= context_menu_link l(:button_move), new_issue_move_path(:ids => @issues.collect(&:id)),
|
||||
:class => 'icon-move', :disabled => !@can[:move] %></li>
|
||||
<li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id), :back_url => @back},
|
||||
:method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %></li>
|
||||
|
||||
<%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
|
||||
</ul>
|
||||
|
|
|
@ -9,15 +9,6 @@
|
|||
|
||||
<p><label for="document_description"><%=l(:field_description)%></label>
|
||||
<%= text_area 'document', 'description', :cols => 60, :rows => 15, :class => 'wiki-edit' %></p>
|
||||
|
||||
<% if User.current.allowed_to?(:add_document_watchers, @project) -%>
|
||||
<p id="watchers_form"><label><%= l(:label_document_watchers) %></label>
|
||||
<% @document.project.users.sort.each do |user| -%>
|
||||
<label class="floating"><%= check_box_tag 'document[watcher_user_ids][]', user.id, @document.watched_by?(user) %> <%=h user %></label>
|
||||
<% end -%>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<!--[eoform:document]-->
|
||||
</div>
|
||||
|
||||
|
|
|
@ -27,16 +27,6 @@
|
|||
|
||||
<% html_title h(@document.title) -%>
|
||||
|
||||
<% content_for :sidebar do %>
|
||||
<% if User.current.allowed_to?(:add_document_watchers, @project) ||
|
||||
(@document.watchers.present? && User.current.allowed_to?(:view_document_watchers, @project)) %>
|
||||
<div id="watchers">
|
||||
<%= render :partial => 'watchers/watchers', :locals => {:watched => @document} %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% end %>
|
||||
|
||||
<% content_for :header_tags do %>
|
||||
<%= stylesheet_link_tag 'scm' %>
|
||||
<% end %>
|
||||
|
|
|
@ -72,7 +72,7 @@ t_height = g_height + headers_height
|
|||
<p class="warning"><%= l(:notice_gantt_chart_truncated, :max => @gantt.max_rows) %></p>
|
||||
<% end %>
|
||||
|
||||
<table style="width:100%; border:0; border-collapse: collapse;">
|
||||
<table width="100%" style="border:0; border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="width:<%= subject_width %>px; padding:0px;">
|
||||
|
||||
|
@ -98,7 +98,7 @@ month_f = @gantt.date_from
|
|||
left = 0
|
||||
height = (show_weeks ? header_heigth : header_heigth + g_height)
|
||||
@gantt.months.times do
|
||||
width = (((month_f >> 1) - month_f) * zoom - 1).to_i
|
||||
width = ((month_f >> 1) - month_f) * zoom - 1
|
||||
%>
|
||||
<div style="left:<%= left %>px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
|
||||
<%= link_to h("#{month_f.year}-#{month_f.month}"), @gantt.params.merge(:year => month_f.year, :month => month_f.month), :title => "#{month_name(month_f.month)} #{month_f.year}"%>
|
||||
|
@ -176,7 +176,7 @@ if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %>
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<table style="width:100%">
|
||||
<table width="100%">
|
||||
<tr>
|
||||
<td align="left"><%= link_to_content_update('« ' + l(:label_previous), params.merge(@gantt.params_previous)) %></td>
|
||||
<td align="right"><%= link_to_content_update(l(:label_next) + ' »', params.merge(@gantt.params_next)) %></td>
|
||||
|
|
|
@ -41,5 +41,17 @@
|
|||
</div>
|
||||
|
||||
<div class="splitcontentright">
|
||||
<%= render :partial => 'members/membership_assignment', :locals => {:principal => @group, :projects => projects - @group.projects, :roles => roles } %>
|
||||
<% if projects.any? %>
|
||||
<fieldset><legend><%=l(:label_project_new)%></legend>
|
||||
<% remote_form_for(:membership, :url => { :action => 'edit_membership', :id => @group }) do %>
|
||||
<%= label_tag "membership_project_id", l(:description_choose_project), :class => "hidden-for-sighted" %>
|
||||
<%= select_tag 'membership[project_id]', options_for_membership_project_select(@group, projects) %>
|
||||
<p><%= l(:label_role_plural) %>:
|
||||
<% roles.each do |role| %>
|
||||
<label><%= check_box_tag 'membership[role_ids][]', role.id %> <%=h role %></label>
|
||||
<% end %></p>
|
||||
<p><%= submit_tag l(:button_add) %></p>
|
||||
<% end %>
|
||||
</fieldset>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<%= observe_field(:user_search,
|
||||
:frequency => 0.5,
|
||||
:update => :users,
|
||||
:url => auto_complete_users_path(:remove_group_members => @group),
|
||||
:url => { :controller => 'groups', :action => 'autocomplete_for_user', :id => @group },
|
||||
:with => 'q')
|
||||
%>
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
<td class="buttons"><%= link_to l(:button_delete), group, :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% else %>
|
||||
<p class="nodata"><%= l(:label_no_data) %></p>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<% html_title "Wiki Syntax Quick Reference" %>
|
||||
<h1>Wiki Syntax Quick Reference</h1>
|
||||
|
||||
<table style="width:100%">
|
||||
<table width="100%">
|
||||
<tr><th colspan="3">Font Styles</th></tr>
|
||||
<tr><th><img src="../images/jstoolbar/bt_strong.png" style="border: 1px solid #bbb;" alt="Strong" /></th><td width="50%">*Strong*</td><td width="50%"><strong>Strong</strong></td></tr>
|
||||
<tr><th><img src="../images/jstoolbar/bt_em.png" style="border: 1px solid #bbb;" alt="Italic" /></th><td>_Italic_</td><td><em>Italic</em></td></tr>
|
||||
|
|
|
@ -13,22 +13,20 @@
|
|||
}
|
||||
a.new { color: #b73535; }
|
||||
|
||||
.syntaxhl .line-numbers { padding: 2px 4px 2px 4px; background-color: #eee; margin:0 }
|
||||
.syntaxhl .comment { color:#666; }
|
||||
.CodeRay .c { color:#666; }
|
||||
|
||||
.syntaxhl .class { color:#B06; font-weight:bold }
|
||||
.syntaxhl .delimiter { color:black }
|
||||
.syntaxhl .function { color:#06B; font-weight:bold }
|
||||
.CodeRay .cl { color:#B06; font-weight:bold }
|
||||
.CodeRay .dl { color:black }
|
||||
.CodeRay .fu { color:#06B; font-weight:bold }
|
||||
|
||||
.syntaxhl .inline { background: #eee }
|
||||
.syntaxhl .inline .inline-delimiter { font-weight: bold; color: #888 }
|
||||
.CodeRay .il { background: #eee }
|
||||
.CodeRay .il .idl { font-weight: bold; color: #888 }
|
||||
|
||||
.syntaxhl .instance-variable { color:#33B }
|
||||
.syntaxhl .reserved { color:#080; font-weight:bold }
|
||||
|
||||
.syntaxhl .string { background-color:#fff0f0; color: #D20; }
|
||||
.syntaxhl .string .delimiter { color:#710 }
|
||||
.CodeRay .iv { color:#33B }
|
||||
.CodeRay .r { color:#080; font-weight:bold }
|
||||
|
||||
.CodeRay .s { background-color:#fff0f0 }
|
||||
.CodeRay .s .dl { color:#710 }
|
||||
<% end %>
|
||||
|
||||
<% html_title "Wiki Formatting" %>
|
||||
|
@ -238,7 +236,7 @@ To go live, all you need to add is a database and a web server.
|
|||
|
||||
<h2><a name="13" class="wiki-page"></a>Code highlighting</h2>
|
||||
|
||||
<p>The default code highlighting relies on <a href="http://coderay.rubychan.de/" class="external">CodeRay</a>, a fast syntax highlighting library written completely in Ruby. It currently supports c, cpp, css, delphi, groovy, html, java, javascript, json, php, python, rhtml, ruby, scheme, sql, xml and yaml languages.</p>
|
||||
<p>Code highlightment relies on <a href="http://coderay.rubychan.de/" class="external">CodeRay</a>, a fast syntax highlighting library written completely in Ruby. It currently supports c, cpp, css, delphi, groovy, html, java, javascript, json, php, python, rhtml, ruby, scheme, sql, xml and yaml languages.</p>
|
||||
|
||||
<p>You can highlight code in your wiki page using this syntax:</p>
|
||||
|
||||
|
@ -250,14 +248,15 @@ To go live, all you need to add is a database and a web server.
|
|||
|
||||
<p>Example:</p>
|
||||
|
||||
<pre><code class="ruby syntaxhl"><span class="line-numbers"> 1</span> <span class="comment"># The Greeter class</span>
|
||||
<span class="line-numbers"> 2</span> <span class="reserved">class</span> <span class="class">Greeter</span>
|
||||
<span class="line-numbers"> 3</span> <span class="reserved">def</span> <span class="function">initialize</span>(name)
|
||||
<span class="line-numbers"> 4</span> <span class="instance-variable">@name</span> = name.capitalize
|
||||
<span class="line-numbers"> 5</span> <span class="reserved">end</span>
|
||||
<span class="line-numbers"> 6</span>
|
||||
<span class="line-numbers"> 7</span> <span class="reserved">def</span> <span class="function">salute</span>
|
||||
<span class="line-numbers"> 8</span> puts <span class="string"><span class="delimiter">"</span><span class="content">Hello </span><span class="inline"><span class="inline-delimiter">#{</span><span class="instance-variable">@name</span><span class="inline-delimiter">}</span></span><span class="content">!</span><span class="delimiter">"</span></span>
|
||||
<span class="line-numbers"> 9</span> <span class="reserved">end</span>
|
||||
<span class="line-numbers"><strong>10</strong></span> <span class="reserved">end</span></code>
|
||||
<pre><code class="ruby CodeRay"><span class="no"> 1</span> <span class="c"># The Greeter class</span>
|
||||
<span class="no"> 2</span> <span class="r">class</span> <span class="cl">Greeter</span>
|
||||
<span class="no"> 3</span> <span class="r">def</span> <span class="fu">initialize</span>(name)
|
||||
<span class="no"> 4</span> <span class="iv">@name</span> = name.capitalize
|
||||
<span class="no"> 5</span> <span class="r">end</span>
|
||||
<span class="no"> 6</span>
|
||||
<span class="no"> 7</span> <span class="r">def</span> <span class="fu">salute</span>
|
||||
<span class="no"> 8</span> puts <span class="s"><span class="dl">"</span><span class="k">Hello </span><span class="il"><span class="idl">#{</span><span class="iv">@name</span><span class="idl">}</span></span><span class="k">!</span><span class="dl">"</span></span>
|
||||
<span class="no"> 9</span> <span class="r">end</span>
|
||||
<span class="no"><strong>10</strong></span> <span class="r">end</span>
|
||||
</code>
|
||||
</pre>
|
||||
|
|
|
@ -46,12 +46,12 @@
|
|||
<div class="splitcontentright">
|
||||
<p>
|
||||
<label for='start_date'><%= l(:field_start_date) %></label>
|
||||
<%= date_field_tag 'start_date', '', :size => 10 %>
|
||||
<%= text_field_tag 'start_date', '', :size => 10 %><%= calendar_for('start_date') %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for='due_date'><%= l(:field_due_date) %></label>
|
||||
<%= date_field_tag 'due_date', '', :size => 10 %>
|
||||
<%= text_field_tag 'due_date', '', :size => 10 %><%= calendar_for('due_date') %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
</div>
|
||||
|
||||
<div class="splitcontentright">
|
||||
<p><%= f.date_field :start_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_start_date') if @issue.leaf? %></p>
|
||||
<p><%= f.date_field :due_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_due_date') if @issue.leaf? %></p>
|
||||
<p><%= f.text_field :start_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_start_date') if @issue.leaf? %></p>
|
||||
<p><%= f.text_field :due_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_due_date') if @issue.leaf? %></p>
|
||||
<p><%= f.text_field :estimated_hours, :size => 3, :disabled => !@issue.leaf? %> <%= l(:field_hours) %></p>
|
||||
<% if @issue.leaf? && Issue.use_field_for_done_ratio? %>
|
||||
<p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
<%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="attachments" class="header_collapsible collapsible collapsed"><legend onclick="toggleFieldset(this);"><%=l(:label_attachment_plural)%></legend>
|
||||
<fieldset id="attachments" class="collapsible collapsed borders"><legend onclick="toggleFieldset(this);"><%=l(:label_attachment_plural)%></legend>
|
||||
<div style="display: none;">
|
||||
<%= render :partial => 'attachments/form' %>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<% form_tag({}) do -%>
|
||||
<%= hidden_field_tag 'back_url', url_for(params), :id => nil %>
|
||||
<%= hidden_field_tag 'back_url', url_for(params) %>
|
||||
<div class="autoscroll">
|
||||
<table class="list issues">
|
||||
<thead><tr>
|
||||
<th class="checkbox hide-when-print"><%= link_to image_tag('toggle_check.png'), {}, :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
|
||||
<th class="checkbox hide-when-print"><%= link_to image_tag('toggle_check.png'), {}, :onclick => 'toggleIssuesSelection(Element.up(this, "form")); return false;',
|
||||
:title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %>
|
||||
</th>
|
||||
<%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %>
|
||||
<% query.columns.each do |column| %>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<p>
|
||||
<strong><%=l(:label_related_issues)%></strong>
|
||||
<div class="contextual">
|
||||
<% if authorize_for('issue_relations', 'new') %>
|
||||
(<%= toggle_link l(:button_add), 'new-relation-form', {:focus => 'relation_issue_to_id'} %>)
|
||||
<%= toggle_link l(:button_add), 'new-relation-form', {:focus => 'relation_issue_to_id'} %>
|
||||
<% end %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p><strong><%=l(:label_related_issues)%></strong></p>
|
||||
|
||||
<% if @relations.present? %>
|
||||
<table style="width:100%">
|
||||
|
|
|
@ -1,7 +1,17 @@
|
|||
<h3><%= l(:label_issue_plural) %></h3>
|
||||
<%= link_to l(:label_issue_view_all), { :controller => 'issues', :action => 'index', :project_id => @project, :set_filter => 1 } %><br />
|
||||
<% if @project %>
|
||||
<%= link_to l(:field_summary), :controller => 'reports', :action => 'issue_report', :id => @project %><br />
|
||||
<% end %>
|
||||
<%= call_hook(:view_issues_sidebar_issues_bottom) %>
|
||||
|
||||
<% if User.current.allowed_to?(:view_calendar, @project, :global => true) %>
|
||||
<%= link_to(l(:label_calendar), :controller => 'calendars', :action => 'show', :project_id => @project) %><br />
|
||||
<% end %>
|
||||
<% if User.current.allowed_to?(:view_gantt, @project, :global => true) %>
|
||||
<%= link_to(l(:label_gantt), :controller => 'gantts', :action => 'show', :project_id => @project) %><br />
|
||||
<% end %>
|
||||
<%= call_hook(:view_issues_sidebar_planning_bottom) %>
|
||||
|
||||
<%= render_sidebar_queries unless @project %>
|
||||
<%= render_sidebar_queries %>
|
||||
<%= call_hook(:view_issues_sidebar_queries_bottom) %>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<% if !@issue.leaf? || @issue.parent || User.current.allowed_to?(:manage_subtasks, @project) %>
|
||||
<hr />
|
||||
<p>
|
||||
<strong><%= l(:label_issue_hierarchy) %></strong>
|
||||
<% if User.current.allowed_to?(:manage_subtasks, @project) %>
|
||||
(<%= link_to(l(:label_subtask_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) %>)
|
||||
<% end %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%= render_parents_and_subtree @issue %>
|
|
@ -65,11 +65,11 @@
|
|||
<% end %>
|
||||
<p>
|
||||
<label for='issue_start_date'><%= l(:field_start_date) %></label>
|
||||
<%= date_field_tag 'issue[start_date]', '', :size => 10 %>
|
||||
<%= text_field_tag 'issue[start_date]', '', :size => 10 %><%= calendar_for('issue_start_date') %>
|
||||
</p>
|
||||
<p>
|
||||
<label for='issue_due_date'><%= l(:field_due_date) %></label>
|
||||
<%= date_field_tag 'issue[due_date]', '', :size => 10 %>
|
||||
<%= text_field_tag 'issue[due_date]', '', :size => 10 %><%= calendar_for('issue_due_date') %>
|
||||
</p>
|
||||
<% if Issue.use_field_for_done_ratio? %>
|
||||
<p>
|
||||
|
|
|
@ -1,29 +1,23 @@
|
|||
<div class="title-bar">
|
||||
|
||||
<h2><%= @query.new_record? ? l(:label_issue_plural) : h(@query.name) %></h2>
|
||||
|
||||
<div class="title-bar-extras">
|
||||
|
||||
<div class="contextual">
|
||||
<% if !@query.new_record? && @query.editable_by?(User.current) %>
|
||||
<%= link_to l(:button_edit), {:controller => 'queries', :action => 'edit', :id => @query}, :class => 'icon icon-edit' %>
|
||||
<%= link_to l(:button_delete), {:controller => 'queries', :action => 'destroy', :id => @query}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
|
||||
<% end %>
|
||||
<%= render :partial => 'queries/new_issue_button' %>
|
||||
</div>
|
||||
|
||||
<h2><%= @query.new_record? ? l(:label_issue_plural) : h(@query.name) %></h2>
|
||||
<% html_title(@query.new_record? ? l(:label_issue_plural) : h(@query.name)) %>
|
||||
|
||||
<% form_tag({ :controller => 'queries', :action => 'new' }, :id => 'query_form') do %>
|
||||
<%= hidden_field_tag('project_id', @project.to_param) if @project %>
|
||||
<div id="query_form_content" class="hide-when-print">
|
||||
<fieldset id="filters" class="header_collapsible collapsible <%= @query.new_record? ? "" : "collapsed" %>">
|
||||
<fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
|
||||
<legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
|
||||
<div class="filter-fields" style="<%= @query.new_record? ? "" : "display: none;" %>">
|
||||
<div style="<%= @query.new_record? ? "" : "display: none;" %>">
|
||||
<%= render :partial => 'queries/filters', :locals => {:query => @query} %>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset id="column_options" class="header_collapsible collapsible collapsed">
|
||||
<fieldset class="collapsible collapsed">
|
||||
<legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
|
||||
<div style="display: none;">
|
||||
<table>
|
||||
|
@ -35,20 +29,6 @@
|
|||
<td><label for='group_by'><%= l(:field_group_by) %></label></td>
|
||||
<td><%= select_tag('group_by', options_for_select([[]] + @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]}, @query.group_by)) %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><%= l(:label_project_plural) %></td>
|
||||
<td>
|
||||
<label>
|
||||
<%= radio_button_tag('display_subprojects', '0', !@query.display_subprojects?) %>
|
||||
<%= l(:text_current_project) %>
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
<%= radio_button_tag('display_subprojects', '1', @query.display_subprojects?) %>
|
||||
<%= l(:label_and_its_subprojects, :value => l(:text_current_project)) %>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
@ -75,9 +55,6 @@
|
|||
</p>
|
||||
<% end %>
|
||||
|
||||
</div><!-- .title-bar-extras -->
|
||||
</div><!-- .title-bar -->
|
||||
|
||||
<%= error_messages_for 'query' %>
|
||||
<% if @query.valid? %>
|
||||
<% if @issues.empty? %>
|
||||
|
|
|
@ -1,26 +1,20 @@
|
|||
<div class="title-bar" id="upper-title-bar">
|
||||
<div class="title-bar-actions">
|
||||
<%= render :partial => 'action_menu' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2><%= h(@issue.tracker.name) %> #<%= h(@issue.id) %><%= call_hook(:view_issues_show_identifier, :issue => @issue) %></h2>
|
||||
|
||||
<div class="<%= @issue.css_classes %> details">
|
||||
<%= avatar(@issue.author, :size => "50") %>
|
||||
|
||||
<h1 class="subject">
|
||||
<%=h(@issue.subject) %> (<%= h(@issue.tracker.name) + ' #' +@issue.id.to_s %>)
|
||||
</h1>
|
||||
<hr />
|
||||
|
||||
<div class="subject">
|
||||
<%= render_issue_subject_with_tree(@issue) %>
|
||||
</div>
|
||||
<p class="author">
|
||||
<%= avatar(@issue.author, :size => "14") %>
|
||||
<%= authoring @issue.created_on, @issue.author %>.
|
||||
<% if @issue.created_on != @issue.updated_on %>
|
||||
<%= l(:label_updated_time, time_tag(@issue.updated_on)) %>.
|
||||
<% end %>
|
||||
</p>
|
||||
<hr />
|
||||
|
||||
<div class="meta">
|
||||
<table class="attributes">
|
||||
<tr>
|
||||
<th class="status"><%=l(:field_status)%>:</th><td class="status"><%= h(@issue.status.name) %></td>
|
||||
|
@ -39,27 +33,21 @@
|
|||
<% if User.current.allowed_to?(:view_time_entries, @project) %>
|
||||
<th class="spent-time"><%=l(:label_spent_time)%>:</th>
|
||||
<td class="spent-time"><%= @issue.spent_hours > 0 ? (link_to l_hours(@issue.spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %></td>
|
||||
<% else %>
|
||||
<th></th><td></td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fixed-version"><%=l(:field_fixed_version)%>:</th><td class="fixed-version"><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
|
||||
<% if @issue.estimated_hours %>
|
||||
<th class="estimated-hours"><%=l(:field_estimated_hours)%>:</th><td class="estimated-hours"><%= l_hours(@issue.estimated_hours) %></td>
|
||||
<% else %>
|
||||
<th></th><td></td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<%= render_custom_fields_rows(@issue) %>
|
||||
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
|
||||
</table>
|
||||
</div><!-- .meta -->
|
||||
|
||||
<% if @issue.description? %>
|
||||
<% if @issue.description? || @issue.attachments.any? -%>
|
||||
<hr />
|
||||
|
||||
<div class="description">
|
||||
<% if @issue.description? %>
|
||||
<div class="contextual">
|
||||
<%= link_to_remote_if_authorized(l(:button_quote), { :url => {:controller => 'journals', :action => 'new', :id => @issue} }, :class => 'icon icon-comment') %>
|
||||
</div>
|
||||
|
@ -68,17 +56,22 @@
|
|||
<div class="wiki">
|
||||
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if @issue.attachments.any? -%>
|
||||
<hr />
|
||||
<%= link_to_attachments @issue %>
|
||||
<% end -%>
|
||||
|
||||
<%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
|
||||
|
||||
<%= render :partial => 'tree_simple' %>
|
||||
<% if !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %>
|
||||
<hr />
|
||||
<div id="issue_tree">
|
||||
<div class="contextual">
|
||||
<%= link_to(l(:button_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %>
|
||||
</div>
|
||||
<p><strong><%=l(:label_subtask_plural)%></strong></p>
|
||||
<%= render_descendants_tree(@issue) unless @issue.leaf? %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if authorize_for('issue_relations', 'new') || @issue.relations.present? %>
|
||||
<hr />
|
||||
|
@ -87,7 +80,6 @@
|
|||
</div>
|
||||
<% end %>
|
||||
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
<% if @changesets.present? %>
|
||||
|
@ -99,19 +91,14 @@
|
|||
|
||||
<% if @journals.present? %>
|
||||
<div id="history">
|
||||
<h3 class="rounded-background"><%=l(:label_history)%></h3>
|
||||
<h3><%=l(:label_history)%></h3>
|
||||
<%= render :partial => 'history', :locals => { :issue => @issue, :journals => @journals } %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
||||
<div style="clear: both;"></div>
|
||||
|
||||
<div class="title-bar" id="lower-title-bar">
|
||||
<div class="title-bar-actions">
|
||||
<%= render :partial => 'action_menu', :locals => {:replace_watcher => 'watcher2' } %>
|
||||
</div>
|
||||
</div>
|
||||
<%= render :partial => 'action_menu' %>
|
||||
|
||||
<div style="clear: both;"></div>
|
||||
<% if authorize_for('issues', 'edit') %>
|
||||
|
@ -142,8 +129,9 @@
|
|||
<% content_for :header_tags do %>
|
||||
<%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %>
|
||||
<%= stylesheet_link_tag 'scm' %>
|
||||
<%= javascript_include_tag 'context_menu.jquery' %>
|
||||
<%= javascript_include_tag 'context_menu' %>
|
||||
<%= stylesheet_link_tag 'context_menu' %>
|
||||
<%= stylesheet_link_tag 'context_menu_rtl' if l(:direction) == 'rtl' %>
|
||||
<% end %>
|
||||
<%= javascript_tag "jQuery(document).ContextMenu('#{issues_context_menu_path}')" %>
|
||||
<div id="context-menu" style="display: none;"></div>
|
||||
<%= javascript_tag "new ContextMenu('#{issues_context_menu_path}')" %>
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
<p><%= authoring @journal.created_at, @journal.user, :label => :label_updated_time_by %></p>
|
||||
|
||||
<div class="text-diff">
|
||||
<%= simple_format_without_paragraph @diff.to_html %>
|
||||
</div>
|
||||
|
||||
<p><%= link_to(l(:button_back), issue_path(@issue)) %></p>
|
||||
|
||||
<% html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue