Merge remote-tracking branch 'origin/dev' into feature/api_read_access_on_project_workflows_3113

pull/850/head
Hagen Schink 11 years ago
commit e02f7c6b9f
  1. 1
      .gitignore
  2. 24
      .jshintignore
  3. 2
      .travis.yml
  4. 100
      Gruntfile.js
  5. 4
      app/assets/javascripts/application.js.erb
  6. 202
      app/assets/javascripts/jquery.trap.js
  7. 14
      app/assets/javascripts/members_select_boxes.js
  8. 4
      app/assets/javascripts/modal.js
  9. 2
      app/assets/javascripts/timelines.js
  10. 2
      app/assets/javascripts/timelines/model/Color.js
  11. 2
      app/assets/javascripts/timelines/model/PlanningElement.js
  12. 143
      app/assets/javascripts/types_checkboxes.js
  13. 16
      app/controllers/activities_controller.rb
  14. 12
      app/controllers/api/v2/planning_elements_controller.rb
  15. 16
      app/controllers/api/v2/users_controller.rb
  16. 52
      app/controllers/api/v2/work_package_priorities_controller.rb
  17. 2
      app/controllers/application_controller.rb
  18. 1
      app/controllers/my_controller.rb
  19. 10
      app/controllers/news_controller.rb
  20. 3
      app/controllers/work_packages/bulk_controller.rb
  21. 7
      app/controllers/work_packages/context_menus_controller.rb
  22. 1
      app/controllers/work_packages/reports_controller.rb
  23. 15
      app/helpers/application_helper.rb
  24. 4
      app/helpers/timelines_helper.rb
  25. 6
      app/helpers/work_packages_helper.rb
  26. 2
      app/models/activity/work_package_activity_provider.rb
  27. 2
      app/models/mail_handler.rb
  28. 11
      app/models/member.rb
  29. 4
      app/models/news.rb
  30. 29
      app/models/project.rb
  31. 4
      app/models/queries/work_packages/available_filter_options.rb
  32. 2
      app/models/query.rb
  33. 4
      app/models/type.rb
  34. 40
      app/models/work_package.rb
  35. 3
      app/services/reports/reports_service.rb
  36. 44
      app/services/reports/responsible_report.rb
  37. 9
      app/services/user_search_service.rb
  38. 1
      app/views/activities/index.html.erb
  39. 3
      app/views/admin/info.html.erb
  40. 2
      app/views/admin/plugins.html.erb
  41. 4
      app/views/admin/projects.html.erb
  42. 32
      app/views/api/v2/work_package_priorities/index.api.rabl
  43. 2
      app/views/auth_sources/edit.html.erb
  44. 2
      app/views/auth_sources/index.html.erb
  45. 2
      app/views/auth_sources/new.html.erb
  46. 2
      app/views/categories/_form.html.erb
  47. 2
      app/views/custom_fields/edit.html.erb
  48. 2
      app/views/custom_fields/new.html.erb
  49. 2
      app/views/enumerations/edit.html.erb
  50. 4
      app/views/enumerations/index.html.erb
  51. 2
      app/views/enumerations/new.html.erb
  52. 4
      app/views/groups/edit.html.erb
  53. 2
      app/views/groups/index.html.erb
  54. 2
      app/views/groups/new.html.erb
  55. 76
      app/views/my/account.html.erb
  56. 61
      app/views/my/blocks/_workpackagesresponsiblefor.html.erb
  57. 2
      app/views/news/edit.html.erb
  58. 2
      app/views/news/new.html.erb
  59. 2
      app/views/planning_element_type_colors/edit.html.erb
  60. 3
      app/views/planning_element_type_colors/index.html.erb
  61. 2
      app/views/planning_element_type_colors/new.html.erb
  62. 2
      app/views/project_types/edit.html.erb
  63. 3
      app/views/project_types/index.html.erb
  64. 2
      app/views/project_types/new.html.erb
  65. 3
      app/views/projects/_form.html.erb
  66. 99
      app/views/projects/form/_types.html.erb
  67. 2
      app/views/projects/form/attributes/_responsible_id.html.erb
  68. 4
      app/views/projects/index.html.erb
  69. 2
      app/views/projects/new.html.erb
  70. 1
      app/views/projects/settings/_types.html.erb
  71. 2
      app/views/roles/edit.html.erb
  72. 2
      app/views/roles/index.html.erb
  73. 2
      app/views/roles/new.html.erb
  74. 4
      app/views/settings/edit.html.erb
  75. 2
      app/views/statuses/edit.html.erb
  76. 4
      app/views/statuses/index.html.erb
  77. 2
      app/views/statuses/new.html.erb
  78. 2
      app/views/timelines/confirm_destroy.html.erb
  79. 6
      app/views/timelines/new.html.erb
  80. 2
      app/views/types/edit.html.erb
  81. 4
      app/views/types/index.html.erb
  82. 2
      app/views/types/new.html.erb
  83. 1
      app/views/user_mailer/_issue_details.html.erb
  84. 1
      app/views/user_mailer/_issue_details.text.erb
  85. 4
      app/views/users/edit.html.erb
  86. 2
      app/views/users/new.html.erb
  87. 2
      app/views/versions/edit.html.erb
  88. 2
      app/views/versions/new.html.erb
  89. 2
      app/views/wiki/index.html.erb
  90. 2
      app/views/work_packages/_relations.html.erb
  91. 6
      app/views/work_packages/bulk/edit.html.erb
  92. 14
      app/views/work_packages/context_menus/index.html.erb
  93. 2
      app/views/work_packages/edit.html.erb
  94. 4
      app/views/work_packages/moves/new.html.erb
  95. 2
      app/views/work_packages/new.html.erb
  96. 4
      app/views/work_packages/reports/report.html.erb
  97. 4
      app/views/workflows/index.html.erb
  98. 19
      config/locales/de.yml
  99. 21
      config/locales/en.yml
  100. 1
      config/routes.rb
  101. Some files were not shown because too many files have changed in this diff Show More

1
.gitignore vendored

@ -79,3 +79,4 @@
# asset cache
/.sass-cache/
/node_modules/

@ -0,0 +1,24 @@
app/assets/javascripts/raphael.js
app/assets/javascripts/raphael-min.js
app/assets/javascripts/date-de-DE.js
app/assets/javascripts/date-en-US.js
app/assets/javascripts/jstoolbar/**/*
app/assets/javascripts/jquery_noconflict.js
app/assets/javascripts/pages/**/*
app/assets/javascripts/project/**/*
app/assets/javascripts/jquery.menu_expand.js
app/assets/javascripts/jquery-ui-i18n.js
#we sould fix this ones
app/assets/javascripts/select_list_move.js
app/assets/javascripts/context_menu.js
app/assets/javascripts/breadcrumb.js
app/assets/javascripts/keyboard_shortcuts.js
app/assets/javascripts/openproject.js
app/assets/javascripts/top-shelf.js
app/assets/javascripts/top_menu.js
app/assets/javascripts/findDomElement.js
app/assets/javascripts/application.js
app/assets/javascripts/action_menu.js
app/assets/javascripts/accessibility.js
app/assets/javascripts/repository_navigation.js

@ -28,7 +28,7 @@
language: ruby
rvm:
- 2.0
- 2.0.0
branches:
only:
- dev

@ -0,0 +1,100 @@
module.exports = function(grunt) {
var jsPath = "app/assets/javascripts/";
var testSource = "mocha/index.html";
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
jshint: {
all: {
files: {
src: [jsPath]
}
}
},
mocha_phantomjs: {
all: [testSource],
small: {
src:[testSource],
options: {
reporter: "dot"
}
},
cov: {
src:[testSource],
options: {
output: "coverage.json",
reporter: "json-cov"
}
},
jenkins: {
src:[testSource],
options: {
reporter: "XUnit"
}
}
},
jscoverage: {
options: {
inputDirectory: 'app/assets/javascripts/',
outputDirectory: 'app/assets/javascripts_cov/'
}
},
watch: {
scripts: {
files: [jsPath] ,
tasks: ['jshint', 'mocha_phantomjs:small'],
options: {
spawn: false,
},
},
},
});
grunt.loadNpmTasks("grunt-jscoverage");
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-mocha-phantomjs');
// Default task(s).
grunt.registerTask('default', ['jshint']);
var tempPath = "app/assets/javascripts_temp/";
var covPath = "app/assets/javascripts_cov/";
grunt.registerTask('moveFiles', 'switch directories for coverage-enabled files and normal files', function () {
var fs = require("fs");
fs.renameSync(jsPath, tempPath);
fs.renameSync(covPath, jsPath);
fs.renameSync(tempPath + "date-en-US.js", jsPath + "date-en-US.js");
});
grunt.registerTask('cleanUpCoverage', 'undo moveFiles', function () {
var done = this.async();
var fs = require("fs");
if (fs.existsSync(tempPath)) {
fs.renameSync(jsPath, covPath);
fs.renameSync(tempPath, jsPath);
}
if (fs.existsSync("coverage.json")) {
fs.unlinkSync("coverage.json");
}
rmdir = require("rimraf");
rmdir(covPath, function (e) {
done(e);
});
});
grunt.registerTask('jsonCov2Html', 'convert coverage to html', function () {
var exec = require("exec");
exec("cat coverage.json | node_modules/json2htmlcov/bin/json2htmlcov > coverage.html");
});
grunt.registerTask('coverage', ['jscoverage', 'moveFiles', 'mocha_phantomjs:cov', 'jsonCov2Html', 'cleanUpCoverage']);
};

@ -46,6 +46,7 @@
//= require jquery_ujs
//= require jquery_noconflict
//= require jquery.colorcontrast
//= require jquery.trap
//= require prototype
//= require effects
//= require dragdrop
@ -114,10 +115,11 @@ jQuery(document).ready(function ($) {
showWeek: true,
changeMonth: true,
changeYear: true,
yearRange: "c-100:c+10",
dateFormat: 'yy-mm-dd',
showButtonPanel: true,
calculateWeek: function (d) {
if (d.getDay() > 1) {
if (d.getDay() != 1) {
d.setDate(d.getDate() - d.getDay() + 1);
}
return $.datepicker.iso8601Week(d);

@ -0,0 +1,202 @@
/*!
Copyright (c) 2011, 2012 Julien Wajsberg <felash@gmail.com>
All rights reserved.
Official repository: https://github.com/julienw/jquery-trap-input
License is there: https://github.com/julienw/jquery-trap-input/blob/master/LICENSE
This is version 1.2.0.
*/
(function( $, undefined ){
/*
(this comment is after the first line of code so that uglifyjs removes it)
Redistribution and use in source and binary forms, with or without
modification, are permitted without condition.
Although that's not an obligation, I would appreciate that you provide a
link to the official repository.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED.
*/
/*jshint boss: true, bitwise: true, curly: true, expr: true, newcap: true, noarg: true, nonew: true, latedef: true, regexdash: true */
var DATA_ISTRAPPING_KEY = "trap.isTrapping";
function onkeypress(e) {
if (e.keyCode === 9) {
var goReverse = !!(e.shiftKey);
if (processTab(this, e.target, goReverse)) {
e.preventDefault();
e.stopPropagation();
}
}
}
// will return true if we could process the tab event
// otherwise, return false
function processTab(container, elt, goReverse) {
var $focussable = getFocusableElementsInContainer(container),
curElt = elt,
index, nextIndex, prevIndex, lastIndex;
do {
index = $focussable.index(curElt);
nextIndex = index + 1;
prevIndex = index - 1;
lastIndex = $focussable.length - 1;
switch(index) {
case -1:
return false; // that's strange, let the browser do its job
case 0:
prevIndex = lastIndex;
break;
case lastIndex:
nextIndex = 0;
break;
}
if (goReverse) {
nextIndex = prevIndex;
}
curElt = $focussable.get(nextIndex);
// IE sometimes throws when an element is not visible
try {
curElt.focus();
} catch(e) {
}
} while (elt === elt.ownerDocument.activeElement);
return true;
}
function filterKeepSpeciallyFocusable() {
return this.tabIndex > 0;
}
function filterKeepNormalElements() {
return !this.tabIndex; // true if no tabIndex or tabIndex == 0
}
function sortFocusable(a, b) {
return (a.t - b.t) || (a.i - b.i);
}
function getFocusableElementsInContainer(container) {
var $container = $(container);
var result = [],
cnt = 0;
fixIndexSelector.enable && fixIndexSelector.enable();
// leaving away command and details for now
$container.find("a[href], link[href], [draggable=true], [contenteditable=true], :input:enabled, [tabindex=0]")
.filter(":visible")
.filter(filterKeepNormalElements)
.each(function(i, val) {
result.push({
v: val, // value
t: 0, // tabIndex
i: cnt++ // index for stable sort
});
});
$container
.find("[tabindex]")
.filter(":visible")
.filter(filterKeepSpeciallyFocusable)
.each(function(i, val) {
result.push({
v: val, // value
t: val.tabIndex, // tabIndex
i: cnt++ // index
});
});
fixIndexSelector.disable && fixIndexSelector.disable();
result = $.map(result.sort(sortFocusable), // needs stable sort
function(val) {
return val.v;
}
);
return $(result);
}
function trap() {
this.keydown(onkeypress);
this.data(DATA_ISTRAPPING_KEY, true);
return this;
}
function untrap() {
this.unbind('keydown', onkeypress);
this.removeData(DATA_ISTRAPPING_KEY);
return this;
}
function isTrapping() {
return !!this.data(DATA_ISTRAPPING_KEY);
}
$.fn.extend({
trap: trap,
untrap: untrap,
isTrapping: isTrapping
});
// jQuery 1.6.x tabindex attr hooks management
// this triggers problems for tabindex attribute
// selectors in IE7-
// see https://github.com/julienw/jquery-trap-input/issues/3
var fixIndexSelector = {};
if ($.find.find && $.find.attr !== $.attr) {
// jQuery uses Sizzle (this is jQuery >= 1.3)
// sizzle uses its own attribute handling (in jq 1.6.x and below)
(function() {
var tabindexKey = "tabindex";
var sizzleAttrHandle = $.expr.attrHandle;
// this function comes directly from jQuery 1.7.2 (propHooks.tabIndex.get)
// we have to put it here if we want to support jQuery < 1.6 which
// doesn't have an attrHooks object to reference.
function getTabindexAttr(elem) {
// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
var attributeNode = elem.getAttributeNode(tabindexKey);
return attributeNode && attributeNode.specified ?
parseInt( attributeNode.value, 10 ) :
undefined;
}
function fixSizzleAttrHook() {
// in jQ <= 1.6.x, we add to Sizzle the attrHook from jQuery's attr method
sizzleAttrHandle[tabindexKey] = sizzleAttrHandle.tabIndex = getTabindexAttr;
}
function unfixSizzleAttrHook() {
delete sizzleAttrHandle[tabindexKey];
delete sizzleAttrHandle.tabIndex;
}
fixIndexSelector = {
enable: fixSizzleAttrHook,
disable: unfixSizzleAttrHook
};
})();
}
})( jQuery );

@ -48,11 +48,11 @@ jQuery(document).ready(function($) {
markup.push(OpenProject.Helpers.markupEscape(
item.name.substring(match + tl, item.name.length)));
return markup.join("");
}
};
formatItemSelection = function (item) {
return OpenProject.Helpers.markupEscape(item.name);
}
};
$("#members_add_form select.select2-select").each(function (ix, elem){
if ($(elem).hasClass("remote") || $(elem).attr("data-ajaxURL") !== undefined) {
@ -60,12 +60,12 @@ jQuery(document).ready(function($) {
if (!$.isEmptyObject(elem.siblings('div.select2-select.select2-container'))) {
setTimeout (function () {
var attributes, allowed, currentName, fakeInput;
attributes = {}
attributes = {};
allowed = ["title", "placeholder"];
for(var i = 0; i < $(elem).get(0).attributes.length; i++) {
currentName = $(elem).get(0).attributes[i].name;
if(currentName.indexOf("data-") == 0 || $.inArray(currentName, allowed)); //only ones starting with data-
if(currentName.indexOf("data-") === 0 || $.inArray(currentName, allowed)); //only ones starting with data-
attributes[currentName] = $(elem).attr(currentName);
}
fakeInput = $(elem).after("<input type='hidden'></input>").siblings(":input:first");
@ -88,7 +88,7 @@ jQuery(document).ready(function($) {
},
results: function (data, page) {
active_items = []
active_items = [];
data.results.items.each(function (e) {
e.name = $('<pre>').text(e.name).html();
active_items.push(e);
@ -107,10 +107,10 @@ jQuery(document).ready(function($) {
$(elem).select2();
}
});
}
};
memberstab = $('#tab-members').first();
if ((memberstab != null) && (memberstab.hasClass("selected"))) {
if ((memberstab !== null) && (memberstab.hasClass("selected"))) {
init_members_cb();
} else {
memberstab.click(init_members_cb);

@ -101,6 +101,10 @@ var ModalHelper = (function() {
this.hideLoadingModal();
this.loadingModal = false;
// use jquery.trap.js to keep the keyboard focus within the modal
// while it's open
body.trap();
body.on("keyup", function (e) {
if (e.which == 27) {
modalHelper.close();

@ -73,7 +73,7 @@
// environment and other global vars
/*jshint browser:true, devel:true*/
/*global jQuery:false, Raphael:false, Timeline:true*/
/*global jQuery:false, Raphael:false, Timeline:true, modalHelperInstance: true, I18n: true*/
if (typeof Timeline === "undefined") {
Timeline = {};

@ -1,4 +1,4 @@
7//-- copyright
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
//

@ -672,7 +672,7 @@ Timeline.PlanningElement = {
textColor = timeline.getLimunanceFor(color) > Timeline.PE_LUMINANCE_THRESHOLD ?
Timeline.PE_DARK_TEXT_COLOR : Timeline.PE_LIGHT_TEXT_COLOR;
var text = this.subject;
text = this.subject;
label = timeline.paper.text(0, 0, text);
label.attr({
'font-size': 12,

@ -0,0 +1,143 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// 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.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
(function($) {
var TypesCheckboxes = function () {
this.init();
};
TypesCheckboxes.prototype = $.extend(TypesCheckboxes.prototype, {
init: function () {
this.append_checkbox_listeners();
this.append_check_uncheck_all_listeners();
if (this.everything_unchecked()) {
this.check_and_disable_standard_type();
}
},
append_checkbox_listeners: function () {
var self = this;
this.all_checkboxes().on("change", function () {
if (self.everything_unchecked()) {
self.check_and_disable_standard_type();
self.display_explanation();
} else {
self.hide_explanation();
self.enable_standard_type();
}
});
},
append_check_uncheck_all_listeners: function () {
var self = this;
$("#project_types #check_all_types").click(function (event) {
self.enable_all_checkboxes();
self.check(self.all_checkboxes());
self.hide_explanation();
event.preventDefault();
});
$("#project_types #uncheck_all_types").click(function (event) {
self.enable_all_checkboxes();
self.uncheck(self.all_except_standard());
self.check_and_disable_standard_type();
self.display_explanation();
event.preventDefault();
});
},
everything_unchecked: function () {
return !(this.all_except_standard().filter(":checked").length > 0);
},
check_and_disable_standard_type: function () {
var standard = this.standard_check_boxes();
this.check($(standard));
this.disable($(standard));
},
enable_standard_type: function () {
this.enable(this.standard_check_boxes());
},
enable_all_checkboxes: function () {
this.enable(this.all_checkboxes())
},
check: function (boxes) {
$(boxes).prop("checked", true);
},
uncheck: function (boxes) {
$(boxes).prop("checked", false);
},
disable: function (boxes) {
var self = this;
$(boxes).prop('disabled', true);
$(boxes).each(function (ix, item) {
self.hidden_type_field($(item)).prop("value", $(item).prop("value"));
});
},
enable: function (boxes) {
var self = this;
$(boxes).prop('disabled', false);
$(boxes).each(function (ix, item) {
self.hidden_type_field($(item)).prop("value", "");
});
},
display_explanation: function () {
$("#types_flash_notice").show();
},
hide_explanation: function () {
$("#types_flash_notice").hide();
},
all_checkboxes: function () {
return $(".types :input[type='checkbox']");
},
all_except_standard: function () {
return $(".types :input[type='checkbox'][data-standard='false']");
},
standard_check_boxes: function () {
return $(".types :input[type='checkbox'][data-standard='true']");
},
hidden_type_field: function (for_box) {
return $(".types :input[type='hidden'][data-for='" + $(for_box).prop("id") + "']");
}
});
$('document').ready(function () {
new TypesCheckboxes();
});
})(jQuery);

@ -47,8 +47,8 @@ class ActivitiesController < ApplicationController
@activity = Redmine::Activity::Fetcher.new(User.current, :project => @project,
:with_subprojects => @with_subprojects,
:author => @author)
@activity.scope_select {|t| !params["show_#{t}"].nil?}
@activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
set_activity_scope
events = @activity.events(@date_from, @date_to)
censor_events_from_projects_with_disabled_activity!(events) unless @project
@ -100,4 +100,16 @@ class ActivitiesController < ApplicationController
event.project_id.nil? || allowed_project_ids.include?(event.project_id)
end
end
def set_activity_scope
if params[:apply]
@activity.scope_select {|t| !params["show_#{t}"].nil?}
elsif session[:activity]
@activity.scope = session[:activity]
else
@activity.scope = (@author.nil? ? :default : :all)
end
session[:activity] = @activity.scope
end
end

@ -38,6 +38,7 @@ module Api
before_filter :find_project_by_project_id,
:authorize, :except => [:index]
before_filter :parse_changed_since, only: [:index]
before_filter :assign_planning_elements, :except => [:index, :update, :create]
# Attention: find_all_projects_by_project_id needs to mimic all of the above
@ -91,7 +92,9 @@ module Api
def update
@planning_element = WorkPackage.find(params[:id])
@planning_element.attributes = permitted_params.planning_element
@planning_element.attributes = permitted_params.planning_element.except :note
@planning_element.add_journal(User.current, permitted_params.planning_element[:note])
successfully_updated = @planning_element.save
@ -207,6 +210,7 @@ module Api
def current_work_packages(projects)
work_packages = WorkPackage.for_projects(projects)
.changed_since(@since)
.includes(:status, :project, :type)
if params[:f]
@ -278,6 +282,12 @@ module Api
end
private
def parse_changed_since
@since = Time.at(Float(params[:changed_since] || 0).to_i) rescue render_400
end
end
end
end

@ -6,18 +6,24 @@ module Api
skip_filter :require_admin, :only => :index
before_filter :check_scope_supplied
def index
@users = UserSearchService.new(params).search.visible_by(User.current)
@users = UserSearchService.new(params).search
respond_to do |format|
format.api
end
end
end
end
private
def check_scope_supplied
render_400 if params.select { |k,v| UserSearchService::SEARCH_SCOPES.include? k }
.select { |k,v| not v.blank? }
.empty?
end
end
end
end

@ -0,0 +1,52 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
# resolves either a given status (show) or returns a list of available statuses
# if the controller is called nested inside a project, it returns only the
# statuses that can be reached by the workflows of the project
module Api
module V2
class WorkPackagePrioritiesController < ApplicationController
include PaginationHelper
include ::Api::V2::ApiController
unloadable
accept_key_auth :index
def index
@priorities = IssuePriority.all
respond_to do |format|
format.api
end
end
end
end
end

@ -685,7 +685,7 @@ class ApplicationController < ActionController::Base
private
def session_expired?
current_user.logged? &&
!api_request? && current_user.logged? &&
(session_ttl_enabled? && (session[:updated_at].nil? ||
(session[:updated_at] + Setting.session_ttl.to_i.minutes) < Time.now))
end

@ -36,6 +36,7 @@ class MyController < ApplicationController
menu_item :password, :only => [:password]
DEFAULT_BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_work_packages,
'workpackagesresponsiblefor' => :label_responsible_for_work_packages,
'issuesreportedbyme' => :label_reported_work_packages,
'issueswatched' => :label_watched_work_packages,
'news' => :label_news_latest,

@ -31,10 +31,9 @@ class NewsController < ApplicationController
include PaginationHelper
default_search_scope :news
model_object News
before_filter :disable_api
before_filter :find_model_object, :except => [:new, :create, :index]
before_filter :find_news_object, :except => [:new, :create, :index]
before_filter :find_project_from_association, :except => [:new, :create, :index]
before_filter :find_project, :only => [:new, :create]
before_filter :authorize, :except => [:index]
@ -96,6 +95,13 @@ class NewsController < ApplicationController
end
private
def find_news_object
@news = @object = News.find(params[:id].to_i)
rescue ActiveRecord::RecordNotFound
render_404
end
def find_project
@project = Project.find(params[:project_id])
rescue ActiveRecord::RecordNotFound

@ -43,7 +43,8 @@ class WorkPackages::BulkController < ApplicationController
@work_packages.sort!
@available_statuses = @projects.map{|p|Workflow.available_statuses(p)}.inject{|memo,w|memo & w}
@custom_fields = @projects.map{|p|p.all_work_package_custom_fields}.inject{|memo,c|memo & c}
@assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
@assignables = @projects.map(&:possible_assignees).inject{|memo,a| memo & a}
@responsibles = @projects.map(&:possible_responsibles).inject{|memo,a| memo & a}
@types = @projects.map(&:types).inject{|memo,t| memo & t}
end

@ -56,12 +56,15 @@ class WorkPackages::ContextMenusController < ApplicationController
:delete => User.current.allowed_to?(:delete_work_packages, @projects)
}
if @project
@assignables = @project.assignable_users
@assignables = @project.possible_assignees
@assignables << @work_package.assigned_to if @work_package && @work_package.assigned_to && !@assignables.include?(@work_package.assigned_to)
@responsibles = @project.possible_responsibles
@responsibles << @work_package.responsible if @work_package && @work_package.responsible && !@responsibles.include?(@work_package.responsible)
@types = @project.types
else
#when multiple projects, we only keep the intersection of each set
@assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
@assignables = @projects.map(&:possible_assignees).inject{|memo,a| memo & a}
@responsibles = @projects.map(&:possible_responsibles).inject{|memo,a| memo & a}
@types = @projects.map(&:types).inject{|memo,t| memo & t}
end

@ -37,6 +37,7 @@ class WorkPackages::ReportsController < ApplicationController
@type_report = reports_service.report_for("type")
@priority_report = reports_service.report_for("priority")
@assignee_report = reports_service.report_for("assigned_to")
@responsible_report = reports_service.report_for("responsible")
@author_report = reports_service.report_for("author")
@version_report = reports_service.report_for("version")
@subproject_report= reports_service.report_for("subproject")

@ -269,11 +269,7 @@ module ApplicationHelper
# Renders flash messages
def render_flash_messages
if User.current.impaired?
flash.map { |k,v| content_tag('div', content_tag('a', join_flash_messages(v), :href => 'javascript:;'), :class => "flash #{k} icon icon-#{k}") }.join.html_safe
else
flash.map { |k,v| content_tag('div', join_flash_messages(v), :class => "flash #{k} icon icon-#{k}") }.join.html_safe
end
flash.map { |k,v| render_flash_message(k, v) }.join.html_safe
end
def join_flash_messages(messages)
@ -284,6 +280,15 @@ module ApplicationHelper
end
end
def render_flash_message(type, message, html_options = {})
html_options = {:class => "flash #{type} icon icon-#{type}"}.merge(html_options)
if User.current.impaired?
content_tag('div', content_tag('a', join_flash_messages(message), :href => 'javascript:;'), html_options)
else
content_tag('div', join_flash_messages(message), html_options)
end
end
# Renders tabs and their content
def render_tabs(tabs)
if tabs.any?

@ -85,10 +85,6 @@ module TimelinesHelper
ProjectType.all.map { |t| [t.name, t.id] }
end
def options_for_responsible(project)
project.users.map { |u| [u.name, u.id] }
end
def visible_parent_project(project)
parent = project.parent

@ -509,12 +509,12 @@ module WorkPackagesHelper
def work_package_form_assignee_attribute(form, work_package, locals = {})
WorkPackageAttribute.new(:assignee,
form.select(:assigned_to_id, (work_package.assignable_users.map {|m| [m.name, m.id]}), :include_blank => true))
form.select(:assigned_to_id, (work_package.assignable_assignees.map {|m| [m.name, m.id]}), :include_blank => true))
end
def work_package_form_responsible_attribute(form, work_package, locals = {})
WorkPackageAttribute.new(:assignee,
form.select(:responsible_id, options_for_responsible(locals[:project]), :include_blank => true))
WorkPackageAttribute.new(:responsible,
form.select(:responsible_id, work_package.assignable_responsibles.map {|m| [m.name, m.id]}, :include_blank => true))
end
def work_package_form_category_attribute(form, work_package, locals = {})

@ -47,7 +47,7 @@ class Activity::WorkPackageActivityProvider < Activity::BaseActivityProvider
end
def self.work_package_title(id, subject, type_name, status_name, is_standard)
title = "#{(is_standard) ? l(:default_type) : "#{type_name}"} ##{id}: #{subject}"
title = "#{(is_standard) ? "" : "#{type_name}"} ##{id}: #{subject}"
title << " (#{status_name})" unless status_name.blank?
end

@ -412,7 +412,7 @@ class MailHandler < ActionMailer::Base
def find_assignee_from_keyword(keyword, issue)
keyword = keyword.to_s.downcase
assignable = issue.assignable_users
assignable = issue.assignable_assignees
assignee = nil
assignee ||= assignable.detect {|a|
a.mail.to_s.downcase == keyword ||

@ -36,10 +36,11 @@ class Member < ActiveRecord::Base
attr_protected :project_id, :user_id, :role_ids
validates_presence_of :principal, :project
validates_presence_of :project
validates_uniqueness_of :user_id, :scope => :project_id
validate :validate_presence_of_role
validate :validate_presence_of_principal
before_destroy :remove_from_category_assignments
after_destroy :unwatch_from_permission_change
@ -133,14 +134,18 @@ class Member < ActiveRecord::Base
def validate_presence_of_role
if member_roles.empty?
errors.add :roles, :empty if roles.empty?
errors.add :base, :role_blank if roles.empty?
else
errors.add :roles, :empty if member_roles.all? do |member_role|
errors.add :base, :role_blank if member_roles.all? do |member_role|
member_role.marked_for_destruction? || member_role.destroyed?
end
end
end
def validate_presence_of_principal
errors.add :base, :principal_blank if principal.blank?
end
def do_add_role(role_or_role_id, inherited_from_id, save_immediately)
id = (role_or_role_id.kind_of? Role) ? role_or_role_id.id : role_or_role_id

@ -89,6 +89,10 @@ class News < ActiveRecord::Base
new_comment(attributes).post!
end
def to_param
id && "#{id} #{title}".parameterize
end
private
def add_author_as_watcher

@ -46,10 +46,14 @@ class Project < ActiveRecord::Base
# Specific overidden Activities
has_many :time_entry_activities
has_many :members, :include => [:user, :roles], :conditions => "#{User.table_name}.type='User' AND #{User.table_name}.status=#{User::STATUSES[:active]}"
has_many :assignable_members,
has_many :possible_assignee_members,
:class_name => 'Member',
:include => [:principal, :roles],
:conditions => Proc.new { self.class.assignable_members_condition }
:conditions => Proc.new { self.class.possible_assignees_condition }
has_many :possible_responsible_members,
:class_name => 'Member',
:include => [:principal, :roles],
:conditions => Proc.new { self.class.possible_responsibles_condition }
has_many :memberships, :class_name => 'Member'
has_many :member_principals, :class_name => 'Member',
:include => :principal,
@ -235,7 +239,7 @@ class Project < ActiveRecord::Base
self.enabled_module_names = Setting.default_projects_modules
end
if !initialized.key?('types') && !initialized.key?('type_ids')
self.types = Type.where(is_default: true)
self.types = Type.default
end
end
@ -597,8 +601,13 @@ class Project < ActiveRecord::Base
end
# Users/groups a work_package can be assigned to
def assignable_users
assignable_members.map(&:principal).compact.sort
def possible_assignees
possible_assignee_members.map(&:principal).compact.sort
end
# Users who can become responsible for a work_package
def possible_responsibles
possible_responsible_members.map(&:principal).compact.sort
end
# Returns the mail adresses of users that should be always notified on project events
@ -916,7 +925,7 @@ class Project < ActiveRecord::Base
protected
def self.assignable_members_condition
def self.possible_assignees_condition
condition = Setting.work_package_group_assignment? ?
["(#{Principal.table_name}.type=? OR #{Principal.table_name}.type=?)", 'User', 'Group'] :
@ -928,4 +937,12 @@ class Project < ActiveRecord::Base
sanitize_sql_array condition
end
def self.possible_responsibles_condition
condition = ["(#{Principal.table_name}.type=? AND #{User.table_name}.status=? AND roles.assignable = ?)",
'User', User::STATUSES[:active], true]
sanitize_sql_array condition
end
end

@ -112,7 +112,9 @@ module Queries::WorkPackages::AvailableFilterOptions
role_values = Role.givable.collect {|r| [r.name, r.id.to_s] }
@available_work_package_filters["assigned_to_role"] = { type: :list_optional, order: 7, values: role_values, name: I18n.t('query_fields.assigned_to_role') } unless role_values.empty?
@available_work_package_filters["responsible_id"] = { type: :list_optional, order: 4, values: assigned_to_values } unless assigned_to_values.empty?
responsible_values = user_values.dup
responsible_values = [["<< #{l(:label_me)} >>", "me"]] + responsible_values if User.current.logged?
@available_work_package_filters["responsible_id"] = { type: :list_optional, order: 4, values: responsible_values } unless responsible_values.empty?
# watcher filters
if User.current.logged?

@ -62,7 +62,7 @@ class Query < ActiveRecord::Base
QueryColumn.new(:subject, :sortable => "#{WorkPackage.table_name}.subject"),
QueryColumn.new(:author),
QueryColumn.new(:assigned_to, :sortable => ["#{User.table_name}.lastname", "#{User.table_name}.firstname", "#{User.table_name}.id"], :groupable => true),
QueryColumn.new(:responsible, sortable: ["responsible.lastname", "responsible.firstname", "responsible.id"], groupable: "#{WorkPackage.table_name}.responsible_id", :join => "LEFT OUTER JOIN users as responsible ON (#{WorkPackage.table_name}.responsible_id = responsible.id)"),
QueryColumn.new(:responsible, sortable: ["#{User.table_name}.lastname", "#{User.table_name}.firstname", "#{User.table_name}.id"], groupable: "#{WorkPackage.table_name}.responsible_id", :join => "LEFT OUTER JOIN users as responsible ON (#{WorkPackage.table_name}.responsible_id = responsible.id)"),
QueryColumn.new(:updated_at, :sortable => "#{WorkPackage.table_name}.updated_at", :default_order => 'desc'),
QueryColumn.new(:category, :sortable => "#{Category.table_name}.name", :groupable => true),
QueryColumn.new(:fixed_version, :sortable => ["#{Version.table_name}.effective_date", "#{Version.table_name}.name"], :default_order => 'desc', :groupable => true),

@ -89,6 +89,10 @@ class Type < ActiveRecord::Base
Type.where(is_standard: true).first
end
def self.default
Type.where(is_default: true)
end
def statuses
return [] if new_record?
@statuses ||= Type.statuses([id])

@ -78,6 +78,10 @@ class WorkPackage < ActiveRecord::Base
{:conditions => {:project_id => projects}}
}
scope :changed_since, lambda { |changed_since|
changed_since ? where(["#{WorkPackage.table_name}.updated_at >= ?", changed_since]) : nil
}
# >>> issues.rb >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
scope :open, :conditions => ["#{Status.table_name}.is_closed = ?", false], :include => :status
@ -371,8 +375,15 @@ class WorkPackage < ActiveRecord::Base
end
end
# Users/groups the work_package can be assigned to
def assignable_assignees
project.possible_assignees
end
# Users the work_package can be assigned to
delegate :assignable_users, :to => :project
def assignable_responsibles
project.possible_responsibles
end
# Versions that the work_package can be assigned to
# A work_package can be assigned to:
@ -387,7 +398,7 @@ class WorkPackage < ActiveRecord::Base
end
def to_s
"#{(kind.is_standard) ? l(:default_type) : "#{kind.name}"} ##{id}: #{subject}"
"#{(kind.is_standard) ? "" : "#{kind.name}"} ##{id}: #{subject}"
end
# Return true if the work_package is closed, otherwise false
@ -886,6 +897,12 @@ class WorkPackage < ActiveRecord::Base
:joins => User.table_name)
end
def self.by_responsible(project)
count_and_group_by(:project => project,
:field => 'responsible_id',
:joins => User.table_name)
end
def self.by_author(project)
count_and_group_by(:project => project,
:field => 'author_id',
@ -917,7 +934,7 @@ class WorkPackage < ActiveRecord::Base
end
def add_time_entry_for(user, attributes)
return if attributes.nil? || attributes.values.all?(&:blank?)
return if time_entry_blank?(attributes)
attributes.reverse_merge!({ :user => user,
:spent_on => Date.today })
@ -925,6 +942,23 @@ class WorkPackage < ActiveRecord::Base
time_entries.build(attributes)
end
##
# Checks if the time entry defined by the given attributes is blank.
# A time entry counts as blank despite a selected activity if that activity
# is simply the default activity and all other attributes are blank.
def time_entry_blank?(attributes)
return true if attributes.nil?
key = "activity_id"
id = attributes[key]
default_id = if id && !id.blank?
Enumeration.exists? :id => id, :is_default => true, :type => 'TimeEntryActivity'
else
true
end
default_id && attributes.except(key).values.all?(&:blank?)
end
# >>> issues.rb >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# this removes all attachments separately before destroying the issue
# avoids getting a ActiveRecord::StaleObjectError when deleting an issue

@ -46,6 +46,7 @@ class Reports::ReportsService
add_report Reports::SubprojectReport
add_report Reports::AuthorReport
add_report Reports::AssigneeReport
add_report Reports::ResponsibleReport
add_report Reports::TypeReport
add_report Reports::PriorityReport
add_report Reports::CategoryReport
@ -64,4 +65,4 @@ class Reports::ReportsService
report_klass.new(@project) if report_klass
end
end
end

@ -26,44 +26,26 @@
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require File.expand_path('../../../test_helper', __FILE__)
class Reports::ResponsibleReport < Reports::Report
class News::CommentsControllerTest < ActionController::TestCase
fixtures :all
def setup
super
User.current = nil
def self.report_type
"responsible"
end
def test_add_comment
@request.session[:user_id] = 2
post :create, :news_id => 1, :comment => { :comments => 'This is a test comment' }
assert_redirected_to '/news/1'
comment = News.find(1).comments.reorder('created_on DESC').first
assert_not_nil comment
assert_equal 'This is a test comment', comment.comments
assert_equal User.find(2), comment.author
def field
@field ||= "responsible_id"
end
def test_empty_comment_should_not_be_added
@request.session[:user_id] = 2
assert_no_difference 'Comment.count' do
post :create, :news_id => 1, :comment => { :comments => '' }
assert_response :redirect
assert_redirected_to '/news/1'
end
def rows
@rows ||= @project.members.collect { |m| m.user }.sort
end
def test_destroy_comment
@request.session[:user_id] = 2
news = News.find(1)
assert_difference 'Comment.count', -1 do
delete :destroy, :id => 2
end
def data
@data ||= WorkPackage.by_responsible(@project)
end
assert_redirected_to '/news/1'
assert_nil Comment.find_by_id(2)
def title
@title ||= WorkPackage.human_attribute_name(:responsible)
end
end

@ -1,6 +1,13 @@
class UserSearchService
attr_accessor :params
SEARCH_SCOPES = [
'ids',
'group_id',
'status',
'name'
]
def initialize(params)
self.params = params
end
@ -46,4 +53,4 @@ class UserSearchService
# .order(sort_clause)
end
end
end

@ -93,6 +93,7 @@ See doc/COPYRIGHT.rdoc for more details.
<p><label><%= check_box_tag 'with_subprojects', 1, @with_subprojects %> <%=l(:label_subproject_plural)%></label></p>
<% end %>
<%= hidden_field_tag('user_id', params[:user_id]) unless params[:user_id].blank? %>
<%= hidden_field_tag('apply', true) %>
<p><%= submit_tag l(:button_apply), :class => 'button-small', :name => nil %></p>
<% end %>
<% end %>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title(l(:label_administration), l(:label_information_plural)) -%>
<%= content_tag :h2, I18n.t('label_information_plural') %>
<%= call_hook(:view_admin_info_top) %>
@ -43,4 +45,3 @@ See doc/COPYRIGHT.rdoc for more details.
<% end %>
</table>
<%= call_hook(:view_admin_info_bottom) %>
<% html_title(l(:label_information_plural)) -%>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), l(:label_plugins) %>
<h2><%= l(:label_plugins) %></h2>
<% if @plugins.any? %>

@ -35,6 +35,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= link_to l(:label_project_new), {:controller => '/projects', :action => 'new'}, :class => 'icon icon-add' %>
<% end %>
<% html_title(l(:label_administration), l(:label_project_plural)) -%>
<h2><%=l(:label_project_plural)%></h2>
<%= render :partial => 'layouts/action_menu_specific' %>
@ -90,5 +92,3 @@ See doc/COPYRIGHT.rdoc for more details.
</tbody>
</table>
</div>
<% html_title(l(:label_project_plural)) -%>

@ -0,0 +1,32 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# 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.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
collection @priorities => :work_package_priorities
attributes :id,
:name,
:position,
:is_default

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), "#{l(:label_edit)} #{l(:label_auth_source)} #{@auth_source.name}" %>
<h2><%=l(:label_auth_source)%> (<%= @auth_source.auth_method_name %>)</h2>
<%= labelled_tabular_form_for @auth_source, :as => :auth_source do |f| %>

@ -31,6 +31,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= link_to l(:label_auth_source_new), { :action => 'new' }, :class => 'icon icon-add' %>
<% end %>
<% html_title l(:label_administration), l(:label_auth_source_plural) %>
<h2><%=l(:label_auth_source_plural)%></h2>
<%= render :partial => 'layouts/action_menu_specific' %>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), l(:label_auth_source_new) %>
<h2><%=l(:label_auth_source_new)%> (<%= @auth_source.auth_method_name %>)</h2>
<%= labelled_tabular_form_for @auth_source, :as => :auth_source do |f| %>

@ -32,6 +32,6 @@ See doc/COPYRIGHT.rdoc for more details.
<div class="box">
<p><%= f.text_field :name, :size => 30, :required => true %></p>
<p>
<%= f.select :assigned_to_id, @project.assignable_users.sort.collect{|u| [u.name, u.id]}, :include_blank => true %>
<%= f.select :assigned_to_id, @project.possible_assignees.sort.collect{|u| [u.name, u.id]}, :include_blank => true %>
</p>
</div>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), "#{l(:label_edit)} #{CustomField.model_name.human} #{@custom_field.name}" %>
<h2><%= link_to l(:label_custom_field_plural), :controller => '/custom_fields', :action => 'index' %>
&#187; <%= link_to l(@custom_field.type_name), :controller => '/custom_fields', :action => 'index', :tab => @custom_field.type %>
&#187; <%=h @custom_field.name %></h2>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), l(:label_custom_field_new) %>
<h2><%= link_to l(:label_custom_field_plural), :controller => '/custom_fields', :action => 'index' %>
&#187; <%= link_to l(@custom_field.type_name), :controller => '/custom_fields', :action => 'index', :tab => @custom_field.type %>
&#187; <%= l(:label_custom_field_new) %></h2>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), "#{l(:label_edit)} #{Enumeration.model_name.human} #{@enumeration.name}" %>
<h2><%= link_to l(@enumeration.option_name), enumerations_path %> &#187; <%=h @enumeration %></h2>
<%= labelled_tabular_form_for @enumeration do |f| %>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), l(:label_enumerations) %>
<h2><%=l(:label_enumerations)%></h2>
<% Enumeration.descendants.each do |klass| %>
@ -62,5 +64,3 @@ See doc/COPYRIGHT.rdoc for more details.
<p><%= link_to l(:label_enumeration_new), { :action => 'new', :type => klass.name } %></p>
<% end %>
<% html_title(l(:label_enumerations)) -%>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), l(:label_enumeration_new) %>
<h2><%= link_to l(@enumeration.option_name), enumerations_path %> &#187; <%=l(:label_enumeration_new)%></h2>
<%= labelled_tabular_form_for @enumeration do |f| %>

@ -27,8 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), "#{l(:label_edit)} #{Group.model_name.human} #{@group.name}" %>
<h2><%= link_to l(:label_group_plural), groups_path %> &#187; <%= h(@group) %></h2>
<%= render_tabs group_settings_tabs %>
<% html_title( Group.model_name.human, @group, l(:label_administration)) -%>

@ -31,6 +31,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= link_to l(:label_group_new), new_group_path, :class => 'icon icon-add' %>
<% end %>
<% html_title l(:label_administration), l("label_group_plural") %>
<h2><%= l(:label_group_plural) %></h2>
<%= render :partial => 'layouts/action_menu_specific' %>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), l("label_group_new") %>
<h2><%= link_to l(:label_group_plural), groups_path %> &#187; <%= l(:label_group_new) %></h2>
<%= labelled_tabular_form_for(@group) do |f| %>

@ -39,43 +39,45 @@ See doc/COPYRIGHT.rdoc for more details.
:builder => TabularFormBuilder,
:lang => current_language,
:html => { :id => 'my_account_form' } do |f| %>
<div class="splitcontentleft">
<h3><%=l(:label_information_plural)%></h3>
<div class="box tabular">
<p><%= f.text_field :firstname, :required => true %></p>
<p><%= f.text_field :lastname, :required => true %></p>
<p><%= f.text_field :mail, :required => true %></p>
<p><%= f.select :language, lang_options_for_select %></p>
<% if Setting.openid? %>
<p><%= f.text_field :identity_url %></p>
<% end %>
<% @user.custom_field_values.select(&:editable?).each do |value| %>
<p><%= custom_field_tag_with_label :user, value %></p>
<% end %>
<%= call_hook(:view_my_account, :user => @user, :form => f) %>
</div>
<%= submit_tag l(:button_save) %>
</div>
<div class="splitcontentright">
<h3><%= User.human_attribute_name(:mail_notification) %></h3>
<div class="box">
<%= render :partial => 'users/mail_notifications' %>
</div>
<h3><%=l(:label_ui, :app_title => Setting.app_title)%></h3>
<div class="box tabular">
<%= render :partial => 'users/impaired_settings' %>
</div>
<h3><%=l(:label_preferences)%></h3>
<div class="box tabular">
<%= render :partial => 'users/preferences' %>
</div>
</div>
<div>
<div class="splitcontentleft">
<h3><%=l(:label_information_plural)%></h3>
<div class="box tabular">
<p><%= f.text_field :firstname, :required => true %></p>
<p><%= f.text_field :lastname, :required => true %></p>
<p><%= f.text_field :mail, :required => true %></p>
<p><%= f.select :language, lang_options_for_select %></p>
<% if Setting.openid? %>
<p><%= f.text_field :identity_url %></p>
<% end %>
<% @user.custom_field_values.select(&:editable?).each do |value| %>
<p><%= custom_field_tag_with_label :user, value %></p>
<% end %>
<%= call_hook(:view_my_account, :user => @user, :form => f) %>
</div>
</div>
<div class="splitcontentright">
<h3><%= User.human_attribute_name(:mail_notification) %></h3>
<div class="box">
<%= render :partial => 'users/mail_notifications' %>
</div>
<h3><%=l(:label_ui, :app_title => Setting.app_title)%></h3>
<div class="box tabular">
<%= render :partial => 'users/impaired_settings' %>
</div>
<h3><%=l(:label_preferences)%></h3>
<div class="box tabular">
<%= render :partial => 'users/preferences' %>
</div>
</div>
<br style="clear:both;" />
</div>
<%= submit_tag l(:button_save) %>
<% end %>
<% html_title(l(:label_my_account)) -%>

@ -0,0 +1,61 @@
<%#-- copyright
OpenProject is a project management system.
Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2013 Jean-Philippe Lang
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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See doc/COPYRIGHT.rdoc for more details.
++#%>
<% responsible = WorkPackage.visible.open.find(:all,
:conditions => {:responsible_id => User.current.id},
:limit => 10,
:include => [ :status, :project, :type, :priority ],
:order => "#{IssuePriority.table_name}.position DESC, #{WorkPackage.table_name}.updated_at DESC") %>
<% responsible_count = WorkPackage.visible.open.count(:conditions => {
:responsible_id => User.current.id
}) %>
<h3><%=l(:label_responsible_for_work_packages)%> (<%= responsible_count %>)</h3>
<%= render :partial => 'work_packages/list_simple', :locals => { :work_packages => responsible } %>
<% if responsible.length > 0 %>
<p class="small">
<%= link_to l(:label_work_package_view_all_responsible_for),
{controller: :work_packages,
action: :index,
set_filter: 1,
responsible_id: 'me',
sort: 'priority:desc,updated_at:desc'},
:class => 'button' %>
</p>
<% end %>
<% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom,
{controller: :work_packages, action: :index, set_filter: 1,
responsible_id: 'me', format: 'atom', key: User.current.rss_key},
{title: l(:label_responsible_for_work_packages)}) %>
<% end %>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_news_edit) %>
<h2><%= News.model_name.human %></h2>
<%= labelled_tabular_form_for @news, :html => { :id => 'news-form' } do |f| %>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_news_new) %>
<h2><%=l(:label_news_new)%></h2>
<%= labelled_tabular_form_for [@project, @news], :html => { :id => 'news-form' } do |f| %>

@ -29,6 +29,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= header_tags %>
<% html_title l(:label_administration), "#{l(:label_edit)} #{l("timelines.admin_menu.color")} #{@color.name}" %>
<%= form_for :color,
:url => color_url(@color),
:html => {:method => 'put'} do |f| %>

@ -36,7 +36,8 @@ See doc/COPYRIGHT.rdoc for more details.
:class => 'icon icon-add') %>
<% end %>
<% html_title l("timelines.admin_menu.colors") %>
<% html_title l(:label_administration), l("timelines.admin_menu.colors") %>
<h2><%= l('timelines.admin_menu.colors') %></h2>
<%= render :partial => 'layouts/action_menu_specific' %>

@ -29,6 +29,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= header_tags %>
<% html_title l(:label_administration), "#{l(:label_new)} #{l("timelines.admin_menu.color")}" %>
<%= form_for :color,
:url => colors_url,
:html => {:method => 'post'} do |f| %>

@ -29,6 +29,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= header_tags %>
<% html_title l(:label_administration), "#{l(:label_edit)} #{l("timelines.admin_menu.project_type")} #{@project_type.name}" %>
<%= labelled_tabular_form_for(@project_type,
:url => project_type_path(@project_type),
:html => {:method => 'put'}) do |f| %>

@ -36,7 +36,8 @@ See doc/COPYRIGHT.rdoc for more details.
:class => 'icon icon-add') %>
<% end %>
<% html_title l("timelines.admin_menu.project_types") %>
<% html_title l(:label_administration), l("timelines.admin_menu.project_types") %>
<h2><%= l("timelines.admin_menu.project_types") %></h2>
<%= render :partial => 'layouts/action_menu_specific' %>

@ -29,6 +29,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= header_tags %>
<% html_title l(:label_administration), "#{l(:label_new)} #{l("timelines.admin_menu.project_type")}" %>
<%= labelled_tabular_form_for(@project_type,
:url => project_types_path,
:html => {:method => 'post'}) do |f| %>

@ -46,10 +46,7 @@ See doc/COPYRIGHT.rdoc for more details.
<% if (project.new_record? || project.module_enabled?('issue_tracking')) %>
<% if renderTypes %>
<fieldset class="box" id="project_types"><legend><%=l(:label_type_plural)%> <span style="font-size:0.9em">(<%= check_all_links 'project_types' %>)</span></legend>
<%= render :partial => 'projects/form/types', :locals => { :f => f, :project => project } %>
<%= hidden_field_tag 'project[type_ids][]', '' %>
</fieldset>
<% end %>
<% end %>

@ -27,45 +27,64 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<table class='list'>
<thead>
<tr>
<th width="90px" class='center'><%= Type.human_attribute_name(:active) %></th>
<th><%= Type.human_attribute_name(:name) %></th>
<th class='center'><%= Type.human_attribute_name(:in_aggregation) %></th>
<th class='center'><%= Type.human_attribute_name(:is_in_roadmap) %></th>
<th class='center'><%= Type.human_attribute_name(:is_milestone) %></th>
</tr>
</thead>
<%= javascript_include_tag 'types_checkboxes' %>
<tbody>
<% Type.all.each do |type| %>
<tr class="<%= cycle('odd', 'even', :name => "pet_table") %>">
<td class='center'>
<%= check_box_tag "project[type_ids][]",
type.id,
project.types.include?(type),
:id => "project_planning_element_type_ids_#{type.id}" %>
<label class='hidden-for-sighted' for="project_planning_element_type_ids_<%= type.id %>">
<%= l('timelines.enable_type_in_project', :type => type.name) %>
</label>
</td>
<td>
<label for="project_planning_element_type_ids_<%= type.id %>">
<%= icon_for_type(type) %>
<%=h type.name %>
</label>
</td>
<td class='center'>
<%= checked_image(type.in_aggregation) %>
</td>
<td class='center'>
<%= checked_image(type.is_in_roadmap) %>
</td>
<td class='center'>
<%= checked_image(type.is_milestone) %>
</td>
<%= render_flash_message :notice,
l(:notice_automatic_set_of_standard_type),
style: "display:none;", id: "types_flash_notice" %>
<fieldset class="box" id="project_types">
<legend><%=l(:label_type_plural)%>
<span style="font-size:0.9em">
(<%= link_to(l(:button_check_all), "#", id: "check_all_types") +
' | ' +
link_to(l(:button_uncheck_all), "#", id: "uncheck_all_types")
%>)
</span>
</legend>
<table class='list types'>
<thead>
<tr>
<th width="90px" class='center'><%= Type.human_attribute_name(:active) %></th>
<th><%= Type.human_attribute_name(:name) %></th>
<th class='center'><%= Type.human_attribute_name(:in_aggregation) %></th>
<th class='center'><%= Type.human_attribute_name(:is_in_roadmap) %></th>
<th class='center'><%= Type.human_attribute_name(:is_milestone) %></th>
</tr>
<% end %>
</tbody>
</table>
</thead>
<tbody>
<% Type.all.each do |type| %>
<tr class="<%= cycle('odd', 'even', :name => "pet_table") %>">
<td class='center'>
<% type_id = "project_planning_element_type_ids_#{type.id}" %>
<%= check_box_tag "project[type_ids][]",
type.id,
project.types.include?(type),
:id => type_id,
:'data-standard' => type.is_standard %>
<%= hidden_field_tag 'project[type_ids][]', '', :'data-for' => type_id %>
<label class='hidden-for-sighted' for=<%= type_id %>>
<%= l('timelines.enable_type_in_project', :type => type.name) %>
</label>
</td>
<td>
<label for="project_planning_element_type_ids_<%= type.id %>">
<%= icon_for_type(type) %>
<%=h type.name %>
</label>
</td>
<td class='center'>
<%= checked_image(type.in_aggregation) %>
</td>
<td class='center'>
<%= checked_image(type.is_in_roadmap) %>
</td>
<td class='center'>
<%= checked_image(type.is_milestone) %>
</td>
</tr>
<% end %>
</tbody>
</table>
</fieldset>

@ -30,7 +30,7 @@ See doc/COPYRIGHT.rdoc for more details.
<p>
<% if project && project.persisted? %>
<% if User.current.impaired? %>
<%= form.select :responsible_id, options_for_responsible(project), :include_blank => true %>
<%= form.select :responsible_id, project.possible_responsibles.map {|m| [m.name, m.id]}, :include_blank => true %>
<% else %>
<% options = { :'data-ajaxURL' => url_for({:controller => "/members",
:action => "paginate_users" }),

@ -39,6 +39,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= link_to l(:label_overall_activity), { :controller => '/activities', :action => 'index' }%>
<% end %>
<% html_title(l(:label_project_plural)) -%>
<h2><%=l(:label_project_plural)%></h2>
<%= render :partial => 'layouts/action_menu_specific' %>
@ -58,5 +60,3 @@ See doc/COPYRIGHT.rdoc for more details.
<%= other_formats_links do |f| %>
<%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
<% end %>
<% html_title(l(:label_project_plural)) -%>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l("label_project_new") %>
<h2><%=l(:label_project_new)%></h2>
<%= labelled_tabular_form_for @project do |f| %>

@ -34,7 +34,6 @@ See doc/COPYRIGHT.rdoc for more details.
:url => { :action => 'types', :id => @project },
:method => :put,
:html => {:id => 'types-form'} do |f| %>
<%=l(:label_type_plural)%> <span style="font-size:0.9em">(<%= check_all_links 'project_types' %>)</span>
<%= render :partial => 'projects/form/types', :locals => { :f => f, :project => @project } %>
<p><%= submit_tag l(:button_save) %></p>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), "#{l(:label_edit)} #{Role.model_name.human} #{@role.name}" %>
<h2><%= link_to l(:label_role_plural), roles_path %> &#187; <%=h @role.name %></h2>
<%= labelled_tabular_form_for @role, :html => { :id => 'role_form' }, :as => :role do |f| %>

@ -31,6 +31,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= link_to l(:label_role_new), {:action => 'new'}, :class => 'icon icon-add' %>
<% end %>
<% html_title l(:label_administration), l("label_role_plural") %>
<h2><%=l(:label_role_plural)%></h2>
<%= render :partial => 'layouts/action_menu_specific' %>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), l("label_group_new") %>
<h2><%= link_to l(:label_role_plural), roles_path %> &#187; <%=l(:label_role_new)%></h2>
<%= labelled_tabular_form_for @role, :html => {:id => 'role_form'}, :as => :role do |f| %>

@ -27,8 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), l(:label_settings) -%>
<h2><%= l(:label_settings) %></h2>
<%= render_tabs administration_settings_tabs %>
<% html_title(l(:label_settings), l(:label_administration)) -%>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), "#{l(:label_edit)} #{Status.model_name.human} #{@status.name}" %>
<h2><%= link_to l(:label_work_package_status_plural), :controller => '/statuses', :action => 'index' %> &#187; <%=h @status %></h2>
<%= labelled_tabular_form_for(@status) do |f| %>

@ -32,6 +32,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= link_to(l(:label_update_work_package_done_ratios), update_work_package_done_ratio_statuses_path, :class => 'icon icon-reload2', :method => 'post', :confirm => l(:text_are_you_sure)) if WorkPackage.use_status_for_done_ratio? %>
<% end %>
<% html_title l(:label_administration), l(:label_work_package_status_plural) -%>
<h2><%=l(:label_work_package_status_plural)%></h2>
<%= render :partial => 'layouts/action_menu_specific' %>
@ -66,5 +68,3 @@ See doc/COPYRIGHT.rdoc for more details.
</table>
<%= pagination_links_full @statuses %>
<% html_title(l(:label_work_package_status_plural)) -%>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), l(:label_work_package_status_new) %>
<h2><%= link_to l(:label_work_package_status_plural), :controller => '/statuses', :action => 'index' %> &#187; <%=l(:label_work_package_status_new)%></h2>
<%= labelled_tabular_form_for(@status) do |f| %>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l("timelines.delete_timeline") %>
<%= header_tags %>
<%= form_for :timeline,

@ -27,9 +27,9 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<h2>
<%=l('timelines.new_timeline')%>
</h2>
<% html_title l("timelines.new_timeline") %>
<h2><%=l('timelines.new_timeline')%></h2>
<%= form_for(:timeline,
:url => project_timelines_path,

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), "#{l(:label_edit)} #{Type.model_name.human} #{@type.name}" %>
<h2><%= link_to t(:label_type_plural), types_path %> &#187; <%= @type.name %></h2>
<%= form_for @type, :builder => TabularFormBuilder do |f| %>

@ -33,6 +33,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= link_to l(:label_type_new), {:action => 'new'}, :class => 'icon icon-add' %>
<% end %>
<% html_title l(:label_administration), l("label_type_plural") %>
<h2><%=l(:label_type_plural)%></h2>
<%= render :partial => 'layouts/action_menu_specific' %>
@ -82,5 +84,3 @@ See doc/COPYRIGHT.rdoc for more details.
</table>
<%= pagination_links_full @types %>
<% html_title(l(:label_type_plural)) -%>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), l(:label_type_new) %>
<h2><%= link_to t(:label_type_plural), types_path %> &#187; <%= t(:label_type_new) %></h2>
<%= form_for controller.type, :builder => TabularFormBuilder do |f| %>

@ -34,6 +34,7 @@ See doc/COPYRIGHT.rdoc for more details.
<li><%= WorkPackage.human_attribute_name(:status) %>: <%= issue.status %></li>
<li><%= WorkPackage.human_attribute_name(:priority) %>: <%= issue.priority %></li>
<li><%= WorkPackage.human_attribute_name(:assigned_to) %>: <%= issue.assigned_to %></li>
<li><%= WorkPackage.human_attribute_name(:responsible) %>: <%= issue.responsible %></li>
<li><%= WorkPackage.human_attribute_name(:category) %>: <%= issue.category %></li>
<li><%= WorkPackage.human_attribute_name(:fixed_version) %>: <%= issue.fixed_version %></li>

@ -34,6 +34,7 @@ See doc/COPYRIGHT.rdoc for more details.
<%= WorkPackage.human_attribute_name(:status) %>: <%= issue.status %>
<%= WorkPackage.human_attribute_name(:priority) %>: <%= issue.priority %>
<%= WorkPackage.human_attribute_name(:assigned_to) %>: <%= issue.assigned_to %>
<%= WorkPackage.human_attribute_name(:responsible) %>: <%= issue.responsible %>
<%= WorkPackage.human_attribute_name(:category) %>: <%= issue.category %>
<%= WorkPackage.human_attribute_name(:fixed_version) %>: <%= issue.fixed_version %>

@ -34,10 +34,10 @@ See doc/COPYRIGHT.rdoc for more details.
<% end %>
<% end %>
<% html_title(l(:label_administration), "#{l(:label_edit)} #{User.model_name.human} #{h(@user.login)}") -%>
<h2><%= link_to l(:label_user_plural), :controller => '/users', :action => 'index' %> &#187; <%=h @user.login %></h2>
<%= render :partial => 'layouts/action_menu_specific' %>
<%= render_tabs user_settings_tabs %>
<% html_title(User.model_name.human, h(@user.login), l(:label_administration)) -%>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_administration), l("label_user_new") %>
<h2><%= link_to l(:label_user_plural), :controller => '/users', :action => 'index' %> &#187; <%=l(:label_user_new)%></h2>
<%= labelled_tabular_form_for @user,

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l("label_roadmap_edit", name: @version.name) %>
<h2><%= Version.model_name.human %></h2>
<%= labelled_tabular_form_for @version do |f| %>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l("label_version_new") %>
<h2><%=l(:label_version_new)%></h2>
<%= labelled_tabular_form_for [@project, @version] do |f| %>

@ -37,6 +37,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= watcher_link(@wiki, User.current) %>
<% end %>
<% html_title l(:label_wiki_toc) %>
<h2><%= l(:label_index_by_title) %></h2>
<%= render :partial => 'layouts/action_menu_specific' %>

@ -41,7 +41,7 @@ See doc/COPYRIGHT.rdoc for more details.
<th><%= WorkPackage.human_attribute_name(:type)%></th>
<th><%= WorkPackage.human_attribute_name(:start_date)%></th>
<th><%= WorkPackage.human_attribute_name(:due_date)%></th>
<th><spen class="hidden-for-sighted"><%= t('button_delete') %></span></th>
<th><span class="hidden-for-sighted"><%= t('button_delete') %></span></th>
</tr>
</thead>
<tbody>

@ -59,6 +59,12 @@ See doc/COPYRIGHT.rdoc for more details.
content_tag('option', l(:label_nobody), :value => 'none') +
options_from_collection_for_select(@assignables, :id, :name)) %>
</p>
<p>
<label for='work_package_responsible_id'><%= WorkPackage.human_attribute_name(:responsible) %></label>
<%= select_tag('work_package[responsible_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_nobody), :value => 'none') +
options_from_collection_for_select(@responsibles, :id, :name)) %>
</p>
<% if @project %>
<p>
<label for='category_id'><%= WorkPackage.human_attribute_name(:category) %></label>

@ -81,7 +81,7 @@ See doc/COPYRIGHT.rdoc for more details.
<% end %>
<% if @assignables.present? -%>
<% assignables = @assignables.dup << [nil, l(:label_none)] %>
<% assignables = @assignables.dup << ["none", l(:label_none)] %>
<% params = default_params.merge(:collection => assignables,
:attribute => 'assigned_to',
:selected => lambda { |user| @work_package && user == @work_package.assigned_to },
@ -89,8 +89,18 @@ See doc/COPYRIGHT.rdoc for more details.
<%= context_menu_entry(params) %>
<% end %>
<% if @responsibles.present? -%>
<% responsibles = @responsibles.dup << ["none", l(:label_none)] %>
<% params = default_params.merge(:collection => responsibles,
:attribute => 'responsible',
:selected => lambda { |user| @work_package && user == @work_package.responsible },
:disabled => lambda { |user| !@can[:update] }) %>
<%= context_menu_entry(params) %>
<% end %>
<% unless @project.nil? || (categories = @project.categories.to_a).empty? -%>
<% categories << [nil, l(:label_none)] %>
<% categories << ["none", l(:label_none)] %>
<% params = default_params.merge(:collection => categories,
:attribute => 'category',
:selected => lambda { |category| @work_package && category == @work_package.category },

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_work_package_edit, name: work_package.to_s) %>
<h2><%=h work_package.to_s %></h2>
<%= render :partial => 'edit', :locals => { :work_package => work_package,

@ -78,14 +78,14 @@ See doc/COPYRIGHT.rdoc for more details.
<%= select_tag('assigned_to_id',
content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_nobody), :value => 'none') +
options_from_collection_for_select(@target_project.assignable_users, :id, :name)) %>
options_from_collection_for_select(@target_project.possible_assignees, :id, :name)) %>
</p>
<p>
<label for='responsible_id'><%= WorkPackage.human_attribute_name(:responsible) %></label>
<%= select_tag('responsible_id',
content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_nobody), :value => 'none') +
options_from_collection_for_select(@target_project.assignable_users, :id, :name)) %>
options_from_collection_for_select(@target_project.possible_responsibles, :id, :name)) %>
</p>
</div>

@ -27,6 +27,8 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_work_package_new) %>
<h2><%=l(:label_work_package_new)%></h2>

@ -27,11 +27,13 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<% html_title l(:label_summary) %>
<h2><%=l(:label_summary)%></h2>
<div class="splitcontentleft">
<%
[@type_report, @priority_report, @assignee_report, @author_report].each do |report| %>
[@type_report, @priority_report, @assignee_report, @responsible_report, @author_report].each do |report| %>
<%= render :partial => 'report_category', :locals => { report: report} %>
<% end %>
<%= call_hook(:view_reports_issue_report_split_content_left, :project => @project) %>

@ -29,7 +29,9 @@ See doc/COPYRIGHT.rdoc for more details.
<%= render :partial => 'action_menu' %>
<h2><%= Workflow.model_name.human %></h2>
<% html_title l(:label_administration), l(:label_workflow_plural) -%>
<h2><%= l(:label_workflow_plural) %></h2>
<%= render :partial => 'layouts/action_menu_specific' %>

@ -235,6 +235,9 @@ de:
reused:
one: "wurde bereits verwendet. Bitte wählen Sie eines, das sich vom letzten unterscheidet."
other: "wurde bereits verwendet. Bitte wählen Sie eines, das sich von den letzten %{count} unterscheidet."
member:
principal_blank: "Bitte wählen Sie mindestens einen Benutzer oder eine Gruppe."
role_blank: "Bitte wählen Sie eine mindestens Rolle."
template:
body: "Bitte überprüfen Sie die folgenden Felder:"
header:
@ -249,7 +252,7 @@ de:
group: "Gruppe"
issue: "Ticket"
category: "Kategorie"
status: "Ticket-Status"
status: "Arbeitspaket-Status"
member: "Mitglied"
news: "News"
project: "Projekt"
@ -696,7 +699,7 @@ de:
label_enable_multi_select: "Mehrfachauswahl umschalten"
label_end_to_end: "Ende - Ende"
label_end_to_start: "Ende - Anfang"
label_enumeration_new: "Neuer Wert"
label_enumeration_new: "Neuer Aufzählungswert"
label_enumerations: "Aufzählungen"
label_environment: "Umgebung"
label_equals: "ist"
@ -742,6 +745,7 @@ de:
label_work_package_category_plural: "Arbeitspaket-Kategorien"
label_work_package_hierarchy: "Arbeitspaket-Hierarchie"
label_work_package_new: "Neues Arbeitspaket"
label_work_package_edit: "Arbeitspaket %{name} editieren"
label_work_package_plural: "Arbeitspakete"
label_work_package_status_new: "Neuer Status"
label_work_package_status_plural: "Arbeitspaket-Status"
@ -750,6 +754,7 @@ de:
label_work_package_view_all: "Alle Tickets anzeigen"
label_work_package_view_all_assigned_to_me: "Alle mir zugewiesenen Arbeitspakete anzeigen"
label_work_package_view_all_reported_by_me: "Alle von mir erstellten Arbeitspakete anzeigen"
label_work_package_view_all_responsible_for: "Alle von mir verantworteten Arbeitspakete anzeigen"
label_work_package_view_all_watched: "Alle beobachteten Arbeitspakete anzeigen"
label_work_package_watchers: "Beobachter"
label_work_packages_by: "Arbeitspakete von %{value}"
@ -807,6 +812,7 @@ de:
label_news_comment_added: "Kommentar einer News hinzugefügt"
label_news_latest: "Letzte News"
label_news_new: "News hinzufügen"
label_news_edit: "News bearbeiten"
label_news_plural: "News"
label_news_view_all: "Alle News anzeigen"
label_next: "Weiter"
@ -878,12 +884,14 @@ de:
label_reporting_plural: "Statusberichte"
label_repository: "Projektarchiv"
label_repository_plural: "Projektarchive"
label_responsible_for_work_packages: "Von mir verantwortete Arbeitspakete"
label_result_plural: "Resultate"
label_reverse_chronological_order: "in umgekehrter zeitlicher Reihenfolge"
label_revision: "Revision"
label_revision_id: "Revision %{value}"
label_revision_plural: "Revisionen"
label_roadmap: "Roadmap"
label_roadmap_edit: "Roadmap %{name} editieren"
label_roadmap_due_in: "Fällig in %{value}"
label_roadmap_no_work_packages: "Keine Arbeitspakete für diese Version"
label_roadmap_overdue: "%{value} verspätet"
@ -964,6 +972,7 @@ de:
label_week: "Woche"
label_wiki_content_added: "Die Wiki-Seite wurde erfolgreich hinzugefügt."
label_wiki_content_updated: "Die Wiki-Seite wurde erfolgreich aktualisiert."
label_wiki_toc: "Table of Contents"
label_wiki_dont_show_menu_item: "Wikiseite nicht in der Projektnavigation anzeigen"
label_wiki_edit: "Wiki-Bearbeitung"
label_wiki_edit_plural: "Wiki-Bearbeitungen"
@ -978,6 +987,7 @@ de:
label_work_package_plural: "Arbeitspakete"
label_work_package_new: "Neues Arbeitspaket"
label_workflow: "Workflow"
label_workflow_plural: "Workflows"
label_workflow_summary: "Zusammenfassung"
label_x_closed_work_packages_abbr:
one: "1 geschlossen"
@ -1128,6 +1138,7 @@ de:
permission_change_wiki_parent_page: "Übergeordnete Wiki Seite ändern"
permission_comment_news: "News kommentieren"
permission_commit_access: "Commit-Zugriff (über WebDAV)"
permission_copy_projects: "Projekte kopieren"
permission_delete_work_package_watchers: "Beobachter löschen"
permission_delete_work_packages: "Arbeitspakete löschen"
permission_delete_messages: "Forenbeiträge löschen"
@ -1164,6 +1175,7 @@ de:
permission_manage_subtasks: "Unteraufgaben verwalten"
permission_manage_versions: "Versionen verwalten"
permission_manage_wiki: "Wiki verwalten"
permission_manage_wiki_menu: "Wiki-Menü verwalten"
permission_move_work_packages: "Arbeitspakete verschieben"
permission_protect_wiki_pages: "Wiki-Seiten schützen"
permission_rename_wiki_pages: "Wiki-Seiten umbenennen"
@ -1384,7 +1396,9 @@ de:
timelines:
admin_menu:
color: "Farbe"
colors: "Farben"
project_type: "Projekt-Typ"
project_types: "Projekt-Typen"
add_project_association: "Projekt-Abhängigkeit erstellen"
associations: "Abhängigkeiten"
@ -1406,6 +1420,7 @@ de:
edit_project_type: "Projekt-Typ bearbeiten"
edit_thing: "Bearbeiten"
edit_timeline: "Zeitplan-Report bearbeiten"
delete_timeline: "Zeitplan-Report löschen"
empty: "(leer)"
enable_type_in_project: 'Typ "%{type}" aktivieren'
end: "Abschluss"

@ -233,6 +233,9 @@ en:
reused:
one: "has been used before. Please choose one that is different from your last one."
other: "has been used before. Please choose one that is different from your last %{count}."
member:
principal_blank: "Please choose at least one user or group."
role_blank: "Please choose at least one role."
template:
body: "Please check the following fields:"
header:
@ -247,7 +250,7 @@ en:
group: "Group"
issue: "Issue"
category: "Category"
status: "Issue status"
status: "Work package status"
member: "Member"
news: "News"
project: "Project"
@ -693,7 +696,7 @@ en:
label_enable_multi_select: "Toggle multiselect"
label_end_to_end: "end to end"
label_end_to_start: "end to start"
label_enumeration_new: "New value"
label_enumeration_new: "New enumeration value"
label_enumerations: "Enumerations"
label_environment: "Environment"
label_equals: "is"
@ -739,6 +742,7 @@ en:
label_work_package_category_plural: "Work package categories"
label_work_package_hierarchy: "Work package hierarchy"
label_work_package_new: "New work package"
label_work_package_edit: "Edit work package %{name}"
label_work_package_plural: "Work packages"
label_work_package_status_new: "New status"
label_work_package_status_plural: "Work package statuses"
@ -747,6 +751,7 @@ en:
label_work_package_view_all: "View all work packages"
label_work_package_view_all_assigned_to_me: "View all work packages assigned to me"
label_work_package_view_all_reported_by_me: "View all work packages reported by me"
label_work_package_view_all_responsible_for: "View all work packages I am responsible for"
label_work_package_view_all_watched: "View all watched work packages"
label_work_package_watchers: "Watchers"
label_work_packages_by: "Work packages by %{value}"
@ -804,6 +809,7 @@ en:
label_news_comment_added: "Comment added to a news"
label_news_latest: "Latest news"
label_news_new: "Add news"
label_news_edit: "Edit news"
label_news_plural: "News"
label_news_view_all: "View all news"
label_next: "Next"
@ -875,12 +881,14 @@ en:
label_reporting_plural: "Reportings"
label_repository: "Repository"
label_repository_plural: "Repositories"
label_responsible_for_work_packages: "Work packages I am responsible for"
label_result_plural: "Results"
label_reverse_chronological_order: "In reverse chronological order"
label_revision: "Revision"
label_revision_id: "Revision %{value}"
label_revision_plural: "Revisions"
label_roadmap: "Roadmap"
label_roadmap_edit: "Edit roadmap %{name}"
label_roadmap_due_in: "Due in %{value}"
label_roadmap_no_work_packages: "No work packages for this version"
label_roadmap_overdue: "%{value} late"
@ -961,6 +969,7 @@ en:
label_week: "Week"
label_wiki_content_added: "Wiki page added"
label_wiki_content_updated: "Wiki page updated"
label_wiki_toc: "Table of Contents"
label_wiki_dont_show_menu_item: "Do not show this wikipage in project navigation"
label_wiki_edit: "Wiki edit"
label_wiki_edit_plural: "Wiki edits"
@ -975,6 +984,7 @@ en:
label_work_package_plural: "Work packages"
label_work_package_new: "New work package"
label_workflow: "Workflow"
label_workflow_plural: "Workflows"
label_workflow_summary: "Summary"
label_x_closed_work_packages_abbr:
one: "1 closed"
@ -1115,6 +1125,7 @@ en:
permission_change_wiki_parent_page: "Change parent wiki page"
permission_comment_news: "Comment news"
permission_commit_access: "Commit access"
permission_copy_projects: "Copy projects"
permission_delete_work_package_watchers: "Delete watchers"
permission_delete_work_packages: "Delete work packages"
permission_delete_messages: "Delete messages"
@ -1151,6 +1162,7 @@ en:
permission_manage_subtasks: "Manage subtasks"
permission_manage_versions: "Manage versions"
permission_manage_wiki: "Manage wiki"
permission_manage_wiki_menu: "Manage wiki menu"
permission_move_work_packages: "Move work packages"
permission_protect_wiki_pages: "Protect wiki pages"
permission_rename_wiki_pages: "Rename wiki pages"
@ -1368,7 +1380,9 @@ en:
timelines:
admin_menu:
color: "Color"
colors: "Colors"
project_type: "Project type"
project_types: "Project types"
project_menu:
planning_elements: "Planning elements"
@ -1396,6 +1410,7 @@ en:
edit_project_type: "Edit project type"
edit_thing: "Edit"
edit_timeline: "Edit timeline report"
delete_timeline: "Delete timeline report"
empty: "(empty)"
enable_type_in_project: 'Enable type "%{type}"'
end: "End"
@ -1454,7 +1469,7 @@ en:
hide_chart: "Hide chart"
noneElement: "(none)"
noneSelection: "(none)"
outline: "Intitial outline expansion"
outline: "Initial outline expansion"
parent: "Show subprojects of"
work_package_filters: "Filter work packages"
work_package_responsible: "Show work packages with responsible"

@ -78,6 +78,7 @@ OpenProject::Application.routes.draw do
resources :reported_project_statuses
resources :statuses, :only => [:index, :show]
resources :timelines
resources :work_package_priorities, only: [:index]
resources :projects do
resources :planning_elements

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save