Merge branch 'feature/rails3' into fix/timelines_html_safe_and_enumerations

pull/188/head
Hagen Schink 12 years ago
commit 046b1691d7
  1. 5
      Gemfile.lock
  2. 42
      app/assets/javascripts/application.js
  3. 42
      app/assets/javascripts/modal.js
  4. 29
      app/assets/javascripts/openproject.js
  5. 34
      app/assets/javascripts/timelines.js
  6. 16
      app/assets/javascripts/timelines_autocompleter.js
  7. 2
      app/assets/javascripts/timelines_select_boxes.js
  8. 23
      app/assets/stylesheets/default/application.css.erb
  9. 2
      app/assets/stylesheets/timelines.css
  10. 2
      app/controllers/account_controller.rb
  11. 60
      app/controllers/api/v1/api_controller.rb
  12. 151
      app/controllers/api/v1/issues_controller.rb
  13. 34
      app/controllers/api/v1/news_controller.rb
  14. 107
      app/controllers/api/v1/projects_controller.rb
  15. 105
      app/controllers/api/v1/timelog_controller.rb
  16. 157
      app/controllers/api/v1/users_controller.rb
  17. 32
      app/controllers/api/v2/api_controller.rb
  18. 29
      app/controllers/api/v2/authentication_controller.rb
  19. 29
      app/controllers/api/v2/planning_element_journals_controller.rb
  20. 40
      app/controllers/api/v2/planning_element_statuses_controller.rb
  21. 35
      app/controllers/api/v2/planning_element_type_colors_controller.rb
  22. 35
      app/controllers/api/v2/planning_element_types_controller.rb
  23. 113
      app/controllers/api/v2/planning_elements_controller.rb
  24. 45
      app/controllers/api/v2/project_associations_controller.rb
  25. 35
      app/controllers/api/v2/project_types_controller.rb
  26. 51
      app/controllers/api/v2/projects_controller.rb
  27. 35
      app/controllers/api/v2/reported_project_statuses_controller.rb
  28. 168
      app/controllers/api/v2/reportings_controller.rb
  29. 35
      app/controllers/api/v2/scenarios_controller.rb
  30. 20
      app/controllers/api/v2/timelines_controller.rb
  31. 56
      app/controllers/application_controller.rb
  32. 5
      app/controllers/auth_sources_controller.rb
  33. 5
      app/controllers/authentication_controller.rb
  34. 19
      app/controllers/boards_controller.rb
  35. 7
      app/controllers/issue_statuses_controller.rb
  36. 39
      app/controllers/issues_controller.rb
  37. 4
      app/controllers/journals_controller.rb
  38. 13
      app/controllers/messages_controller.rb
  39. 26
      app/controllers/news_controller.rb
  40. 8
      app/controllers/planning_element_journals_controller.rb
  41. 10
      app/controllers/planning_element_statuses_controller.rb
  42. 32
      app/controllers/planning_element_type_colors_controller.rb
  43. 27
      app/controllers/planning_element_types_controller.rb
  44. 76
      app/controllers/planning_elements_controller.rb
  45. 17
      app/controllers/principals_controller.rb
  46. 51
      app/controllers/project_associations_controller.rb
  47. 35
      app/controllers/project_types_controller.rb
  48. 45
      app/controllers/projects_controller.rb
  49. 13
      app/controllers/reported_project_statuses_controller.rb
  50. 65
      app/controllers/reportings_controller.rb
  51. 13
      app/controllers/repositories_controller.rb
  52. 7
      app/controllers/roles_controller.rb
  53. 23
      app/controllers/scenarios_controller.rb
  54. 3
      app/controllers/time_entries/reports_controller.rb
  55. 62
      app/controllers/timelines/timelines_projects_controller.rb
  56. 33
      app/controllers/timelines_controller.rb
  57. 34
      app/controllers/timelog_controller.rb
  58. 7
      app/controllers/trackers_controller.rb
  59. 30
      app/controllers/users_controller.rb
  60. 14
      app/controllers/wiki_controller.rb
  61. 76
      app/helpers/application_helper.rb
  62. 8
      app/helpers/issues_helper.rb
  63. 2
      app/helpers/journals_helper.rb
  64. 103
      app/helpers/pagination_helper.rb
  65. 4
      app/helpers/planning_elements_helper.rb
  66. 2
      app/helpers/projects_helper.rb
  67. 9
      app/helpers/timelines_helper.rb
  68. 4
      app/helpers/timelines_journals_helper.rb
  69. 10
      app/models/alternate_date.rb
  70. 10
      app/models/available_project_status.rb
  71. 10
      app/models/default_planning_element_type.rb
  72. 8
      app/models/enabled_planning_element_type.rb
  73. 1
      app/models/issue.rb
  74. 2
      app/models/mail_handler.rb
  75. 86
      app/models/planning_element.rb
  76. 2
      app/models/planning_element_scenario.rb
  77. 4
      app/models/planning_element_status.rb
  78. 22
      app/models/planning_element_type.rb
  79. 8
      app/models/planning_element_type_color.rb
  80. 2
      app/models/principal.rb
  81. 90
      app/models/project.rb
  82. 17
      app/models/project_association.rb
  83. 14
      app/models/project_type.rb
  84. 12
      app/models/query.rb
  85. 6
      app/models/reported_project_status.rb
  86. 14
      app/models/reporting.rb
  87. 8
      app/models/scenario.rb
  88. 26
      app/models/timeline.rb
  89. 27
      app/models/user.rb
  90. 4
      app/views/api/v1/issues/index.api.rsb
  91. 0
      app/views/api/v1/issues/show.api.rsb
  92. 4
      app/views/api/v1/news/index.api.rsb
  93. 4
      app/views/api/v1/projects/index.api.rsb
  94. 0
      app/views/api/v1/projects/level_list.api.rsb
  95. 0
      app/views/api/v1/projects/show.api.rsb
  96. 0
      app/views/api/v1/timelog/index.api.rsb
  97. 0
      app/views/api/v1/timelog/show.api.rsb
  98. 4
      app/views/api/v1/users/index.api.rsb
  99. 0
      app/views/api/v1/users/show.api.rsb
  100. 0
      app/views/api/v2/authentication/index.api.rsb
  101. Some files were not shown because too many files have changed in this diff Show More

@ -92,6 +92,8 @@ GEM
activerecord (>= 3.0.0)
binding_of_caller (0.7.1)
debug_inspector (>= 0.0.1)
bourne (1.4.0)
mocha (~> 0.13.2)
builder (3.0.4)
bullet (4.6.0)
uniform_notifier
@ -101,6 +103,8 @@ GEM
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
capybara-screenshot (0.3.6)
capybara (>= 1.0, < 3)
childprocess (0.3.9)
ffi (~> 1.0, >= 1.0.11)
coderay (1.0.9)
@ -364,6 +368,7 @@ DEPENDENCIES
awesome_nested_set
bullet
capybara
capybara-screenshot
coderay (~> 1.0.5)
coffee-rails (~> 3.2.1)
color-tools (~> 1.3.0)

@ -49,6 +49,14 @@ if (typeof []._reverse == 'undefined') {
jQuery.fn.reverse = Array.prototype._reverse;
}
jQuery(document).ajaxError(function(event, request, settings) {
if (request.status === 401 && /Reason: login needed/.match(request.getAllResponseHeaders())) {
if (confirm(I18n.t("js.logoff") + "\r\n" + I18n.t("js.redirect_login"))) {
location.href = openProject.loginUrl + "?back_url=" + encodeURIComponent(location.href);
}
}
});
function checkAll (id, checked) {
var els = Element.descendants(id);
@ -462,6 +470,7 @@ var WarnLeavingUnsaved = Class.create({
document.observe("dom:loaded", function() {
Ajax.Responders.register({
onCreate: function(request){
debugger;
var csrf_meta_tag = $$('meta[name=csrf-token]')[0];
if (csrf_meta_tag) {
@ -547,8 +556,39 @@ jQuery.viewportHeight = function() {
document.body.clientHeight;
};
/* TODO: integrate with existing code and/or refactor */
+
+ /*
+ * 1 - registers a callback which copies the csrf token into the
+ * X-CSRF-Token header with each ajax request. Necessary to
+ * work with rails applications which have fixed
+ * CVE-2011-0447
+ * 2 - shows and hides ajax indicator
+ */
jQuery(document).ready(function($) {
document.ajaxActive = false;
$(document).ajaxSend(function (event, request) {
document.ajaxActive = true;
var csrf_meta_tag = $('meta[name=csrf-token]');
if (csrf_meta_tag) {
var header = 'X-CSRF-Token',
token = csrf_meta_tag.attr('content');
request.setRequestHeader[header] = token;
}
if ($('#ajax-indicator')) {
$('#ajax-indicator').show();
}
});
// ajaxStop gets called when ALL Requests finish, so we won't need a counter as in PT
$(document).ajaxStop(function () {
document.ajaxActive = false;
if ($('#ajax-indicator')) {
$('#ajax-indicator').hide();
}
addClickEventToAllErrorMessages();
});
var propagateOpenClose = function () {
if ($(this).is(":visible")) {

@ -69,10 +69,18 @@ var ModalHelper = (function() {
var modalHelper = this;
var timeline = modalHelper.timeline;
var url = modalHelper.options.url_prefix +
modalHelper.options.project_prefix +
projectId + '/planning_elements/';
var submiturl = url
var non_api_url = modalHelper.options.url_prefix +
modalHelper.options.project_prefix +
"/" +
projectId +
'/planning_elements/';
var api_url = modalHelper.options.url_prefix +
modalHelper.options.api_prefix +
modalHelper.options.project_prefix +
"/" +
projectId +
'/planning_elements/';
if (typeof elementId === 'function') {
callback = elementId;
@ -85,13 +93,14 @@ var ModalHelper = (function() {
if (type === 'new') {
url += 'new.js';
non_api_url += 'new.js';
} else if (type === 'edit') {
if (typeof elementId === 'string' || typeof elementId === 'number') {
url += elementId + '/edit.js';
submiturl += elementId + '.json';
non_api_url += elementId + '/edit.js';
api_url += elementId + '.json';
} else {
@ -101,7 +110,7 @@ var ModalHelper = (function() {
} else if (type === 'show') {
if (typeof elementId === 'string' || typeof elementId === 'number') {
url += elementId + '.js';
non_api_url += elementId + '.js';
} else {
@ -115,7 +124,7 @@ var ModalHelper = (function() {
}
//create the modal by using the html the url gives us.
modalHelper.createModal(url, function(ele) {
modalHelper.createModal(non_api_url, function(ele) {
var projects = timeline.projects;
var project;
var projectSelect;
@ -129,12 +138,15 @@ var ModalHelper = (function() {
if (type === 'new') {
submiturl = modalHelper.options.url_prefix +
modalHelper.options.project_prefix +
projectSelect.val() + '/planning_elements.json';
api_url = modalHelper.options.url_prefix +
modalHelper.options.api_prefix +
modalHelper.options.project_prefix +
"/" +
projectSelect.val() +
'/planning_elements.json';
}
modalHelper.submitBackground(jQuery(this), submiturl, function(err, res) {
modalHelper.submitBackground(jQuery(this), api_url, function(err, res) {
var element;
modalHelper.hideLoadingModal();
@ -307,7 +319,9 @@ var ModalHelper = (function() {
var tokenName, token, action, data = {};
var url = modalHelper.options.url_prefix +
modalHelper.options.project_prefix +
projectId + '/planning_elements/';
"/" +
projectId +
'/planning_elements/';
tokenName = jQuery('meta[name=csrf-param]').attr('content');
token = jQuery('meta[name=csrf-token]').attr('content');

@ -17,6 +17,8 @@ window.OpenProject = (function ($) {
options = options || {};
this.urlRoot = options.urlRoot || "";
this.loginUrl = options.loginUrl || "";
if (!/\/$/.test(this.urlRoot)) {
this.urlRoot += '/';
}
@ -65,7 +67,7 @@ window.OpenProject = (function ($) {
}
if (!url) {
url = this.getFullUrl("/projects/level_list.json");
url = this.getFullUrl("/api/v1/projects/level_list.json");
}
if (this.projects) {
@ -115,6 +117,24 @@ window.OpenProject = (function ($) {
return (str+'').replace(REGEXP_ESCAPE, "\\$1");
};
/**
* Use select2's escapeMarkup function for correctly escaping
* text and preventing XSS.
*/
Helpers.markupEscape = (function(){
try {
var escapeMarkup = jQuery.fn.select2.defaults.escapeMarkup;
if(typeof escapeMarkup === "undefined") {
throw 'jQuery.fn.select2.defaults.escapeMarkup is undefined';
}
return escapeMarkup;
} catch (e){
console.log('Error: jQuery.fn.select2.defaults.escapeMarkup not found.\n' +
'Exception: ' + e.toString());
throw e;
}
}());
/**
* replace wrong with right in text
*
@ -231,12 +251,13 @@ window.OpenProject = (function ($) {
// fallback to base behavior
if (result.matches === undefined) {
return replaceSpecialChars(format(result.text, query.term));
return replaceSpecialChars(
Helpers.markupEscape(format(result.text, query.term)));
}
// shortcut for empty searches
if (query.sterm.length === 0) {
return result.text;
return Helpers.markupEscape(result.text);
}
var matches = result.matches.slice(),
@ -248,7 +269,7 @@ window.OpenProject = (function ($) {
text = Helpers.replace(text, match[0], format(match[0], match[1]));
}
return replaceSpecialChars(text);
return replaceSpecialChars(Helpers.markupEscape(text));
};
})();

@ -44,13 +44,13 @@ Timeline = {
columns: [],
exclude_own_planning_elements: false,
exclude_reporters: false,
global_prefix: '/timelines/',
api_prefix: '/api/v2',
hide_other_group: false,
hide_tree_root: false,
i18n: {}, // undefined would be bad.
initial_outline_expansion: 0, // aggregations only
project_prefix: '/timelines/projects/',
planning_element_prefix: '/timelines',
project_prefix: '/projects',
planning_element_prefix: '',
ui_root: jQuery('#timeline'),
url_prefix: '' // empty prefix so it is not undefined.
},
@ -267,8 +267,8 @@ Timeline = {
this.modalHelper = new ModalHelper(
this,
{
api_prefix : this.options.api_prefix,
url_prefix : this.options.url_prefix,
global_prefix : this.options.global_prefix,
project_prefix : this.options.project_prefix
}
);
@ -277,8 +277,8 @@ Timeline = {
timelineLoader = new Timeline.TimelineLoader(
this,
{
api_prefix : this.options.api_prefix,
url_prefix : this.options.url_prefix,
global_prefix : this.options.global_prefix,
project_prefix : this.options.project_prefix,
planning_element_prefix : this.options.planning_element_prefix,
project_id : this.options.project_id,
@ -320,8 +320,8 @@ Timeline = {
var timelineLoader = new Timeline.TimelineLoader(
this,
{
api_prefix : this.options.api_prefix,
url_prefix : this.options.url_prefix,
global_prefix : this.options.global_prefix,
project_prefix : this.options.project_prefix,
planning_element_prefix : this.options.planning_element_prefix,
project_id : this.options.project_id,
@ -880,7 +880,6 @@ Timeline = {
* The following list describes the required options
*
* url_prefix : timeline.options.url_prefix,
* global_prefix : timeline.options.global_prefix,
* project_prefix : timeline.options.project_prefix,
* project_id : timeline.options.project_id,
*
@ -903,7 +902,7 @@ Timeline = {
this.loader = new QueueingLoader(options.ajax_defaults);
this.dataEnhancer = new DataEnhancer(timeline);
this.globalPrefix = options.url_prefix + options.global_prefix,
this.globalPrefix = options.url_prefix + options.api_prefix;
jQuery(this.loader).on('success', jQuery.proxy(this, 'onLoadSuccess'))
.on('error', jQuery.proxy(this, 'onLoadError'))
@ -942,7 +941,9 @@ Timeline = {
TimelineLoader.prototype.registerProjectReportings = function () {
var projectPrefix = this.options.url_prefix +
this.options.api_prefix +
this.options.project_prefix +
"/" +
this.options.project_id;
var url = projectPrefix + '/reportings.json?only=via_target';
@ -971,22 +972,21 @@ Timeline = {
url += '&grouping_two=' + this.options.grouping_two.join();
}
this.loader.register(
Timeline.Reporting.identifier,
{ url : url });
this.loader.register(Timeline.Reporting.identifier,
{ url : url });
},
TimelineLoader.prototype.registerGlobalElements = function () {
this.loader.register(
Timeline.PlanningElementType.identifier,
{ url : this.globalPrefix + 'planning_element_types.json' });
{ url : this.globalPrefix + '/planning_element_types.json' });
this.loader.register(
Timeline.Color.identifier,
{ url : this.globalPrefix + 'colors.json' });
{ url : this.globalPrefix + '/colors.json' });
this.loader.register(
Timeline.ProjectType.identifier,
{ url : this.globalPrefix + 'project_types.json' });
{ url : this.globalPrefix + '/project_types.json' });
};
TimelineLoader.prototype.registerProjects = function (ids) {
@ -996,7 +996,7 @@ Timeline = {
this.loader.register(
Timeline.Project.identifier + '_' + i,
{ url : this.globalPrefix +
'projects.json?ids=' +
'/projects.json?ids=' +
project_ids_of_packet.join(',')},
{ storeIn : Timeline.Project.identifier }
);
@ -1007,7 +1007,9 @@ Timeline = {
this.inChunks(ids, function (projectIdsOfPacket, i) {
var projectPrefix = this.options.url_prefix +
this.options.api_prefix +
this.options.project_prefix +
"/" +
projectIdsOfPacket.join(',');
// load current planning elements.
@ -1848,6 +1850,7 @@ Timeline = {
var url = options.url_prefix;
url += options.project_prefix;
url += "/"
url += this.identifier;
url += "/timelines";
@ -2230,6 +2233,7 @@ Timeline = {
var url = options.url_prefix;
url += options.project_prefix;
url += "/";
url += this.getProject().identifier;
url += "/planning_elements/";
url += this.id;

@ -124,18 +124,22 @@
markup = [];
if (match < 0) {
return "<span data-value='" + item.id + "'>" + item.name + "</span>";
return "<span data-value='" + item.id + "'>" +
OpenProject.Helpers.markupEscape(item.name) + "</span>";
}
markup.push(item.name.substring(0, match));
markup.push(OpenProject.Helpers.markupEscape(
item.name.substring(0, match)));
markup.push("<span class='select2-match' data-value='" + item.id + "'>");
markup.push(item.name.substring(match, match + tl));
markup.push(OpenProject.Helpers.markupEscape(
item.name.substring(match, match + tl)));
markup.push("</span>");
markup.push(item.name.substring(match + tl, item.name.length));
return markup.join("")
markup.push(OpenProject.Helpers.markupEscape(
item.name.substring(match + tl, item.name.length)));
return markup.join("");
},
formatSelection: function (item) {
return item.name;
return OpenProject.Helpers.markupEscape(item.name);
},
initSelection: function (element, callback) {
var data = [];

@ -9,7 +9,7 @@
// See doc/COPYRIGHT.rdoc for more details.
//++
//requires 'timelines_autocompleter'
//requires 'autocompleter'
jQuery(document).ready(function($) {
[

@ -95,6 +95,7 @@ h4, .wiki h3 {font-size: 13px;padding: 0px 10px 1px 0px;margin-bottom: 5px; bord
padding: 0;
right: 2px;
clear: both;
padding-right: 15px;
}
#login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
@ -2782,18 +2783,36 @@ ul.action_menu_more {
background-position:-39px -200px;
background-repeat:no-repeat;
}
.pagination .navigate-left {
.pagination .previous_page {
background: url(<%= asset_path 'double_arrow_left.png' %>) 0 1px no-repeat;
width:100%;
height:15px;
padding-left: 18px;
}
.pagination .navigate-right {
.pagination .next_page {
background: url(<%= asset_path 'double_arrow_right.png' %>) right 1px no-repeat;
width:100%;
height:15px;
padding-right: 18px;
}
.pagination .next_page.disabled,
.pagination .previous_page.disabled {
display: none;
}
.pagination .range,
.pagination .per_page_options {
padding-left: 0.5em;
}
.pagination .per_page_options {
border-left: 1px solid #4B4B4B;
margin-left: 0.5em;
}
#menu-sidebar.hidden {
display:none;
}

@ -68,7 +68,7 @@ li.select2-result.select2-visible-selected-parent {
.tl-space-both, .tl-space-before {
margin-left: 1em;
}
#content .timelines_edit_form fieldset.collapsible.header_collapsible {
#content .edit_form fieldset.collapsible.header_collapsible {
margin-bottom: 1em;
}

@ -169,7 +169,7 @@ class AccountController < ApplicationController
user.login = registration['nickname'] unless registration['nickname'].nil?
user.mail = registration['email'] unless registration['email'].nil?
user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil?
user.random_password
user.random_password!
user.register
case Setting.self_registration

@ -0,0 +1,60 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V1
module ApiController
module ClassMethods
def included(base)
base.class_eval do
if ((respond_to? :skip_before_filter) &&
(respond_to? :prepend_before_filter))
skip_before_filter :disable_api
prepend_before_filter :disable_everything_except_api
end
end
end
def permeate_permissions(*filter_names)
filter_names.each do |filter_name|
define_method filter_name do |*args, &block|
begin
original_controller = params[:controller]
params[:controller] = original_controller.gsub(api_version, "")
result = super(*args, &block)
ensure
params[:controller] = original_controller
end
result
end
end
end
end
extend ClassMethods
def api_version
/api\/v1\//
end
permeate_permissions :authorize
permeate_permissions :authorize_for_user
permeate_permissions :check_if_deletion_allowed
permeate_permissions :find_optional_project
permeate_permissions :find_project
permeate_permissions :find_time_entry
end
end
end

@ -0,0 +1,151 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V1
class IssuesController < IssuesController
include ::Api::V1::ApiController
def index
sort_init(@query.sort_criteria.empty? ? [DEFAULT_SORT_ORDER] : @query.sort_criteria)
sort_update(@query.sortable_columns)
if @query.valid?
@issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
:order => sort_clause)
.page(page_param)
.per_page(per_page_param)
@issue_count_by_group = @query.issue_count_by_group
respond_to do |format|
format.api
end
else
# Send html if the query is not valid
render(:template => 'issues/index', :layout => !request.xhr?)
end
rescue ActiveRecord::RecordNotFound
render_404
end
def show
@journals = @issue.journals.changing.find(:all, :include => [:user, :journaled], :order => "#{Journal.table_name}.created_at ASC")
@journals.reverse! if User.current.wants_comments_in_reverse_order?
@changesets = @issue.changesets.visible.all(:include => [{ :repository => {:project => :enabled_modules} }, :user])
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
@relations = @issue.relations(:include => { :other_issue => [:status,
:priority,
:tracker,
{ :project => :enabled_modules }]
}
).select{ |r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
@ancestors = @issue.ancestors.visible.all(:include => [:tracker,
:assigned_to,
:status,
:priority,
:fixed_version,
:project])
@descendants = @issue.descendants.visible.all(:include => [:tracker,
:assigned_to,
:status,
:priority,
:fixed_version,
:project])
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
respond_to do |format|
format.api
end
end
def create
call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
IssueObserver.instance.send_notification = params[:send_notification] == '0' ? false : true
if @issue.save
attachments = Attachment.attach_files(@issue, params[:attachments])
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_successful_create)
call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
respond_to do |format|
format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
end
return
else
respond_to do |format|
format.api { render_validation_errors(@issue) }
end
end
end
def update
update_issue_from_params
JournalObserver.instance.send_notification = params[:send_notification] == '0' ? false : true
if @issue.save_issue_with_child_records(params, @time_entry)
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_successful_update) unless @issue.current_journal == @journal
respond_to do |format|
format.api { head :ok }
end
else
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_successful_update) unless @issue.current_journal == @journal
@journal = @issue.current_journal
respond_to do |format|
format.api { render_validation_errors(@issue) }
end
end
end
def destroy
@hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
if @hours > 0
case params[:todo]
when 'destroy'
# nothing to do
when 'nullify'
TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
when 'reassign'
reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
if reassign_to.nil?
flash.now[:error] = l(:error_issue_not_found_in_project)
return
else
TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
end
else
# display the destroy form if it's a user request
return unless api_request?
end
end
@issues.each do |issue|
begin
issue.reload.destroy
rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists
# nothing to do, issue was already deleted (eg. by a parent)
end
end
respond_to do |format|
format.api { head :ok }
end
end
end
end
end

@ -0,0 +1,34 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V1
class NewsController < NewsController
include ::Api::V1::ApiController
def index
scope = @project ? @project.news.visible : News.visible
@newss = scope.includes(:author, :project)
.order("#{News.table_name}.created_on DESC")
.page(page_param)
.per_page(per_page_param)
respond_to do |format|
format.api
end
end
end
end
end

@ -0,0 +1,107 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V1
class ProjectsController < ProjectsController
include ::Api::V1::ApiController
include PaginationHelper
def index
@projects = Project.visible.order('lft')
.page(page_param)
.per_page(per_page_param)
respond_to do |format|
format.api
end
end
def show
@users_by_role = @project.users_by_role
@subprojects = @project.children.visible.all
@news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
@trackers = @project.rolled_up_trackers
cond = @project.project_condition(Setting.display_subprojects_issues?)
@open_issues_by_tracker = Issue.visible.count(:group => :tracker,
:include => [:project, :status, :tracker],
:conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
@total_issues_by_tracker = Issue.visible.count(:group => :tracker,
:include => [:project, :status, :tracker],
:conditions => cond)
if User.current.allowed_to?(:view_time_entries, @project)
@total_hours = TimeEntry.visible.sum(:hours, :include => :project, :conditions => cond).to_f
end
respond_to do |format|
format.api
end
end
def level_list
respond_to do |format|
format.api {
@elements = Project.project_level_list(Project.visible)
}
end
end
def update
@project.safe_attributes = params[:project]
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
respond_to do |format|
format.api { head :ok }
end
else
respond_to do |format|
format.api { render_validation_errors(@project) }
end
end
end
def destroy
@project_to_destroy = @project
@project_to_destroy.destroy
respond_to do |format|
format.api { head :ok }
end
end
def create
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@project = Project.new
@project.safe_attributes = params[:project]
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
add_current_user_to_project_if_not_admin(@project)
respond_to do |format|
format.api { render :action => 'show', :status => :created, :location => url_for(:controller => '/projects', :action => 'show', :id => @project.id) }
end
else
respond_to do |format|
format.api { render_validation_errors(@project) }
end
end
end
end
end
end

@ -0,0 +1,105 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V1
class TimelogController < TimelogController
include ::Api::V1::ApiController
def index
sort_init 'spent_on', 'desc'
sort_update 'spent_on' => 'spent_on',
'user' => 'user_id',
'activity' => 'activity_id',
'project' => "#{Project.table_name}.name",
'issue' => 'issue_id',
'hours' => 'hours'
cond = ARCondition.new
if @issue
cond << "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}"
elsif @project
cond << @project.project_condition(Setting.display_subprojects_issues?)
end
retrieve_date_range
cond << ['spent_on BETWEEN ? AND ?', @from, @to]
respond_to do |format|
format.api {
@entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
@entries = TimeEntry.visible.includes(:project, :activity, :user, {:issue => :tracker})
.where(cond.conditions)
.order(sort_clause)
.page(page_param)
.per_page(per_page_param)
}
end
end
def show
respond_to do |format|
format.api
end
end
def create
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
@time_entry.safe_attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
if @time_entry.save
respond_to do |format|
format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
end
else
respond_to do |format|
format.api { render_validation_errors(@time_entry) }
end
end
end
def update
@time_entry.safe_attributes = params[:time_entry]
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
if @time_entry.save
respond_to do |format|
format.api { head :ok }
end
else
respond_to do |format|
format.api { render_validation_errors(@time_entry) }
end
end
end
def destroy
if @time_entry.destroy && @time_entry.destroyed?
respond_to do |format|
format.api { head :ok }
end
else
respond_to do |format|
format.api { render_validation_errors(@time_entry) }
end
end
rescue ::ActionController::RedirectBackError
redirect_to :action => 'index', :project_id => @time_entry.project
end
end
end
end

@ -0,0 +1,157 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V1
class UsersController < UsersController
include ::Api::V1::ApiController
def index
sort_init 'login', 'asc'
sort_update %w(login firstname lastname mail admin created_on last_login_on)
scope = User
scope = scope.in_group(params[:group_id].to_i) if params[:group_id].present?
@status = params[:status] ? params[:status].to_i : 1
c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
unless params[:name].blank?
name = "%#{params[:name].strip.downcase}%"
c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
end
@users = scope.order(sort_clause)
.where(c.conditions)
.page(page_param)
.per_page(per_page_param)
respond_to do |format|
format.api
end
end
def show
# show projects based on current user visibility
@memberships = @user.memberships.all(:conditions => Project.visible_by(User.current))
events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
@events_by_day = events.group_by(&:event_date)
unless User.current.admin?
if !(@user.active? || @user.registered?) || (@user != User.current && @memberships.empty? && events.empty?)
render_404
return
end
end
respond_to do |format|
format.api
end
end
def create
@user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
@user.safe_attributes = params[:user]
@user.admin = params[:user][:admin] || false
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] if @user.change_password_allowed?
if @user.save
# TODO: Similar to My#account
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
@user.pref.save
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
UserMailer.account_information(@user, params[:user][:password]).deliver if params[:send_information]
respond_to do |format|
format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
end
else
@auth_sources = AuthSource.find(:all)
# Clear password input
@user.password = @user.password_confirmation = nil
respond_to do |format|
format.api { render_validation_errors(@user) }
end
end
end
def update
@user.admin = params[:user][:admin] if params[:user][:admin]
@user.login = params[:user][:login] if params[:user][:login]
@user.safe_attributes = params[:user].except(:login) # :login is protected
if params[:user][:password].present? && @user.change_password_allowed?
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
end
# Was the account actived ? (do it before User#save clears the change)
was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
if @user.save
# TODO: Similar to My#account
@user.pref.attributes = params[:pref]
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
@user.pref.save
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
if was_activated
UserMailer.account_activated(@user).deliver
elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.change_password_allowed?
UserMailer.account_information(@user, params[:user][:password]).deliver
end
respond_to do |format|
format.api { head :ok }
end
else
@auth_sources = AuthSource.find(:all)
@membership ||= Member.new
# Clear password input
@user.password = @user.password_confirmation = nil
respond_to do |format|
format.api { render_validation_errors(@user) }
end
end
rescue ::ActionController::RedirectBackError
redirect_to :controller => '/users', :action => 'edit', :id => @user
end
def destroy
# as destroying users is a lengthy process we handle it in the background
# and lock the account now so that no action can be performed with it
@user.status = User::STATUS_LOCKED
@user.save
# TODO: use Delayed::Worker.delay_jobs = false in test environment as soon as
# delayed job allows for it
Rails.env.test? ?
@user.destroy :
@user.delay.destroy
flash[:notice] = l('account.deleted')
respond_to do |format|
format.api do
head :ok
end
end
end
end
end
end

@ -0,0 +1,32 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
module ApiController
include ::Api::V1::ApiController
extend ::Api::V1::ApiController::ClassMethods
def api_version
/api\/v2\//
end
permeate_permissions :apply_at_timestamp,
:determine_base,
:find_all_projects_by_project_id,
:find_project_by_project_id,
:jump_to_project_menu_item
end
end
end

@ -0,0 +1,29 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class AuthenticationController < AuthenticationController
include ::Api::V2::ApiController
unloadable
def index
respond_to do |format|
format.api
end
end
end
end
end

@ -0,0 +1,29 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class PlanningElementJournalsController < PlanningElementJournalsController
include ::Api::V2::ApiController
def index
@journals = @planning_element.journals
respond_to do |format|
format.api
end
end
end
end
end

@ -0,0 +1,40 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class PlanningElementStatusesController < PlanningElementStatusesController
unloadable
helper :timelines
include ::Api::V2::ApiController
accept_key_auth :index, :show
def index
@planning_element_statuses = PlanningElementStatus.active
respond_to do |format|
format.api
end
end
def show
@planning_element_status = PlanningElementStatus.active.find(params[:id])
respond_to do |format|
format.api
end
end
end
end
end

@ -0,0 +1,35 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class PlanningElementTypeColorsController < PlanningElementTypeColorsController
include ::Api::V2::ApiController
def index
@colors = PlanningElementTypeColor.all
respond_to do |format|
format.api
end
end
def show
@color = PlanningElementTypeColor.find(params[:id])
respond_to do |format|
format.api
end
end
end
end
end

@ -0,0 +1,35 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class PlanningElementTypesController < PlanningElementTypesController
include ::Api::V2::ApiController
def index
@planning_element_types = @base.all
respond_to do |format|
format.api
end
end
def show
@planning_element_type = @base.find(params[:id])
respond_to do |format|
format.api
end
end
end
end
end

@ -0,0 +1,113 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class PlanningElementsController < PlanningElementsController
include ::Api::V2::ApiController
def index
optimize_planning_elements_for_less_db_queries
respond_to do |format|
format.api
end
end
def create
@planning_element = @planning_elements.new(permitted_params.planning_element)
successfully_created = @planning_element.save
respond_to do |format|
format.api do
if successfully_created
redirect_url = api_v2_project_planning_element_url(
@project, @planning_element,
# TODO this probably should be (params[:format] ||'xml'), however, client code currently anticipates xml responses.
:format => 'xml'
)
see_other(redirect_url)
else
render_validation_errors(@planning_element)
end
end
end
end
def show
@planning_element = @project.planning_elements.find(params[:id])
respond_to do |format|
format.api
end
end
def update
@planning_element = @planning_elements.find(params[:id])
@planning_element.attributes = permitted_params.planning_element
successfully_updated = @planning_element.save
respond_to do |format|
format.api do
if successfully_updated
no_content
else
render_validation_errors(@planning_element)
end
end
end
end
def list
options = {:order => 'id'}
projects = Project.visible.select do |project|
User.current.allowed_to?(:view_planning_elements, project)
end
if params[:ids]
ids = params[:ids].split(/,/).map(&:strip).select { |s| s =~ /^\d*$/ }.map(&:to_i).sort
project_ids = projects.map(&:id).sort
options[:conditions] = ["id IN (?) AND project_id IN (?)", ids, project_ids]
end
@planning_elements = PlanningElement.all(options)
respond_to do |format|
format.api { render :action => :index }
end
end
def destroy
@planning_element = @project.planning_elements.find(params[:id])
@planning_element.destroy!
respond_to do |format|
format.api
end
end
def move_to_trash
@planning_element = @planning_elements.find(params[:id])
@planning_element.destroy
respond_to do |format|
format.api
end
end
end
end
end

@ -0,0 +1,45 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class ProjectAssociationsController < ProjectAssociationsController
include ::Api::V2::ApiController
respond_to :api
def index
@project_associations = @project.project_associations.visible
respond_with(@project_associations)
end
def available_projects
available_projects = @project.associated_project_candidates
@elements = Project.project_level_list(Project.visible)
@disabled = Project.visible - available_projects
respond_with(@elements, @disabled)
end
def show
@project_association = @project.project_associations.find(params[:id])
check_visibility
respond_with(@project_associations)
end
end
end
end

@ -0,0 +1,35 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class ProjectTypesController < ProjectTypesController
include ::Api::V2::ApiController
def index
@project_types = ProjectType.all
respond_to do |format|
format.api
end
end
def show
@project_type = ProjectType.find(params[:id])
respond_to do |format|
format.api
end
end
end
end
end

@ -0,0 +1,51 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class ProjectsController < ProjectsController
include ::Api::V2::ApiController
def index
options = {:order => 'lft'}
if params[:ids]
ids, identifiers = params[:ids].split(/,/).map(&:strip).partition { |s| s =~ /^\d*$/ }
ids = ids.map(&:to_i).sort
identifiers = identifiers.sort
options[:conditions] = ["id IN (?) OR identifier IN (?)", ids, identifiers]
end
@projects = @base.visible.all(options)
respond_to do |format|
format.api
end
end
def show
@project = @base.find(params[:id])
authorize
return if performed?
respond_to do |format|
format.api
end
end
def find_project
@project = Project.find(params[:id])
end
end
end
end

@ -0,0 +1,35 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class ReportedProjectStatusesController < ReportedProjectStatusesController
include ::Api::V2::ApiController
def index
@reported_project_statuses = @base.all
respond_to do |format|
format.api
end
end
def show
@reported_project_status = @base.find(params[:id])
respond_to do |format|
format.api
end
end
end
end
end

@ -0,0 +1,168 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class ReportingsController < ReportingsController
include ::Api::V2::ApiController
def available_projects
available_projects = @project.reporting_to_project_candidates
respond_to do |format|
format.api {
@elements = Project.project_level_list(Project.visible)
@disabled = Project.visible - available_projects
}
end
end
def index
condition_params = [];
temp_condition = ""
condition = ""
if (params[:project_types].present?)
project_types = params[:project_types].split(/,/).map(&:to_i)
temp_condition += "#{Project.quoted_table_name}.project_type_id IN (?)"
condition_params << project_types
if (project_types.include?(-1))
temp_condition += " OR #{Project.quoted_table_name}.project_type_id IS NULL"
temp_condition = "(#{temp_condition})"
end
end
condition += temp_condition
temp_condition = ""
if (params[:project_statuses].present?)
condition += " AND " unless condition.empty?
project_statuses = params[:project_statuses].split(/,/).map(&:to_i)
temp_condition += "#{Reporting.quoted_table_name}.reported_project_status_id IN (?)"
condition_params << project_statuses
if (project_statuses.include?(-1))
temp_condition += " OR #{Reporting.quoted_table_name}.reported_project_status_id IS NULL"
temp_condition = "(#{temp_condition})"
end
end
condition += temp_condition
temp_condition = ""
if (params[:project_responsibles].present?)
condition += " AND " unless condition.empty?
project_responsibles = params[:project_responsibles].split(/,/).map(&:to_i)
temp_condition += "#{Project.quoted_table_name}.responsible_id IN (?)"
condition_params << project_responsibles
if (project_responsibles.include?(-1))
temp_condition += " OR #{Project.quoted_table_name}.responsible_id IS NULL"
temp_condition = "(#{temp_condition})"
end
end
condition += temp_condition
temp_condition = ""
if (params[:project_parents].present?)
condition += " AND " unless condition.empty?
project_parents = params[:project_parents].split(/,/).map(&:to_i)
nested_set_selection = Project.find(project_parents).map { |p| p.lft..p.rgt }.inject([]) { |r, e| e.each { |i| r << i }; r }
temp_condition += "#{Project.quoted_table_name}.lft IN (?)"
condition_params << nested_set_selection
end
condition += temp_condition
temp_condition = ""
if (params[:grouping_one].present? && condition.present?)
condition += " OR "
grouping = params[:grouping_one].split(/,/).map(&:to_i)
temp_condition += "#{Project.quoted_table_name}.id IN (?)"
condition_params << grouping
end
condition += temp_condition
conditions = [condition] + condition_params unless condition.empty?
case params[:only]
when "via_source"
@reportings = @project.reportings_via_source.find(:all,
:include => :project,
:conditions => conditions
)
when "via_target"
@reportings = @project.reportings_via_target.find(:all,
:include => :project,
:conditions => conditions
)
else
@reportings = @project.reportings.all
end
# get all reportings for which projects have ancestors.
nested_sets_for_parents = (@reportings.inject([]) { |r, e| r << e.reporting_to_project; r << e.project }).uniq.map { |p| [p.lft, p.rgt] }
condition_params = [];
temp_condition = ""
condition = ""
nested_sets_for_parents.each do |set|
condition += " OR " unless condition.empty?
condition += "#{Project.quoted_table_name}.lft < ? AND #{Project.quoted_table_name}.rgt > ?"
condition_params << set[0]
condition_params << set[1]
end
conditions = [condition] + condition_params unless condition.empty?
case params[:only]
when "via_source"
@ancestor_reportings = @project.reportings_via_source.find(:all,
:include => :project,
:conditions => conditions
)
when "via_target"
@ancestor_reportings = @project.reportings_via_target.find(:all,
:include => :project,
:conditions => conditions
)
else
@ancestor_reportings = @project.reportings.all
end
@reportings = (@reportings + @ancestor_reportings).uniq
respond_to do |format|
format.api do
@reportings.select(&:visible?)
end
end
end
def show
@reporting = @project.reportings_via_source.find(params[:id])
check_visibility
respond_to do |format|
format.api
end
end
end
end
end

@ -0,0 +1,35 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class ScenariosController < ScenariosController
include ::Api::V2::ApiController
def index
@scenarios = @project.scenarios
respond_to do |format|
format.api
end
end
def show
@scenario = @project.scenarios.find(params[:id])
respond_to do |format|
format.api
end
end
end
end
end

@ -0,0 +1,20 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module Api
module V2
class TimelinesController < TimelinesController
include ::Api::V2::ApiController
end
end
end

@ -173,7 +173,7 @@ class ApplicationController < ActionController::Base
end
respond_to do |format|
format.any(:html, :atom) { redirect_to signin_path(:back_url => url) }
format.any(:xml, :js, :json) { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="OpenProject API"' }
format.any(:xml, :js, :json) { head :unauthorized, "Reason" => "login needed" }
end
return false
end
@ -455,45 +455,6 @@ class ApplicationController < ActionController::Base
self.class.accept_key_auth_actions || []
end
# Returns the number of objects that should be displayed
# on the paginated list
def per_page_option
per_page = nil
if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
per_page = params[:per_page].to_s.to_i
session[:per_page] = per_page
elsif session[:per_page]
per_page = session[:per_page]
else
per_page = Setting.per_page_options_array.first || 25
end
per_page
end
# Returns offset and limit used to retrieve objects
# for an API response based on offset, limit and page parameters
def api_offset_and_limit(options=params)
if options[:offset].present?
offset = options[:offset].to_i
if offset < 0
offset = 0
end
end
limit = options[:limit].to_i
if limit < 1
limit = 25
elsif limit > 100
limit = 100
end
if offset.nil? && options[:page].present?
offset = (options[:page].to_i - 1) * limit
offset = 0 if offset < 0
end
offset ||= 0
[offset, limit]
end
# qvalues http header parser
# code taken from webrick
def parse_qvalues(value)
@ -614,6 +575,21 @@ class ApplicationController < ActionController::Base
end
helper_method :default_breadcrumb
def disable_everything_except_api
if !api_request?
head 410
return false
end
true
end
def disable_api
if api_request?
head 410
return false
end
true
end
ActiveSupport.run_load_hooks(:application_controller, self)
private

@ -11,12 +11,15 @@
#++
class AuthSourcesController < ApplicationController
include PaginationHelper
layout 'admin'
before_filter :require_admin
def index
@auth_source_pages, @auth_sources = paginate auth_source_class.name.tableize, :per_page => 10
@auth_sources = AuthSource.page(params[:page])
.per_page(per_page_param)
render "auth_sources/index"
end

@ -9,17 +9,18 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesAuthenticationController < ApplicationController
class AuthenticationController < ApplicationController
unloadable
helper :timelines
before_filter :disable_api
before_filter :require_login
accept_key_auth :index
def index
respond_to do |format|
format.html
format.api
end
end
end

@ -18,6 +18,7 @@ class BoardsController < ApplicationController
include MessagesHelper
include SortHelper
include WatchersHelper
include PaginationHelper
def index
@boards = @project.boards
@ -37,19 +38,19 @@ class BoardsController < ApplicationController
'replies' => "#{Message.table_name}.replies_count",
'updated_on' => "#{Message.table_name}.updated_on"
@topic_count = @board.topics.count
@topic_pages = Paginator.new self, @topic_count, per_page_option, params['page']
@topics = @board.topics.find :all, :order => ["#{Message.table_name}.sticky DESC", sort_clause].compact.join(', '),
:include => [:author, {:last_reply => :author}],
:limit => @topic_pages.items_per_page,
:offset => @topic_pages.current.offset
@topics = @board.topics.order(["#{Message.table_name}.sticky DESC", sort_clause].compact.join(', '))
.includes(:author, { :last_reply => :author })
.page(params[:page])
.per_page(per_page_param)
@message = Message.new
render :action => 'show', :layout => !request.xhr?
}
format.atom {
@messages = @board.messages.find :all, :order => 'created_on DESC',
:include => [:author, :board],
:limit => Setting.feeds_limit.to_i
@messages = @board.messages.order('created_on DESC')
.includes(:author, :board)
.limit(Setting.feeds_limit.to_i)
render_feed(@messages, :title => "#{@project}: #{@board}")
}
end

@ -11,6 +11,8 @@
#++
class IssueStatusesController < ApplicationController
include PaginationHelper
layout 'admin'
before_filter :require_admin
@ -19,7 +21,10 @@ class IssueStatusesController < ApplicationController
:redirect_to => { :action => :index }
def index
@issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position"
@issue_statuses = IssueStatus.order('position')
.page(params[:page])
.per_page(per_page_param)
render :action => "index", :layout => false if request.xhr?
end

@ -18,6 +18,7 @@ class IssuesController < ApplicationController
menu_item :view_all_issues, :only => [:all]
default_search_scope :issues
before_filter :disable_api
before_filter :find_issue, :only => [:show, :edit, :update, :quoted]
before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :move, :perform_move, :destroy]
before_filter :check_project_uniqueness, :only => [:move, :perform_move]
@ -44,37 +45,33 @@ class IssuesController < ApplicationController
include SortHelper
include IssuesHelper
include Redmine::Export::PDF
include PaginationHelper
def index
sort_init(@query.sort_criteria.empty? ? [DEFAULT_SORT_ORDER] : @query.sort_criteria)
sort_update(@query.sortable_columns)
if @query.valid?
case params[:format]
when 'csv', 'pdf'
@limit = Setting.issues_export_limit.to_i
when 'atom'
@limit = Setting.feeds_limit.to_i
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = per_page_option
end
per_page = case params[:format]
when 'csv', 'pdf'
Setting.issues_export_limit.to_i
when 'atom'
Setting.feeds_limit.to_i
else
per_page_param
end
@issue_count = @query.issue_count
@issue_pages = Paginator.new self, @issue_count, @limit, params['page']
@offset ||= @issue_pages.current.offset
@issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
:order => sort_clause,
:offset => @offset,
:limit => @limit)
:order => sort_clause)
.page(page_param)
.per_page(per_page)
@issue_count_by_group = @query.issue_count_by_group
respond_to do |format|
format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
format.html { render :template => 'issues/index', :layout => !request.xhr? }
format.api
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
end
else
@ -121,7 +118,6 @@ class IssuesController < ApplicationController
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
respond_to do |format|
format.html { render :template => 'issues/show' }
format.api
format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
end
@ -149,13 +145,11 @@ class IssuesController < ApplicationController
redirect_to(params[:continue] ? { :action => 'new', :project_id => @project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
{ :action => 'show', :id => @issue })
}
format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
end
return
else
respond_to do |format|
format.html { render :action => 'new' }
format.api { render_validation_errors(@issue) }
end
end
end
@ -207,7 +201,6 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
format.api { head :ok }
end
else
render_attachment_warning_if_needed(@issue)
@ -216,7 +209,6 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@issue) }
end
end
end
@ -280,7 +272,6 @@ class IssuesController < ApplicationController
end
respond_to do |format|
format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
format.api { head :ok }
end
end

@ -90,4 +90,8 @@ class JournalsController < ApplicationController
def valid_field?(field)
field.to_s.strip == "description"
end
def default_breadcrumb
I18n.t(:label_journal_diff)
end
end

@ -18,6 +18,7 @@ class MessagesController < ApplicationController
before_filter :authorize, :except => [:preview, :edit, :update, :destroy]
include AttachmentsHelper
include PaginationHelper
REPLIES_PER_PAGE = 25 unless const_defined?(:REPLIES_PER_PAGE)
@ -32,15 +33,13 @@ class MessagesController < ApplicationController
page = 1 + offset / REPLIES_PER_PAGE
end
@reply_count = @topic.children.count
@reply_pages = Paginator.new self, @reply_count, REPLIES_PER_PAGE, page
@replies = @topic.children.find(:all, :include => [:author, :attachments, {:board => :project}],
:order => "#{Message.table_name}.created_on ASC",
:limit => @reply_pages.items_per_page,
:offset => @reply_pages.current.offset)
@replies = @topic.children.includes(:author, :attachments, {:board => :project})
.order("#{Message.table_name}.created_on ASC")
.page(page)
.per_page(per_page_param)
@reply = Message.new(:subject => "RE: #{@message.subject}")
render :action => "show", :layout => false if request.xhr?
render :action => "show", :layout => !request.xhr?
end
# new topic

@ -11,8 +11,12 @@
#++
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_project_from_association, :except => [:new, :create, :index]
before_filter :find_project, :only => [:new, :create]
@ -22,28 +26,16 @@ class NewsController < ApplicationController
menu_item :new_news, :only => [:new, :create]
def index
case params[:format]
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = 10
end
scope = @project ? @project.news.visible : News.visible
@news_count = scope.count
@news_pages = Paginator.new self, @news_count, @limit, params['page']
@offset ||= @news_pages.current.offset
@newss = scope.all(:include => [:author, :project],
:order => "#{News.table_name}.created_on DESC",
:offset => @offset,
:limit => @limit)
@newss = scope.includes(:author, :project)
.order("#{News.table_name}.created_on DESC")
.page(params[:page])
.per_page(per_page_param)
respond_to do |format|
format.html { render :layout => false if request.xhr? }
format.api
format.html { render :layout => !request.xhr? }
format.atom { render_feed(@newss, :title => (@project ? @project.name : Setting.app_title) + ": #{l(:label_news_plural)}") }
end
end

@ -9,12 +9,13 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesPlanningElementJournalsController < ApplicationController
class PlanningElementJournalsController < ApplicationController
unloadable
helper :timelines
include Timelines::ExtendedHTTP
include ExtendedHTTP
before_filter :disable_api
before_filter :find_project_by_project_id
before_filter :find_planning_element_by_planning_element_id
before_filter :authorize
@ -25,7 +26,6 @@ class Timelines::TimelinesPlanningElementJournalsController < ApplicationControl
@journals = @planning_element.journals
respond_to do |format|
format.html { render_404 }
format.api
end
end
@ -37,6 +37,6 @@ class Timelines::TimelinesPlanningElementJournalsController < ApplicationControl
def find_planning_element_by_planning_element_id
raise ActiveRecord::RecordNotFound if @project.blank?
@planning_element = @project.timelines_planning_elements.find(params[:planning_element_id])
@planning_element = @project.planning_elements.find(params[:planning_element_id])
end
end

@ -9,25 +9,25 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesPlanningElementStatusesController < ApplicationController
class PlanningElementStatusesController < ApplicationController
unloadable
helper :timelines
before_filter :disable_api
accept_key_auth :index, :show
def index
@planning_element_statuses = Timelines::PlanningElementStatus.active
@planning_element_statuses = PlanningElementStatus.active
respond_to do |format|
format.html { render_404 }
format.api
end
end
def show
@planning_element_status = Timelines::PlanningElementStatus.active.find(params[:id])
@planning_element_status = PlanningElementStatus.active.find(params[:id])
respond_to do |format|
format.html { render_404 }
format.api
end
end
end

@ -9,44 +9,44 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesColorsController < ApplicationController
class PlanningElementTypeColorsController < ApplicationController
unloadable
helper :timelines
before_filter :disable_api
before_filter :require_admin_unless_readonly_api_request
accept_key_auth :index, :show
helper :timelines
layout 'admin'
def index
@colors = Timelines::Color.all
@colors = PlanningElementTypeColor.all
respond_to do |format|
format.html
format.api
end
end
def show
@color = Timelines::Color.find(params[:id])
@color = PlanningElementTypeColor.find(params[:id])
respond_to do |format|
format.api
end
end
def new
@color = Timelines::Color.new
@color = PlanningElementTypeColor.new
respond_to do |format|
format.html
end
end
def create
@color = Timelines::Color.new(permitted_params.color)
@color = PlanningElementTypeColor.new(permitted_params.color)
if @color.save
flash[:notice] = l(:notice_successful_create)
redirect_to timelines_colors_path
redirect_to colors_path
else
flash.now[:error] = l('timelines.color_could_not_be_saved')
render :action => "new"
@ -54,18 +54,18 @@ class Timelines::TimelinesColorsController < ApplicationController
end
def edit
@color = Timelines::Color.find(params[:id])
@color = PlanningElementTypeColor.find(params[:id])
respond_to do |format|
format.html
end
end
def update
@color = Timelines::Color.find(params[:id])
@color = PlanningElementTypeColor.find(params[:id])
if @color.update_attributes(permitted_params.color)
flash[:notice] = l(:notice_successful_update)
redirect_to timelines_colors_path
redirect_to colors_path
else
flash.now[:error] = l('timelines.color_could_not_be_saved')
render :action => 'edit'
@ -73,29 +73,29 @@ class Timelines::TimelinesColorsController < ApplicationController
end
def move
@color = Timelines::Color.find(params[:id])
@color = PlanningElementTypeColor.find(params[:id])
if @color.update_attributes(permitted_params.color_move)
flash[:notice] = l(:notice_successful_update)
else
render :action => 'edit'
end
redirect_to timelines_colors_path
redirect_to colors_path
end
def confirm_destroy
@color = Timelines::Color.find(params[:id])
@color = PlanningElementTypeColor.find(params[:id])
respond_to do |format|
format.html
end
end
def destroy
@color = Timelines::Color.find(params[:id])
@color = PlanningElementTypeColor.find(params[:id])
@color.destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to timelines_colors_path
redirect_to colors_path
end
protected

@ -9,10 +9,11 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesPlanningElementTypesController < ApplicationController
class PlanningElementTypesController < ApplicationController
unloadable
helper :timelines
before_filter :disable_api
before_filter :determine_base
before_filter :check_permissions
before_filter :ensure_global_scope, :except => [:index, :show]
@ -22,14 +23,13 @@ class Timelines::TimelinesPlanningElementTypesController < ApplicationController
helper :timelines
layout 'admin'
extend Timelines::Pagination::Controller
timelines_paginate_model Timelines::PlanningElementType
extend Pagination::Controller
paginate_model PlanningElementType
def index
@planning_element_types = @base.all
respond_to do |format|
format.html { render_404 if @project }
format.api
end
end
@ -37,22 +37,21 @@ class Timelines::TimelinesPlanningElementTypesController < ApplicationController
@planning_element_type = @base.find(params[:id])
respond_to do |format|
format.html { render_404 }
format.api
end
end
def new
@planning_element_type = Timelines::PlanningElementType.new
@planning_element_type = PlanningElementType.new
respond_to do |format|
format.html
end
end
def create
@planning_element_type = Timelines::PlanningElementType.new(permitted_params.planning_element_type)
@planning_element_type = PlanningElementType.new(permitted_params.planning_element_type)
if @planning_element_type.save
flash[:notice] = l(:notice_successful_create)
redirect_to timelines_planning_element_types_path
redirect_to planning_element_types_path
else
flash.now[:error] = l('timelines.planning_element_type_could_not_be_saved')
render :action => "new"
@ -71,7 +70,7 @@ class Timelines::TimelinesPlanningElementTypesController < ApplicationController
if @planning_element_type.update_attributes(permitted_params.planning_element_type)
flash[:notice] = l(:notice_successful_update)
redirect_to timelines_planning_element_types_path
redirect_to planning_element_types_path
else
flash.now[:error] = l('timelines.planning_element_type_could_not_be_saved')
render :action => 'edit'
@ -86,7 +85,7 @@ class Timelines::TimelinesPlanningElementTypesController < ApplicationController
else
render :action => 'edit'
end
redirect_to timelines_planning_element_types_path
redirect_to planning_element_types_path
end
def confirm_destroy
@ -101,7 +100,7 @@ class Timelines::TimelinesPlanningElementTypesController < ApplicationController
@planning_element_type.destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to timelines_planning_element_types_path
redirect_to planning_element_types_path
end
protected
@ -109,14 +108,14 @@ class Timelines::TimelinesPlanningElementTypesController < ApplicationController
def determine_base
if params[:project_id]
@project = Project.find(params[:project_id])
@base = @project.timelines_planning_element_types
@base = @project.planning_element_types
else
@base = Timelines::PlanningElementType
@base = PlanningElementType
end
end
def check_permissions
if @base == Timelines::PlanningElementType
if @base == PlanningElementType
render_403 unless readonly_api_request or User.current.allowed_to_globally?(:edit_timelines)
else
authorize

@ -9,17 +9,18 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesPlanningElementsController < ApplicationController
class PlanningElementsController < ApplicationController
unloadable
helper :timelines, :timelines_planning_elements
helper :timelines, :planning_elements
include Timelines::ExtendedHTTP
include ExtendedHTTP
menu_item :timelines_planning_elements
menu_item :timelines_recycle_bin, :only => [:move_to_trash, :confirm_move_to_trash,
menu_item :planning_elements
menu_item :recycle_bin, :only => [:move_to_trash, :confirm_move_to_trash,
:recycle_bin, :destroy_all, :confirm_destroy_all,
:restore_all, :confirm_restore_all]
before_filter :disable_api
before_filter :find_project_by_project_id,
:authorize,
:assign_planning_elements, :except => [:index, :list]
@ -41,7 +42,6 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
respond_to do |format|
format.html
format.api
end
end
@ -52,7 +52,7 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
end
def recycle_bin
@planning_elements = @project.timelines_planning_elements.deleted
@planning_elements = @project.planning_elements.deleted
respond_to do |format|
format.html
end
@ -76,29 +76,20 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
format.html do
if successfully_created
flash[:notice] = l(:notice_successful_create)
redirect_to timelines_project_planning_element_path(@project, @planning_element)
redirect_to project_planning_element_path(@project, @planning_element)
else
flash.now[:error] = l('timelines.planning_element_could_not_be_saved')
render :action => "new"
end
end
format.api do
if successfully_created
see_other(timelines_project_planning_element_url(@project, @planning_element, :format => 'xml'))
else
render_validation_errors(@planning_element)
end
end
end
end
def show
@planning_element = @project.timelines_planning_elements.find(params[:id])
@planning_element = @project.planning_elements.find(params[:id])
respond_to do |format|
format.html
format.api
format.js { render :partial => 'show'}
end
end
@ -122,20 +113,12 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
format.html do
if successfully_updated
flash[:notice] = l(:notice_successful_update)
redirect_to timelines_project_planning_element_path(@project, @planning_element)
redirect_to project_planning_element_path(@project, @planning_element)
else
flash.now[:error] = l('timelines.planning_element_could_not_be_saved')
render :action => "edit"
end
end
format.api do
if successfully_updated
no_content
else
render_validation_errors(@planning_element)
end
end
end
end
@ -152,11 +135,10 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
options[:conditions] = ["id IN (?) AND project_id IN (?)", ids, project_ids]
end
@planning_elements = Timelines::PlanningElement.all(options)
@planning_elements = PlanningElement.all(options)
respond_to do |format|
format.html { render :action => :index }
format.api { render :action => :index }
end
end
@ -169,7 +151,7 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
end
def confirm_destroy
@planning_element = @project.timelines_planning_elements.find(params[:id])
@planning_element = @project.planning_elements.find(params[:id])
respond_to do |format|
format.html
@ -177,20 +159,19 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
end
def destroy
@planning_element = @project.timelines_planning_elements.find(params[:id])
@planning_element = @project.planning_elements.find(params[:id])
@planning_element.destroy!
respond_to do |format|
format.html do
flash[:notice] = l(:notice_successful_delete)
redirect_to timelines_project_planning_elements_path(@project)
redirect_to project_planning_elements_path(@project)
end
format.api
end
end
def confirm_destroy_all
@planning_elements = @project.timelines_planning_elements.deleted
@planning_elements = @project.planning_elements.deleted
respond_to do |format|
format.html
@ -198,12 +179,12 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
end
def destroy_all
@project.timelines_planning_elements.deleted.each do |element|
@project.planning_elements.deleted.each do |element|
element.destroy!
end
flash[:notice] = l("timelines.notice_successful_deleted_all_elements")
redirect_to(recycle_bin_timelines_project_planning_elements_path(@project))
redirect_to(recycle_bin_project_planning_elements_path(@project))
end
def move_to_trash
@ -213,14 +194,13 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
respond_to do |format|
format.html do
flash[:notice] = l("timelines.notice_successful_moved_to_trash")
redirect_to timelines_project_planning_elements_path(@project)
redirect_to project_planning_elements_path(@project)
end
format.api
end
end
def restore
@planning_element = @project.timelines_planning_elements.find(params[:id])
@planning_element = @project.planning_elements.find(params[:id])
successfully_restored = @planning_element.restore!
respond_to do |format|
@ -231,13 +211,13 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
flash.now[:error] = l('timelines.planning_element_could_not_be_restored')
end
redirect_to(recycle_bin_timelines_project_planning_elements_path(@project))
redirect_to(recycle_bin_project_planning_elements_path(@project))
end
end
end
def confirm_restore_all
@planning_elements = @project.timelines_planning_elements.deleted
@planning_elements = @project.planning_elements.deleted
respond_to do |format|
format.html
@ -245,12 +225,12 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
end
def restore_all
@project.timelines_planning_elements.deleted.each do |element|
@project.planning_elements.deleted.each do |element|
element.restore!
end
flash[:notice] = l("timelines.notice_successful_restored_all_elements")
redirect_to(recycle_bin_timelines_project_planning_elements_path(@project))
redirect_to(recycle_bin_project_planning_elements_path(@project))
end
protected
@ -294,12 +274,12 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
# assign_planning_elements and apply_at_timestamp
if params[:at].blank?
@planning_elements = Timelines::PlanningElement.for_projects(@projects).without_deleted
@planning_elements = PlanningElement.for_projects(@projects).without_deleted
else
begin
time = Time.at(Integer(params[:at]))
# intentionally avoiding without_deleted scope
@planning_elements = Timelines::PlanningElement.for_projects(@projects).at_time(time)
@planning_elements = PlanningElement.for_projects(@projects).at_time(time)
rescue ArgumentError
render_errors(:at => 'unknown format')
end
@ -308,7 +288,7 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
end
def assign_planning_elements
@planning_elements = @project.timelines_planning_elements.without_deleted
@planning_elements = @project.planning_elements.without_deleted
end
def apply_at_timestamp
@ -316,7 +296,7 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
time = Time.at(Integer(params[:at]))
# intentionally rebuilding scope chain to avoid without_deleted scope
@planning_elements = @project.timelines_planning_elements.at_time(time)
@planning_elements = @project.planning_elements.at_time(time)
rescue ArgumentError
render_errors(:at => 'unknown format')
@ -374,7 +354,7 @@ class Timelines::TimelinesPlanningElementsController < ApplicationController
ids_hash = @planning_elements.inject({}) { |h, pe| h[pe.id] = pe; h }
children_hash = Hash.new { |h,k| h[k] = [] }
parent_refl, children_refl = [:parent, :children].map{|assoc| Timelines::PlanningElement.reflect_on_association(assoc)}
parent_refl, children_refl = [:parent, :children].map{|assoc| PlanningElement.reflect_on_association(assoc)}
associations = {
:belongs_to => ActiveRecord::Associations::BelongsToAssociation,

@ -0,0 +1,17 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class PrincipalsController < ApplicationController
extend Pagination::Controller
paginate_model Principal
search_for Principal, :like
end

@ -9,17 +9,18 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesProjectAssociationsController < ApplicationController
class ProjectAssociationsController < ApplicationController
unloadable
helper :timelines
before_filter :disable_api
before_filter :find_project_by_project_id
before_filter :authorize
before_filter :check_allows_association
accept_key_auth :index, :show
menu_item :timelines_project_associations
menu_item :project_associations
def index
respond_to do |format|
@ -27,35 +28,20 @@ class Timelines::TimelinesProjectAssociationsController < ApplicationController
# TODO:
# Project types should be ordered by position
# Projects and associations should be ordered by project tree
@project_types = [nil] + Timelines::ProjectType.all
@project_associations_by_type = @project.timelines_project_associations_by_type
@project_types = [nil] + ProjectType.all
@project_associations_by_type = @project.project_associations_by_type
end
format.api do
@project_associations = @project.timelines_project_associations.visible
end
end
end
def available_projects
available_projects = @project.timelines_associated_project_candidates
respond_to do |format|
format.html { render_404 }
format.api {
@elements = Project.project_level_list(Project.visible)
@disabled = Project.visible - available_projects
}
end
end
def new
@project_association = Timelines::ProjectAssociation.new(params[:project_association])
@project_association = ProjectAssociation.new(params[:project_association])
@project_association.project_a = @project
@associated_project_candidates_by_type = @project.timelines_associated_project_candidates_by_type
@associated_project_candidates_by_type = @project.associated_project_candidates_by_type
end
def create
@project_association = Timelines::ProjectAssociation.new(params[:project_association])
@project_association = ProjectAssociation.new(params[:project_association])
@project_association.project_a = @project
@project_association.project_b_id = params[:project_association_select][:project_b_id]
@ -63,23 +49,24 @@ class Timelines::TimelinesProjectAssociationsController < ApplicationController
if @project_association.save
flash[:notice] = l(:notice_successful_create)
redirect_to timelines_project_project_associations_path(@project)
redirect_to project_project_associations_path(@project)
else
render :action => 'new'
end
end
# TODO: this method should be removed once the controller no longer responds to
# api calls
def show
@project_association = @project.timelines_project_associations.find(params[:id])
@project_association = @project.project_associations.find(params[:id])
check_visibility
respond_to do |format|
format.api
end
end
def edit
@project_association = @project.timelines_project_associations.find(params[:id])
@project_association = @project.project_associations.find(params[:id])
check_visibility
respond_to do |format|
@ -88,7 +75,7 @@ class Timelines::TimelinesProjectAssociationsController < ApplicationController
end
def update
@project_association = @project.timelines_project_associations.find(params[:id])
@project_association = @project.project_associations.find(params[:id])
check_visibility
@project_association.description = params[:project_association][:description]
@ -98,14 +85,14 @@ class Timelines::TimelinesProjectAssociationsController < ApplicationController
if @project_association.projects.include?(@project) and @project_association.save
flash[:notice] = l(:notice_successful_update)
redirect_to timelines_project_project_associations_path(@project)
redirect_to project_project_associations_path(@project)
else
render :action => 'edit'
end
end
def confirm_destroy
@project_association = @project.timelines_project_associations.find(params[:id])
@project_association = @project.project_associations.find(params[:id])
check_visibility
respond_to do |format|
@ -114,19 +101,19 @@ class Timelines::TimelinesProjectAssociationsController < ApplicationController
end
def destroy
@project_association = @project.timelines_project_associations.find(params[:id])
@project_association = @project.project_associations.find(params[:id])
check_visibility
@project_association.destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to timelines_project_project_associations_path(@project)
redirect_to project_project_associations_path(@project)
end
protected
def check_allows_association
render_404 unless @project.timelines_allows_association?
render_404 unless @project.allows_association?
end
def check_visibility

@ -9,13 +9,14 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesProjectTypesController < ApplicationController
extend Timelines::Pagination::Controller
timelines_paginate_model Timelines::ProjectType
class ProjectTypesController < ApplicationController
extend Pagination::Controller
paginate_model ProjectType
unloadable
helper :timelines
before_filter :disable_api
before_filter :check_permissions
accept_key_auth :index, :show
@ -23,26 +24,25 @@ class Timelines::TimelinesProjectTypesController < ApplicationController
layout 'admin'
def index
@project_types = Timelines::ProjectType.all
@project_types = ProjectType.all
respond_to do |format|
format.html
format.api
end
end
def new
@project_type = Timelines::ProjectType.new
@project_type = ProjectType.new
respond_to do |format|
format.html
end
end
def create
@project_type = Timelines::ProjectType.new(permitted_params.project_type)
@project_type = ProjectType.new(permitted_params.project_type)
if @project_type.save
flash[:notice] = l(:notice_successful_create)
redirect_to timelines_project_types_path
redirect_to project_types_path
else
flash.now[:error] = l('timelines.project_type_could_not_be_saved')
render :action => 'new'
@ -50,25 +50,24 @@ class Timelines::TimelinesProjectTypesController < ApplicationController
end
def show
@project_type = Timelines::ProjectType.find(params[:id])
@project_type = ProjectType.find(params[:id])
respond_to do |format|
format.api
end
end
def edit
@project_type = Timelines::ProjectType.find(params[:id])
@project_type = ProjectType.find(params[:id])
respond_to do |format|
format.html
end
end
def update
@project_type = Timelines::ProjectType.find(params[:id])
@project_type = ProjectType.find(params[:id])
if @project_type.update_attributes(permitted_params.project_type)
flash[:notice] = l(:notice_successful_update)
redirect_to timelines_project_types_path
redirect_to project_types_path
else
flash.now[:error] = l('timelines.project_type_could_not_be_saved')
render :action => :edit
@ -76,20 +75,20 @@ class Timelines::TimelinesProjectTypesController < ApplicationController
end
def confirm_destroy
@project_type = Timelines::ProjectType.find(params[:id])
@project_type = ProjectType.find(params[:id])
respond_to do |format|
format.html
end
end
def destroy
@project_type = Timelines::ProjectType.find(params[:id])
@project_type = ProjectType.find(params[:id])
flash[:notice] = l(:notice_successful_delete) if @project_type.destroy
redirect_to timelines_project_types_path
redirect_to project_types_path
end
def move
@project_type = Timelines::ProjectType.find(params[:id])
@project_type = ProjectType.find(params[:id])
if @project_type.update_attributes(permitted_params.project_type_move)
flash[:notice] = l(:notice_successful_update)
@ -97,7 +96,7 @@ class Timelines::TimelinesProjectTypesController < ApplicationController
flash.now[:error] = l('timelines.project_type_could_not_be_saved')
render :action => 'edit'
end
redirect_to timelines_project_types_path
redirect_to project_types_path
end
protected

@ -11,16 +11,25 @@
#++
class ProjectsController < ApplicationController
extend Pagination::Controller
paginate_model Project
menu_item :overview
menu_item :roadmap, :only => :roadmap
menu_item :settings, :only => :settings
helper :timelines
before_filter :disable_api
before_filter :find_project, :except => [ :index, :level_list, :new, :create, :copy ]
before_filter :authorize, :only => [ :show, :settings, :edit, :update, :modules ]
before_filter :authorize_global, :only => [:new, :create]
before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
before_filter :jump_to_project_menu_item, :only => :show
before_filter :load_project_settings, :only => :settings
before_filter :determine_base
accept_key_auth :index, :level_list, :show, :create, :update, :destroy
after_filter :only => [:create, :edit, :update, :archive, :unarchive, :destroy] do |controller|
@ -36,8 +45,8 @@ class ProjectsController < ApplicationController
include ProjectsHelper
# for timelines
def timelines_planning_element_types
params[:project].assert_valid_keys("timelines_planning_element_type_ids")
def planning_element_types
params[:project].assert_valid_keys("planning_element_type_ids")
if @project.update_attributes(params[:project])
flash[:notice] = l('notice_successful_update')
else
@ -52,11 +61,6 @@ class ProjectsController < ApplicationController
format.html {
@projects = Project.visible.find(:all, :order => 'lft')
}
format.api {
@offset, @limit = api_offset_and_limit
@project_count = Project.visible.count
@projects = Project.visible.all(:offset => @offset, :limit => @limit, :order => 'lft')
}
format.atom {
projects = Project.visible.find(:all, :order => 'created_on DESC',
:limit => Setting.feeds_limit.to_i)
@ -65,15 +69,6 @@ class ProjectsController < ApplicationController
end
end
def level_list
respond_to do |format|
format.html { render_404 }
format.api {
@elements = Project.project_level_list(Project.visible)
}
end
end
def new
@issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
@trackers = Tracker.all
@ -95,12 +90,10 @@ class ProjectsController < ApplicationController
flash[:notice] = l(:notice_successful_create)
redirect_to :controller => '/projects', :action => 'settings', :id => @project
}
format.api { render :action => 'show', :status => :created, :location => url_for(:controller => '/projects', :action => 'show', :id => @project.id) }
end
else
respond_to do |format|
format.html { render :action => 'new' }
format.api { render_validation_errors(@project) }
end
end
@ -164,7 +157,6 @@ class ProjectsController < ApplicationController
respond_to do |format|
format.html
format.api
end
end
@ -183,7 +175,6 @@ class ProjectsController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_to :action => 'settings', :id => @project
}
format.api { head :ok }
end
else
respond_to do |format|
@ -191,7 +182,6 @@ class ProjectsController < ApplicationController
load_project_settings
render :action => 'settings'
}
format.api { render_validation_errors(@project) }
end
end
end
@ -216,11 +206,10 @@ class ProjectsController < ApplicationController
def destroy
@project_to_destroy = @project
if api_request? || params[:confirm]
if params[:confirm]
@project_to_destroy.destroy
respond_to do |format|
format.html { redirect_to :controller => '/admin', :action => 'projects' }
format.api { head :ok }
end
end
@ -289,4 +278,14 @@ private
end
end
protected
def determine_base
if params[:project_type_id]
@base = ProjectType.find(params[:project_type_id]).projects
else
@base = Project
end
end
end

@ -9,13 +9,14 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesReportedProjectStatusesController < ApplicationController
class ReportedProjectStatusesController < ApplicationController
unloadable
helper :timelines
extend Timelines::Pagination::Controller
timelines_paginate_model Timelines::ReportedProjectStatus
extend Pagination::Controller
paginate_model ReportedProjectStatus
before_filter :disable_api
before_filter :require_login
before_filter :determine_base
accept_key_auth :index, :show
@ -24,7 +25,6 @@ class Timelines::TimelinesReportedProjectStatusesController < ApplicationControl
@reported_project_statuses = @base.all
respond_to do |format|
format.html { render_404 }
format.api
end
end
@ -32,7 +32,6 @@ class Timelines::TimelinesReportedProjectStatusesController < ApplicationControl
@reported_project_status = @base.find(params[:id])
respond_to do |format|
format.html { render_404 }
format.api
end
end
@ -40,9 +39,9 @@ class Timelines::TimelinesReportedProjectStatusesController < ApplicationControl
def determine_base
if params[:project_type_id]
@base = Timelines::ProjectType.find(params[:project_type_id]).reported_project_statuses.active
@base = ProjectType.find(params[:project_type_id]).reported_project_statuses.active
else
@base = Timelines::ReportedProjectStatus.active
@base = ReportedProjectStatus.active
end
end
end

@ -9,25 +9,22 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesReportingsController < ApplicationController
class ReportingsController < ApplicationController
unloadable
helper :timelines
before_filter :disable_api
before_filter :find_project_by_project_id
before_filter :authorize
accept_key_auth :index, :show
menu_item :timelines_reportings
menu_item :reportings
def available_projects
available_projects = @project.timelines_reporting_to_project_candidates
available_projects = @project.reporting_to_project_candidates
respond_to do |format|
format.html { render_404 }
format.api {
@elements = Project.project_level_list(Project.visible)
@disabled = Project.visible - available_projects
}
end
end
@ -39,10 +36,10 @@ class Timelines::TimelinesReportingsController < ApplicationController
if (params[:project_types].present?)
project_types = params[:project_types].split(/,/).map(&:to_i)
temp_condition += "#{Project.quoted_table_name}.timelines_project_type_id IN (?)"
temp_condition += "#{Project.quoted_table_name}.project_type_id IN (?)"
condition_params << project_types
if (project_types.include?(-1))
temp_condition += " OR #{Project.quoted_table_name}.timelines_project_type_id IS NULL"
temp_condition += " OR #{Project.quoted_table_name}.project_type_id IS NULL"
temp_condition = "(#{temp_condition})"
end
end
@ -54,10 +51,10 @@ class Timelines::TimelinesReportingsController < ApplicationController
condition += " AND " unless condition.empty?
project_statuses = params[:project_statuses].split(/,/).map(&:to_i)
temp_condition += "#{Timelines::Reporting.quoted_table_name}.reported_project_status_id IN (?)"
temp_condition += "#{Reporting.quoted_table_name}.reported_project_status_id IN (?)"
condition_params << project_statuses
if (project_statuses.include?(-1))
temp_condition += " OR #{Timelines::Reporting.quoted_table_name}.reported_project_status_id IS NULL"
temp_condition += " OR #{Reporting.quoted_table_name}.reported_project_status_id IS NULL"
temp_condition = "(#{temp_condition})"
end
end
@ -69,10 +66,10 @@ class Timelines::TimelinesReportingsController < ApplicationController
condition += " AND " unless condition.empty?
project_responsibles = params[:project_responsibles].split(/,/).map(&:to_i)
temp_condition += "#{Project.quoted_table_name}.timelines_responsible_id IN (?)"
temp_condition += "#{Project.quoted_table_name}.responsible_id IN (?)"
condition_params << project_responsibles
if (project_responsibles.include?(-1))
temp_condition += " OR #{Project.quoted_table_name}.timelines_responsible_id IS NULL"
temp_condition += " OR #{Project.quoted_table_name}.responsible_id IS NULL"
temp_condition = "(#{temp_condition})"
end
end
@ -106,17 +103,17 @@ class Timelines::TimelinesReportingsController < ApplicationController
case params[:only]
when "via_source"
@reportings = @project.timelines_reportings_via_source.find(:all,
@reportings = @project.reportings_via_source.find(:all,
:include => :project,
:conditions => conditions
)
when "via_target"
@reportings = @project.timelines_reportings_via_target.find(:all,
@reportings = @project.reportings_via_target.find(:all,
:include => :project,
:conditions => conditions
)
else
@reportings = @project.timelines_reportings.all
@reportings = @project.reportings.all
end
# get all reportings for which projects have ancestors.
@ -137,48 +134,44 @@ class Timelines::TimelinesReportingsController < ApplicationController
case params[:only]
when "via_source"
@ancestor_reportings = @project.timelines_reportings_via_source.find(:all,
@ancestor_reportings = @project.reportings_via_source.find(:all,
:include => :project,
:conditions => conditions
)
when "via_target"
@ancestor_reportings = @project.timelines_reportings_via_target.find(:all,
@ancestor_reportings = @project.reportings_via_target.find(:all,
:include => :project,
:conditions => conditions
)
else
@ancestor_reportings = @project.timelines_reportings.all
@ancestor_reportings = @project.reportings.all
end
@reportings = (@reportings + @ancestor_reportings).uniq
respond_to do |format|
format.html do
@reportings = @project.timelines_reportings_via_source.all.select(&:visible?)
end
format.api do
@reportings.select(&:visible?)
@reportings = @project.reportings_via_source.all.select(&:visible?)
end
end
end
def show
@reporting = @project.timelines_reportings_via_source.find(params[:id])
@reporting = @project.reportings_via_source.find(params[:id])
check_visibility
respond_to do |format|
format.html
format.api
end
end
def new
@reporting_to_project_candidates = @project.timelines_reporting_to_project_candidates
@reporting_to_project_candidates = @project.reporting_to_project_candidates
if @reporting_to_project_candidates.blank?
flash[:warning] = l('timelines.no_projects_for_reporting_available')
redirect_to timelines_project_reportings_path(@project)
redirect_to project_reportings_path(@project)
else
@reporting = Timelines::Reporting.new
@reporting = Reporting.new
respond_to do |format|
format.html
@ -187,13 +180,13 @@ class Timelines::TimelinesReportingsController < ApplicationController
end
def create
@reporting = @project.timelines_reportings_via_source.build
@reporting = @project.reportings_via_source.build
@reporting.reporting_to_project_id = params['reporting']['reporting_to_project_id']
check_visibility
if @reporting.save
flash[:notice] = l(:notice_successful_create)
redirect_to timelines_project_reportings_path
redirect_to project_reportings_path
else
flash.now[:error] = l('timelines.reporting_could_not_be_saved')
render :action => 'new'
@ -201,7 +194,7 @@ class Timelines::TimelinesReportingsController < ApplicationController
end
def edit
@reporting = @project.timelines_reportings_via_source.find(params[:id])
@reporting = @project.reportings_via_source.find(params[:id])
check_visibility
respond_to do |format|
@ -210,12 +203,12 @@ class Timelines::TimelinesReportingsController < ApplicationController
end
def update
@reporting = @project.timelines_reportings_via_source.find(params[:id])
@reporting = @project.reportings_via_source.find(params[:id])
check_visibility
if @reporting.update_attributes(params[:reporting])
flash[:notice] = l(:notice_successful_update)
redirect_to timelines_project_reportings_path
redirect_to project_reportings_path
else
flash.now[:error] = l('timelines.reporting_could_not_be_saved')
render :action => :edit
@ -223,7 +216,7 @@ class Timelines::TimelinesReportingsController < ApplicationController
end
def confirm_destroy
@reporting = @project.timelines_reportings_via_source.find(params[:id])
@reporting = @project.reportings_via_source.find(params[:id])
check_visibility
respond_to do |format|
@ -232,12 +225,12 @@ class Timelines::TimelinesReportingsController < ApplicationController
end
def destroy
@reporting = @project.timelines_reportings_via_source.find(params[:id])
@reporting = @project.reportings_via_source.find(params[:id])
check_visibility
@reporting.destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to timelines_project_reportings_path
redirect_to project_reportings_path
end
protected

@ -18,6 +18,8 @@ class ChangesetNotFound < Exception; end
class InvalidRevisionParam < Exception; end
class RepositoriesController < ApplicationController
include PaginationHelper
menu_item :repository
menu_item :settings, :only => :edit
default_search_scope :changesets
@ -94,14 +96,9 @@ class RepositoriesController < ApplicationController
end
def revisions
@changeset_count = @repository.changesets.size
@changeset_pages = Paginator.new self, @changeset_count,
per_page_option,
params['page']
@changesets = @repository.changesets.find(:all,
:limit => @changeset_pages.items_per_page,
:offset => @changeset_pages.current.offset,
:include => [:user, :repository])
@changesets = @repository.changesets.includes(:user, :repository)
.page(params[:page])
.per_page(per_page_param)
respond_to do |format|
format.html { render :layout => false if request.xhr? }

@ -11,12 +11,17 @@
#++
class RolesController < ApplicationController
include PaginationHelper
layout 'admin'
before_filter :require_admin
def index
@role_pages, @roles = paginate :roles, :per_page => 25, :order => 'builtin, position'
@roles = Role.order('builtin, position')
.page(params[:page])
.per_page(per_page_param)
render :action => "index", :layout => false if request.xhr?
end

@ -9,30 +9,27 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesScenariosController < ApplicationController
class ScenariosController < ApplicationController
unloadable
helper :timelines
before_filter :disable_api
before_filter :find_project_by_project_id
before_filter :authorize
accept_key_auth :index, :show
# API actions
def index
@scenarios = @project.timelines_scenarios
@scenarios = @project.scenarios
respond_to do |format|
format.html { render_404 }
format.api
end
end
def show
@scenario = @project.timelines_scenarios.find(params[:id])
@scenario = @project.scenarios.find(params[:id])
respond_to do |format|
format.html { render_404 }
format.api
end
end
@ -40,11 +37,11 @@ class Timelines::TimelinesScenariosController < ApplicationController
# Admin actions
def new
@scenario = @project.timelines_scenarios.new(:name => l('timelines.new_scenario'))
@scenario = @project.scenarios.new(:name => l('timelines.new_scenario'))
end
def create
@scenario = @project.timelines_scenarios.new(permitted_params.scenario)
@scenario = @project.scenarios.new(permitted_params.scenario)
if @scenario.save
flash[:notice] = l(:notice_successful_create)
@ -55,11 +52,11 @@ class Timelines::TimelinesScenariosController < ApplicationController
end
def edit
@scenario = @project.timelines_scenarios.find(params[:id])
@scenario = @project.scenarios.find(params[:id])
end
def update
@scenario = @project.timelines_scenarios.find(params[:id])
@scenario = @project.scenarios.find(params[:id])
if @scenario.update_attributes(permitted_params.scenario)
flash[:notice] = l(:notice_successful_update)
@ -70,11 +67,11 @@ class Timelines::TimelinesScenariosController < ApplicationController
end
def confirm_destroy
@scenario = @project.timelines_scenarios.find(params[:id])
@scenario = @project.scenarios.find(params[:id])
end
def destroy
@scenario = @project.timelines_scenarios.find(params[:id])
@scenario = @project.scenarios.find(params[:id])
@scenario.destroy
flash[:notice] = l(:notice_successful_delete)

@ -214,4 +214,7 @@ class TimeEntries::ReportsController < ApplicationController
sql
end
def default_breadcrumb
I18n.t(:label_spent_time)
end
end

@ -1,62 +0,0 @@
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesProjectsController < ApplicationController
extend Timelines::Pagination::Controller
unloadable
helper :timelines
before_filter :determine_base
accept_key_auth :index, :show
timelines_paginate_model Project
def index
options = {:order => 'lft'}
if params[:ids]
ids, identifiers = params[:ids].split(/,/).map(&:strip).partition { |s| s =~ /^\d*$/ }
ids = ids.map(&:to_i).sort
identifiers = identifiers.sort
options[:conditions] = ["id IN (?) OR identifier IN (?)", ids, identifiers]
end
@projects = @base.visible.all(options)
respond_to do |format|
format.html { render_404 }
format.api
end
end
def show
@project = @base.find(params[:id])
authorize
return if performed?
respond_to do |format|
format.html { render_404 }
format.api
end
end
protected
def determine_base
if params[:project_type_id]
@base = Timelines::ProjectType.find(params[:project_type_id]).projects
else
@base = Project
end
end
end

@ -9,71 +9,72 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::TimelinesTimelinesController < ApplicationController
class TimelinesController < ApplicationController
unloadable
helper :timelines
before_filter :disable_api
before_filter :find_project_by_project_id
before_filter :authorize
menu_item :timelines_reports
menu_item :reports
def index
@timeline = @project.timelines_timelines.first
@timeline = @project.timelines.first
if @timeline.nil?
redirect_to new_timelines_project_timeline_path(@project)
redirect_to new_project_timeline_path(@project)
else
redirect_to timelines_project_timeline_path(@project, @timeline)
redirect_to project_timeline_path(@project, @timeline)
end
end
def show
@visible_timelines = @project.timelines_timelines.all
@timeline = @project.timelines_timelines.find(params[:id])
@visible_timelines = @project.timelines.all
@timeline = @project.timelines.find(params[:id])
end
def new
@timeline = @project.timelines_timelines.build
@timeline = @project.timelines.build
end
def create
remove_blank_options
@timeline = @project.timelines_timelines.build(params[:timeline])
@timeline = @project.timelines.build(params[:timeline])
if @timeline.save
flash[:notice] = l(:notice_successful_create)
redirect_to timelines_project_timeline_path(@project, @timeline)
redirect_to project_timeline_path(@project, @timeline)
else
render :action => "new"
end
end
def edit
@timeline = @project.timelines_timelines.find(params[:id])
@timeline = @project.timelines.find(params[:id])
end
def update
@timeline = @project.timelines_timelines.find(params[:id])
@timeline = @project.timelines.find(params[:id])
if @timeline.update_attributes(params[:timeline])
flash[:notice] = l(:notice_successful_update)
redirect_to timelines_project_timeline_path(@project, @timeline)
redirect_to project_timeline_path(@project, @timeline)
else
render :action => "edit"
end
end
def confirm_destroy
@timeline = @project.timelines_timelines.find(params[:id])
@timeline = @project.timelines.find(params[:id])
end
def destroy
@timeline = @project.timelines_timelines.find(params[:id])
@timeline = @project.timelines.find(params[:id])
@timeline.destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to timelines_project_timelines_path @project
redirect_to project_timelines_path @project
end
protected

@ -12,6 +12,8 @@
class TimelogController < ApplicationController
menu_item :issues
before_filter :disable_api
before_filter :find_project, :only => [:new, :create]
before_filter :find_time_entry, :only => [:show, :edit, :update, :destroy]
before_filter :authorize, :except => [:index]
@ -21,6 +23,7 @@ class TimelogController < ApplicationController
include SortHelper
include TimelogHelper
include CustomFieldsHelper
include PaginationHelper
def index
sort_init 'spent_on', 'desc'
@ -45,27 +48,17 @@ class TimelogController < ApplicationController
format.html {
# Paginate results
@entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset)
@entries = TimeEntry.visible.includes(:project, :activity, :user, {:issue => :tracker})
.where(cond.conditions)
.order(sort_clause)
.page(params[:page])
.per_page(per_page_param)
@total_hours = TimeEntry.visible.sum(:hours, :include => [:project, :issue], :conditions => cond.conditions).to_f
render :layout => !request.xhr?
}
format.api {
@entry_count = TimeEntry.visible.count(:include => [:project, :issue], :conditions => cond.conditions)
@entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
@entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
:conditions => cond.conditions,
:order => sort_clause,
:limit => @entry_pages.items_per_page,
:offset => @entry_pages.current.offset)
}
format.atom {
entries = TimeEntry.visible.find(:all,
:include => [:project, :activity, :user, {:issue => :tracker}],
@ -89,7 +82,6 @@ class TimelogController < ApplicationController
respond_to do |format|
# TODO: Implement html response
format.html { render :nothing => true, :status => 406 }
format.api
end
end
@ -113,12 +105,10 @@ class TimelogController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
}
format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
end
else
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@time_entry) }
end
end
end
@ -140,12 +130,10 @@ class TimelogController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_back_or_default :action => 'index', :project_id => @time_entry.project
}
format.api { head :ok }
end
else
respond_to do |format|
format.html { render :action => 'edit' }
format.api { render_validation_errors(@time_entry) }
end
end
end
@ -157,7 +145,6 @@ class TimelogController < ApplicationController
flash[:notice] = l(:notice_successful_delete)
redirect_to :back
}
format.api { head :ok }
end
else
respond_to do |format|
@ -165,7 +152,6 @@ class TimelogController < ApplicationController
flash[:error] = l(:notice_unable_delete_time_entry)
redirect_to :back
}
format.api { render_validation_errors(@time_entry) }
end
end
rescue ::ActionController::RedirectBackError

@ -11,12 +11,17 @@
#++
class TrackersController < ApplicationController
include PaginationHelper
layout 'admin'
before_filter :require_admin
def index
@tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position'
@trackers = Tracker.order('position')
.page(params[:page])
.per_page(per_page_param)
render :action => "index", :layout => false if request.xhr?
end

@ -13,6 +13,7 @@
class UsersController < ApplicationController
layout 'admin'
before_filter :disable_api
before_filter :require_admin, :except => [:show, :deletion_info, :destroy]
before_filter :find_user, :only => [:show,
:edit,
@ -29,18 +30,12 @@ class UsersController < ApplicationController
include SortHelper
include CustomFieldsHelper
include PaginationHelper
def index
sort_init 'login', 'asc'
sort_update %w(login firstname lastname mail admin created_on last_login_on)
case params[:format]
when 'xml', 'json'
@offset, @limit = api_offset_and_limit
else
@limit = per_page_option
end
scope = User
scope = scope.in_group(params[:group_id].to_i) if params[:group_id].present?
@ -52,21 +47,16 @@ class UsersController < ApplicationController
c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
end
@user_count = scope.count(:conditions => c.conditions)
@user_pages = Paginator.new self, @user_count, @limit, params['page']
@offset ||= @user_pages.current.offset
@users = scope.find :all,
:order => sort_clause,
:conditions => c.conditions,
:limit => @limit,
:offset => @offset
@users = scope.order(sort_clause)
.where(c.conditions)
.page(page_param)
.per_page(per_page_param)
respond_to do |format|
format.html {
@groups = Group.all.sort
render :layout => !request.xhr?
}
format.api
end
end
@ -86,7 +76,6 @@ class UsersController < ApplicationController
respond_to do |format|
format.html { render :layout => 'base' }
format.api
end
end
@ -121,7 +110,6 @@ class UsersController < ApplicationController
edit_user_path(@user)
)
}
format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
end
else
@auth_sources = AuthSource.find(:all)
@ -130,7 +118,6 @@ class UsersController < ApplicationController
respond_to do |format|
format.html { render :action => 'new' }
format.api { render_validation_errors(@user) }
end
end
end
@ -169,7 +156,6 @@ class UsersController < ApplicationController
flash[:notice] = l(:notice_successful_update)
redirect_to :back
}
format.api { head :ok }
end
else
@auth_sources = AuthSource.find(:all)
@ -179,7 +165,6 @@ class UsersController < ApplicationController
respond_to do |format|
format.html { render :action => :edit }
format.api { render_validation_errors(@user) }
end
end
rescue ::ActionController::RedirectBackError
@ -231,9 +216,6 @@ class UsersController < ApplicationController
redirect_to users_path
end
end
format.api do
head :ok
end
end
end

@ -37,6 +37,7 @@ class WikiController < ApplicationController
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed}
include AttachmentsHelper
include PaginationHelper
attr_reader :page, :related_page
@ -209,16 +210,13 @@ class WikiController < ApplicationController
# show page history
def history
@version_count = @page.content.versions.count
@version_pages = Paginator.new self, @version_count, per_page_option, params['p']
# don't load text
@versions = @page.content.versions.find :all,
:select => "id, user_id, notes, created_at, version",
:order => 'version DESC',
:limit => @version_pages.items_per_page + 1,
:offset => @version_pages.current.offset
@versions = @page.content.versions.select("id, user_id, notes, created_at, version")
.order('version DESC')
.page(params[:page])
.per_page(per_page_param)
render :layout => false if request.xhr?
render :layout => !request.xhr?
end
def diff

@ -419,52 +419,6 @@ module ApplicationHelper
path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
end
def pagination_links_full(paginator, count=nil, options={})
page_param = options.delete(:page_param) || :page
per_page_links = options.delete(:per_page_links)
# options for the underlying classic pagination
ignored_options = [:window_size,
:always_show_anchors,
:link_to_current_page,
:name,
:prefix,
:suffix]
url_param = options.reject{ |key, value| ignored_options.include?(key) }
# don't reuse query params if filters are present
url_param.merge!(:fields => nil, :values => nil, :operators => nil) if params.delete(:set_filter)
html = ''
if paginator.current.previous
html << link_to_content_update(l(:label_previous), url_param.merge(page_param => paginator.current.previous), :class => 'navigate-left') + ' '
end
html << (pagination_links_each(paginator, options) do |n|
link_to_content_update(n.to_s, url_param.merge(page_param => n))
end || '')
if paginator.current.next
html << ' ' + link_to_content_update((l(:label_next)), url_param.merge(page_param => paginator.current.next), :class => 'navigate-right')
end
unless count.nil?
html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
if per_page_links != false && links = per_page_links(paginator.items_per_page)
html << " | #{links}"
end
end
html.html_safe
end
def per_page_links(selected=nil)
links = Setting.per_page_options_array.collect do |n|
n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
end
links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
end
def reorder_links(name, url, options = {})
method = options[:method] || :post
@ -1095,7 +1049,8 @@ module ApplicationHelper
tags = ''
tags += javascript_tag(%Q{
window.openProject = new OpenProject({
urlRoot : '#{Redmine::Utils.relative_url_root}'
urlRoot : '#{Redmine::Utils.relative_url_root}',
loginUrl: '#{url_for :controller => "/account", :action => "login"}'
});
})
unless User.current.pref.warn_on_leaving_unsaved == '0'
@ -1166,12 +1121,17 @@ module ApplicationHelper
#
# Returns the footer text displayed in the layout file.
#
def layout_footer_text
content_tag :div, :class => 'bgl' do
content_tag :div, :class => 'bgr' do
raw t(:text_powered_by, :link => link_to(Redmine::Info.app_name, Redmine::Info.url))
def footer_content
elements = []
unless OpenProject::Footer.content.nil?
OpenProject::Footer.content.each do |name, value|
elements << (content_tag :span, :class => "footer_#{name}" do
value.respond_to?(:call) ? value.call.strip : value.strip
end)
end
end
elements << I18n.t(:text_powered_by, :link => link_to(Redmine::Info.app_name, Redmine::Info.url))
result = elements.join(", ").html_safe
end
# start timelines stuff
@ -1197,7 +1157,7 @@ module ApplicationHelper
end
link_to(h(text),
timelines_project_planning_element_path(planning_element.project, planning_element),
project_planning_element_path(planning_element.project, planning_element),
:title => planning_element.name)
end
@ -1228,7 +1188,7 @@ module ApplicationHelper
end
link = link_to(h("*#{planning_element.id} #{planning_element.planning_element_status.nil? ? "" : planning_element.planning_element_status.name + ":"} #{planning_element.name} "),
timelines_project_planning_element_path(planning_element.project, planning_element),
project_planning_element_path(planning_element.project, planning_element),
:title => h("#{truncate(planning_element.name, :length => 100)} #{planning_element.planning_element_status.nil? ? "" :
"(" + planning_element.planning_element_status.name + ")"}"))
link += "#{planning_element.start_date.nil? ? "[?]" : planning_element.start_date.to_s}#{start_date_change}#{planning_element.end_date.nil? ? "[?]" :
@ -1257,7 +1217,7 @@ module ApplicationHelper
parse_redmine_links_without_planning_element_links(text, project, obj, attr, only_path, options)
text.gsub!(%r{(?:\W|^|\A)((\*+)(\d+))(?:\W|$|\z)}) do |match|
text, stars, id = $1, $2, $3
planning_element = Timelines::PlanningElement.without_deleted.visible.find_by_id(id)
planning_element = PlanningElement.without_deleted.visible.find_by_id(id)
if planning_element.present?
if stars == "*"
@ -1293,7 +1253,13 @@ module ApplicationHelper
end
def password_complexity_requirements
raw "<em>" + l(:text_caracters_minimum, :count => Setting.password_min_length) + "</em>"
rules = OpenProject::Passwords::Evaluator.rules_description
# use 0..0, so this doesn't fail if rules is an empty string
rules[0] = rules[0..0].upcase
s = raw "<em>" + OpenProject::Passwords::Evaluator.min_length_description + "</em>"
s += raw "<br /><em>" + rules + "</em>" unless rules.empty?
s
end
end

@ -288,4 +288,12 @@ module IssuesHelper
ActiveSupport::Inflector.transliterate(el[0]).downcase
end
end
def value_overridden_by_children?(attrib)
Issue::ATTRIBS_WITH_VALUES_FROM_CHILDREN.include? attrib
end
def attrib_disabled?(issue, attrib)
value_overridden_by_children?(attrib) && !(issue.new_record? || issue.leaf?)
end
end

@ -83,7 +83,7 @@ module JournalsHelper
end
if editable
l << link_to_in_place_notes_editor(image_tag('edit.png', :alt => l(:button_edit), :title => l(:button_edit)), "journal-#{journal.id}-notes",
{ :controller => 'journals', :action => 'edit', :id => journal },
{ :controller => '/journals', :action => 'edit', :id => journal },
:title => l(:button_edit))
end
end

@ -0,0 +1,103 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
#
# Copyright (C) 2012-2013 the OpenProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'will_paginate'
module PaginationHelper
def pagination_links_full(paginator, options = {})
merged_options = { next_label: I18n.t(:label_next),
previous_label: I18n.t(:label_next),
container: true }.merge(options)
html = ''.html_safe
if paginator.total_entries > 0
html << will_paginate(paginator, merged_options.merge({ :container => false }) )
html << content_tag(:span, "(#{paginator.offset + 1} - #{paginator.offset + paginator.length}/#{paginator.total_entries})", :class => 'range')
if per_page_links && links = per_page_links(paginator.per_page)
html << links
end
end
merged_options[:container] ?
content_tag(:p, html, :class => "pagination") :
html
end
def per_page_links(selected=nil)
links = Setting.per_page_options_array.collect do |n|
n == selected ?
content_tag(:span, n, :class => 'current') :
link_to_content_update(n, params.merge(:per_page => n))
end
content_tag :span, :class => 'per_page_options' do
links.size > 1 ? l(:label_display_per_page, links.join(', ')).html_safe : nil
end
end
# Returns page option used for pagination
# based on:
# * offset
# * limit
# * page
# parameters.
# Preferes page over the other two and
# calculates page in it's absence based on limit and offset.
# Return 1 if all else fails.
def page_param(options = params)
page = if options[:page]
options[:page].to_i
elsif options[:offset] && options[:limit]
begin
# + 1 as page is not 0 but 1 based
options[:offset].to_i/per_page_param(options) + 1
rescue ZeroDivisionError
1
end
else
1
end
page > 0 ?
page :
1
end
# Returns per_page option used for pagination
# based on:
# * per_page session value
# * per_page options value
# * limit options value
# in that order
# Return smallest possible setting if all else fails.
def per_page_param(options = params)
per_page_candidates = [session[:per_page].to_i, options[:per_page].to_i, options[:limit].to_i]
unless (union = per_page_candidates & Setting.per_page_options_array).empty?
session[:per_page] = union.first
union.first
else
Setting.per_page_options_array.sort.first
end
end
end

@ -9,7 +9,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
module TimelinesPlanningElementsHelper
module PlanningElementsHelper
def render_planning_element(api, planning_element)
api.planning_element(planning_element.destroyed? ? {:destroyed => true} : {}) do
api.id(planning_element.id)
@ -72,7 +72,7 @@ module TimelinesPlanningElementsHelper
if include_journals?
api.array :journals, :size => planning_element.journals.size do
planning_element.journals.each do |journal|
render(:partial => '/timelines/timelines_planning_element_journals/journal.api',
render(:partial => '/api/v2/planning_element_journals/journal.api',
:object => journal)
end
end

@ -25,7 +25,7 @@ module ProjectsHelper
{:name => 'repository', :action => :manage_repository, :partial => 'projects/settings/repository', :label => :label_repository},
{:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural},
{:name => 'activities', :action => :manage_project_activities, :partial => 'projects/settings/activities', :label => :enumeration_activities},
{:name => 'timelines', :action => :manage_timelines_project_configuration, :partial => 'projects/settings/timelines', :label => :'timelines.timelines_settings'}
{:name => 'timelines', :action => :manage_project_configuration, :partial => 'projects/settings/timelines', :label => :'timelines.settings'}
]
tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
end

@ -1,5 +1,4 @@
#encoding: utf-8
#-- copyright
# OpenProject is a project management system.
#
@ -43,7 +42,7 @@ module TimelinesHelper
end
def parent_id_select_tag(form, planning_element)
available_parents = planning_element.project.timelines_planning_elements.without_deleted.find(:all, :order => "COALESCE(parent_id, id), parent_id")
available_parents = planning_element.project.planning_elements.without_deleted.find(:all, :order => "COALESCE(parent_id, id), parent_id")
available_parents -= [planning_element]
available_options = available_parents.map do |pe|
@ -85,7 +84,7 @@ module TimelinesHelper
end
def options_for_project_types
Timelines::ProjectType.all.map { |t| [t.name, t.id] }
ProjectType.all.map { |t| [t.name, t.id] }
end
def options_for_responsible(project)
@ -95,14 +94,14 @@ module TimelinesHelper
def visible_parent_project(project)
parent = project.parent
while parent.present? && !parent.timelines_visible?
while parent.present? && !parent.visible?
parent = parent.parent
end
parent
end
def timelines_header_tags
def header_tags
%Q{
<style type='text/css'>
#content table.issues td.center,

@ -33,7 +33,7 @@ module TimelinesJournalsHelper
def timelines_time_tag(time)
text = format_time(time)
if @project
link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time))
link_to(text, {:controller => '/activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time))
else
content_tag('label', text, :title => format_time(time), :class => "timestamp")
end
@ -89,7 +89,7 @@ module TimelinesJournalsHelper
end
if editable
l << link_to_in_place_notes_editor(image_tag('edit.png', :alt => l(:button_edit), :title => l(:button_edit)), "journal-#{journal.id}-notes",
{ :controller => 'journals', :action => 'edit', :id => journal },
{ :controller => '/journals', :action => 'edit', :id => journal },
:title => l(:button_edit))
end
end

@ -9,16 +9,16 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::AlternateDate < ActiveRecord::Base
class AlternateDate < ActiveRecord::Base
unloadable
self.table_name = 'timelines_alternate_dates'
self.table_name = 'alternate_dates'
include Timelines::TimestampsCompatibility
include TimestampsCompatibility
belongs_to :planning_element, :class_name => "Timelines::PlanningElement",
belongs_to :planning_element, :class_name => "PlanningElement",
:foreign_key => 'planning_element_id'
belongs_to :scenario, :class_name => "Timelines::Scenario",
belongs_to :scenario, :class_name => "Scenario",
:foreign_key => 'scenario_id'
# history-related = historic

@ -9,16 +9,16 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::AvailableProjectStatus < ActiveRecord::Base
class AvailableProjectStatus < ActiveRecord::Base
unloadable
self.table_name = 'timelines_available_project_statuses'
self.table_name = 'available_project_statuses'
include Timelines::TimestampsCompatibility
include TimestampsCompatibility
belongs_to :project_type, :class_name => 'Timelines::ProjectType',
belongs_to :project_type, :class_name => 'ProjectType',
:foreign_key => 'project_type_id'
belongs_to :reported_project_status, :class_name => 'Timelines::ReportedProjectStatus',
belongs_to :reported_project_status, :class_name => 'ReportedProjectStatus',
:foreign_key => 'reported_project_status_id'
attr_accessible :reported_project_status_id

@ -9,16 +9,16 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::DefaultPlanningElementType < ActiveRecord::Base
class DefaultPlanningElementType < ActiveRecord::Base
unloadable
self.table_name = 'timelines_default_planning_element_types'
self.table_name = 'default_planning_element_types'
include Timelines::TimestampsCompatibility
include TimestampsCompatibility
belongs_to :project_type, :class_name => 'Timelines::ProjectType',
belongs_to :project_type, :class_name => 'ProjectType',
:foreign_key => 'project_type_id'
belongs_to :planning_element_type, :class_name => 'Timelines::PlanningElementType',
belongs_to :planning_element_type, :class_name => 'PlanningElementType',
:foreign_key => 'planning_element_type_id'
attr_accessible :planning_element_type_id

@ -9,16 +9,16 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::EnabledPlanningElementType < ActiveRecord::Base
class EnabledPlanningElementType < ActiveRecord::Base
unloadable
self.table_name = 'timelines_enabled_planning_element_types'
self.table_name = 'enabled_planning_element_types'
include Timelines::TimestampsCompatibility
include TimestampsCompatibility
belongs_to :project, :class_name => 'Project',
:foreign_key => 'project_id'
belongs_to :planning_element_type, :class_name => 'Timelines::PlanningElementType',
belongs_to :planning_element_type, :class_name => 'PlanningElementType',
:foreign_key => 'planning_element_type_id'
attr_accessible :planning_element_type_id

@ -64,6 +64,7 @@ class Issue < ActiveRecord::Base
:order_column => "#{table_name}.id"
DONE_RATIO_OPTIONS = %w(issue_field issue_status)
ATTRIBS_WITH_VALUES_FROM_CHILDREN = %w(priority_id start_date due_date estimated_hours done_ratio)
attr_protected :project_id, :author_id, :lft, :rgt

@ -342,7 +342,7 @@ class MailHandler < ActionMailer::Base
user = User.new
user.mail = email_address
user.login = user.mail
user.password = SecureRandom.hex [Setting.password_min_length.to_i, 10].max
user.random_password!
user.language = Setting.default_language
names = fullname.blank? ? email_address.gsub(/@.*$/, '').split('.') : fullname.split

@ -9,27 +9,27 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::PlanningElement < ActiveRecord::Base
class PlanningElement < ActiveRecord::Base
unloadable
self.table_name = 'timelines_planning_elements'
self.table_name = 'planning_elements'
acts_as_tree
include Timelines::TimestampsCompatibility
include Timelines::NestedAttributesForApi
include TimestampsCompatibility
include NestedAttributesForApi
include ActiveModel::ForbiddenAttributesProtection
belongs_to :project, :class_name => "Project"
belongs_to :responsible, :class_name => "User",
:foreign_key => "responsible_id"
belongs_to :planning_element_type, :class_name => "Timelines::PlanningElementType",
belongs_to :planning_element_type, :class_name => "PlanningElementType",
:foreign_key => 'planning_element_type_id'
belongs_to :planning_element_status, :class_name => "Timelines::PlanningElementStatus",
belongs_to :planning_element_status, :class_name => "PlanningElementStatus",
:foreign_key => 'planning_element_status_id'
has_many :alternate_dates, :class_name => "Timelines::AlternateDate",
has_many :alternate_dates, :class_name => "AlternateDate",
:foreign_key => 'planning_element_id',
:autosave => true,
:dependent => :delete_all
@ -41,9 +41,9 @@ class Timelines::PlanningElement < ActiveRecord::Base
acts_as_watchable
acts_as_journalized :activity_type => 'timelines_planning_elements',
acts_as_journalized :activity_type => 'planning_elements',
:activity_permission => :view_planning_elements,
:event_url => Proc.new { |j| {:controller => 'timelines_planning_elements',
:event_url => Proc.new { |j| {:controller => '/planning_elements',
:action => 'show',
:id => j.journaled,
:project_id => j.project,
@ -54,49 +54,49 @@ class Timelines::PlanningElement < ActiveRecord::Base
# instances of one planning element are returned.
SQL_FOR_AT = {
:select => "#{Timelines::PlanningElement.quoted_table_name}.id,
#{Timelines::PlanningElement.quoted_table_name}.name,
#{Timelines::PlanningElement.quoted_table_name}.description,
#{Timelines::PlanningElement.quoted_table_name}.planning_element_status_comment,
#{Timelines::AlternateDate.quoted_table_name }.start_date,
#{Timelines::AlternateDate.quoted_table_name }.end_date,
#{Timelines::PlanningElement.quoted_table_name}.parent_id,
#{Timelines::PlanningElement.quoted_table_name}.project_id,
#{Timelines::PlanningElement.quoted_table_name}.responsible_id,
#{Timelines::PlanningElement.quoted_table_name}.planning_element_type_id,
#{Timelines::PlanningElement.quoted_table_name}.planning_element_status_id,
#{Timelines::PlanningElement.quoted_table_name}.created_at,
#{Timelines::PlanningElement.quoted_table_name}.deleted_at,
#{Timelines::AlternateDate.quoted_table_name }.updated_at",
:select => "#{PlanningElement.quoted_table_name}.id,
#{PlanningElement.quoted_table_name}.name,
#{PlanningElement.quoted_table_name}.description,
#{PlanningElement.quoted_table_name}.planning_element_status_comment,
#{AlternateDate.quoted_table_name }.start_date,
#{AlternateDate.quoted_table_name }.end_date,
#{PlanningElement.quoted_table_name}.parent_id,
#{PlanningElement.quoted_table_name}.project_id,
#{PlanningElement.quoted_table_name}.responsible_id,
#{PlanningElement.quoted_table_name}.planning_element_type_id,
#{PlanningElement.quoted_table_name}.planning_element_status_id,
#{PlanningElement.quoted_table_name}.created_at,
#{PlanningElement.quoted_table_name}.deleted_at,
#{AlternateDate.quoted_table_name }.updated_at",
:joins => "LEFT JOIN (
SELECT
#{Timelines::AlternateDate.quoted_table_name}.planning_element_id,
MAX(#{Timelines::AlternateDate.quoted_table_name}.updated_at) AS updated_at
FROM #{Timelines::AlternateDate.quoted_table_name}
#{AlternateDate.quoted_table_name}.planning_element_id,
MAX(#{AlternateDate.quoted_table_name}.updated_at) AS updated_at
FROM #{AlternateDate.quoted_table_name}
WHERE
#{Timelines::AlternateDate.quoted_table_name}.created_at <= ?
#{AlternateDate.quoted_table_name}.created_at <= ?
GROUP BY
#{Timelines::AlternateDate.quoted_table_name}.planning_element_id
) AS timelines_alternate_dates_sub
ON #{Timelines::PlanningElement.quoted_table_name}.id = timelines_alternate_dates_sub.planning_element_id
#{AlternateDate.quoted_table_name}.planning_element_id
) AS alternate_dates_sub
ON #{PlanningElement.quoted_table_name}.id = alternate_dates_sub.planning_element_id
INNER JOIN
#{Timelines::AlternateDate.quoted_table_name}
ON #{Timelines::AlternateDate.quoted_table_name}.planning_element_id = timelines_alternate_dates_sub.planning_element_id
AND #{Timelines::AlternateDate.quoted_table_name}.updated_at = timelines_alternate_dates_sub.updated_at"
#{AlternateDate.quoted_table_name}
ON #{AlternateDate.quoted_table_name}.planning_element_id = alternate_dates_sub.planning_element_id
AND #{AlternateDate.quoted_table_name}.updated_at = alternate_dates_sub.updated_at"
}
scope :without_deleted, :conditions => "#{Timelines::PlanningElement.quoted_table_name}.deleted_at IS NULL"
scope :deleted, :conditions => "#{Timelines::PlanningElement.quoted_table_name}.deleted_at IS NOT NULL"
scope :without_deleted, :conditions => "#{PlanningElement.quoted_table_name}.deleted_at IS NULL"
scope :deleted, :conditions => "#{PlanningElement.quoted_table_name}.deleted_at IS NOT NULL"
scope :visible, lambda {|*args| { :include => :project,
:conditions => Timelines::PlanningElement.visible_condition(args.first || User.current) } }
:conditions => PlanningElement.visible_condition(args.first || User.current) } }
alias_method :destroy!, :destroy
scope :at_time, lambda { |time|
{:select => SQL_FOR_AT[:select],
:conditions => ["(#{Timelines::PlanningElement.quoted_table_name}.deleted_at IS NULL
OR #{Timelines::PlanningElement.quoted_table_name}.deleted_at >= ?)", time],
:conditions => ["(#{PlanningElement.quoted_table_name}.deleted_at IS NULL
OR #{PlanningElement.quoted_table_name}.deleted_at >= ?)", time],
:joins => sanitize_sql([SQL_FOR_AT[:joins], time]),
:readonly => true
}
@ -112,7 +112,7 @@ class Timelines::PlanningElement < ActiveRecord::Base
# Used for journal entry / activities list
def activity_type
'timelines_planning_elements'
'planning_elements'
end
# Used for activities list
@ -211,16 +211,16 @@ class Timelines::PlanningElement < ActiveRecord::Base
end
def all_scenarios
project.timelines_scenarios.sort_by(&:id).map do |scenario|
project.scenarios.sort_by(&:id).map do |scenario|
alternate_date = alternate_dates.to_a.find { |a| a.scenario_id.to_s == scenario.id.to_s }
alternate_date ||= alternate_dates.build.tap { |ad| ad.scenario_id = scenario.id }
Timelines::PlanningElementScenario.new(alternate_date)
PlanningElementScenario.new(alternate_date)
end
end
def scenarios
alternate_dates.scenaric.sort_by(&:scenario_id).map do |alternate_date|
Timelines::PlanningElementScenario.new(alternate_date)
PlanningElementScenario.new(alternate_date)
end
end
@ -246,7 +246,7 @@ class Timelines::PlanningElement < ActiveRecord::Base
alternate_date = alternate_dates.to_a.find { |date| date.scenario_id.to_s == pe_scenario['id'].to_s }
unless alternate_date
if self.new_record?
alternate_date = Timelines::AlternateDate.new.tap { |ad| ad.scenario_id = pe_scenario['id'] }
alternate_date = AlternateDate.new.tap { |ad| ad.scenario_id = pe_scenario['id'] }
alternate_date.planning_element = self
alternate_dates << alternate_date
else

@ -9,7 +9,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::PlanningElementScenario
class PlanningElementScenario
unloadable
attr_accessor :alternate_date

@ -9,10 +9,10 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::PlanningElementStatus < Enumeration
class PlanningElementStatus < Enumeration
unloadable
has_many :planning_elements, :class_name => "Timelines::PlanningElement",
has_many :planning_elements, :class_name => "PlanningElement",
:foreign_key => 'planning_element_status_id'
OptionName = :enumeration_planning_element_statuses

@ -9,32 +9,32 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::PlanningElementType < ActiveRecord::Base
class PlanningElementType < ActiveRecord::Base
unloadable
self.table_name = 'timelines_planning_element_types'
self.table_name = 'planning_element_types'
acts_as_list
default_scope :order => 'position ASC'
include Timelines::TimestampsCompatibility
include TimestampsCompatibility
extend Timelines::Pagination::Model
extend Pagination::Model
has_many :default_planning_element_types, :class_name => 'Timelines::DefaultPlanningElementType',
has_many :default_planning_element_types, :class_name => 'DefaultPlanningElementType',
:foreign_key => 'planning_element_type_id',
:dependent => :delete_all
has_many :project_types, :through => :default_planning_element_types
has_many :enabled_planning_element_types, :class_name => 'Timelines::EnabledPlanningElementType',
has_many :enabled_planning_element_types, :class_name => 'EnabledPlanningElementType',
:foreign_key => 'planning_element_type_id',
:dependent => :delete_all
has_many :projects, :through => :enabled_planning_element_types
belongs_to :color, :class_name => 'Timelines::Color',
belongs_to :color, :class_name => 'PlanningElementTypeColor',
:foreign_key => 'color_id'
has_many :planning_elements, :class_name => 'Timelines::PlanningElement',
has_many :planning_elements, :class_name => 'PlanningElement',
:foreign_key => 'planning_element_type_id',
:dependent => :nullify
@ -57,16 +57,16 @@ class Timelines::PlanningElementType < ActiveRecord::Base
def enabled_in?(object)
case object
when Timelines::ProjectType
when ProjectType
object.planning_element_types.include?(self)
when Project
object.timelines_planning_element_types.include?(self)
object.planning_element_types.include?(self)
else
false
end
end
def available_colors
Timelines::Color.all
PlanningElementTypeColor.all
end
end

@ -9,17 +9,17 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::Color < ActiveRecord::Base
class PlanningElementTypeColor < ActiveRecord::Base
unloadable
self.table_name = 'timelines_colors'
self.table_name = 'planning_element_type_colors'
acts_as_list
default_scope :order => 'position ASC'
include Timelines::TimestampsCompatibility
include TimestampsCompatibility
has_many :planning_element_types, :class_name => 'Timelines::PlanningElementType',
has_many :planning_element_types, :class_name => 'PlanningElementType',
:foreign_key => 'color_id',
:dependent => :nullify

@ -11,7 +11,7 @@
#++
class Principal < ActiveRecord::Base
extend Timelines::Pagination::Model
extend Pagination::Model
self.table_name = "#{table_name_prefix}users#{table_name_suffix}"

@ -93,7 +93,7 @@ class Project < ActiveRecord::Base
# timelines stuff
extend Timelines::Pagination::Model
extend Pagination::Model
scope :like, lambda { |q|
s = "%#{q.to_s.strip.downcase}%"
@ -103,126 +103,126 @@ class Project < ActiveRecord::Base
scope :selectable_projects
belongs_to :timelines_project_type, :class_name => "::Timelines::ProjectType"
belongs_to :project_type, :class_name => "::ProjectType"
belongs_to :timelines_responsible, :class_name => "User"
belongs_to :responsible, :class_name => "User"
has_many :timelines_timelines, :class_name => "::Timelines::Timeline",
has_many :timelines, :class_name => "::Timeline",
:dependent => :destroy
has_many :timelines_planning_elements, :class_name => "::Timelines::PlanningElement",
has_many :planning_elements, :class_name => "::PlanningElement",
:dependent => :destroy
has_many :timelines_scenarios, :class_name => "::Timelines::Scenario",
has_many :scenarios, :class_name => "::Scenario",
:dependent => :destroy
has_many :timelines_reportings_via_source, :class_name => "::Timelines::Reporting",
has_many :reportings_via_source, :class_name => "::Reporting",
:foreign_key => 'project_id',
:dependent => :delete_all
has_many :timelines_reportings_via_target, :class_name => "::Timelines::Reporting",
has_many :reportings_via_target, :class_name => "::Reporting",
:foreign_key => 'reporting_to_project_id',
:dependent => :delete_all
has_many :timelines_reporting_to_projects, :through => :timelines_reportings_via_source,
has_many :reporting_to_projects, :through => :reportings_via_source,
:source => :reporting_to_project
has_many :timelines_project_a_associations, :class_name => "::Timelines::ProjectAssociation",
has_many :project_a_associations, :class_name => "::ProjectAssociation",
:foreign_key => 'project_a_id',
:dependent => :delete_all
has_many :timelines_project_b_associations, :class_name => "::Timelines::ProjectAssociation",
has_many :project_b_associations, :class_name => "::ProjectAssociation",
:foreign_key => 'project_b_id',
:dependent => :delete_all
has_many :timelines_associated_a_projects, :through => :timelines_project_a_associations,
has_many :associated_a_projects, :through => :project_a_associations,
:source => :project_b
has_many :timelines_associated_b_projects, :through => :timelines_project_b_associations,
has_many :associated_b_projects, :through => :project_b_associations,
:source => :project_a
has_many :timelines_enabled_planning_element_types, :class_name => "::Timelines::EnabledPlanningElementType",
has_many :enabled_planning_element_types, :class_name => "::EnabledPlanningElementType",
:dependent => :delete_all
has_many :timelines_planning_element_types, :through => :timelines_enabled_planning_element_types,
has_many :planning_element_types, :through => :enabled_planning_element_types,
:source => :planning_element_type
include Timelines::CollectionProxy
include TimelinesCollectionProxy
collection_proxy :timelines_project_associations, :for => [:timelines_project_a_associations,
:timelines_project_b_associations] do
collection_proxy :project_associations, :for => [:project_a_associations,
:project_b_associations] do
def visible(user = User.current)
all.select { |assoc| assoc.visible?(user) }
end
end
collection_proxy :timelines_associated_projects, :for => [:timelines_associated_a_projects,
:timelines_associated_b_projects] do
collection_proxy :associated_projects, :for => [:associated_a_projects,
:associated_b_projects] do
def visible(user = User.current)
all.select { |other| other.timelines_visible?(user) }
all.select { |other| other.visible?(user) }
end
end
collection_proxy :timelines_reportings, :for => [:timelines_reportings_via_source,
:timelines_reportings_via_target],
collection_proxy :reportings, :for => [:reportings_via_source,
:reportings_via_target],
:leave_public => true
after_save :assign_default_planning_element_types_as_enabled_planning_element_types
safe_attributes 'timelines_project_type_id',
'timelines_planning_element_type_ids',
'timelines_responsible_id'
safe_attributes 'project_type_id',
'planning_element_type_ids',
'responsible_id'
def timelines_associated_project_candidates(user = User.current)
def associated_project_candidates(user = User.current)
# TODO: Check if admins shouldn't see all projects here
projects = Project.visible.all
projects.delete(self)
projects -= timelines_associated_projects
projects.select{|p| p.timelines_allows_association?}
projects -= associated_projects
projects.select{|p| p.allows_association?}
end
def timelines_associated_project_candidates_by_type(user = User.current)
def associated_project_candidates_by_type(user = User.current)
# TODO: values need sorting by project tree
timelines_associated_project_candidates(user).group_by(&:timelines_project_type)
associated_project_candidates(user).group_by(&:project_type)
end
def timelines_project_associations_by_type(user = User.current)
def project_associations_by_type(user = User.current)
# TODO: values need sorting by project tree
timelines_project_associations.visible.group_by do |a|
a.project(self).timelines_project_type
project_associations.visible.group_by do |a|
a.project(self).project_type
end
end
def timelines_reporting_to_project_candidates(user = User.current)
def reporting_to_project_candidates(user = User.current)
# TODO: Check if admins shouldn't see all projects here
projects = Project.visible.all
projects.delete(self)
projects -= timelines_reporting_to_projects
projects -= reporting_to_projects
projects
end
def timelines_visible?(user = User.current)
def visible?(user = User.current)
self.active? and (self.is_public? or user.admin? or user.member_of?(self))
end
def timelines_allows_association?
if self.timelines_project_type.present?
self.timelines_project_type.allows_association
def allows_association?
if self.project_type.present?
self.project_type.allows_association
else
true
end
end
def assign_default_planning_element_types_as_enabled_planning_element_types
return if timelines_enabled_planning_element_types.present?
return if timelines_project_type.blank?
return if enabled_planning_element_types.present?
return if project_type.blank?
self.timelines_planning_element_types = timelines_project_type.planning_element_types
self.planning_element_types = project_type.planning_element_types
end
def has_many_dependent_for_timelines_planning_elements
def has_many_dependent_for_planning_elements
# Overwrites :dependent => :destroy - before_destroy callback
# since we need to call the destroy! method instead of the destroy
# method which just moves the element to the recycle bin
timelines_planning_elements.each {|element| element.destroy!}
planning_elements.each {|element| element.destroy!}
end
def self.selectable_projects

@ -9,12 +9,12 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::ProjectAssociation < ActiveRecord::Base
class ProjectAssociation < ActiveRecord::Base
unloadable
self.table_name = 'timelines_project_associations'
self.table_name = 'project_associations'
include Timelines::TimestampsCompatibility
include TimestampsCompatibility
belongs_to :project_a, :class_name => "Project",
:foreign_key => 'project_a_id'
@ -25,6 +25,9 @@ class Timelines::ProjectAssociation < ActiveRecord::Base
attr_accessible :description
validate :validate,
:validate_projects_not_identical
def projects
[project_a, project_b].compact.uniq.sort_by(&:id)
end
@ -46,12 +49,16 @@ class Timelines::ProjectAssociation < ActiveRecord::Base
[:project_a, :project_b].each do |field|
project = send(field)
if project.present? # otherwise the presence_of validation will be triggered
errors.add(field, :project_association_not_allowed) unless project.timelines_allows_association?
errors.add(field, :project_association_not_allowed) unless project.allows_association?
end
end
end
def validate_projects_not_identical
errors.add(:base, :identical_projects) if project_a == project_b
end
def visible?(user = User.current)
projects.all? { |p| p.timelines_visible?(user) }
projects.all? { |p| p.visible?(user) }
end
end

@ -9,29 +9,29 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::ProjectType < ActiveRecord::Base
class ProjectType < ActiveRecord::Base
unloadable
extend Timelines::Pagination::Model
extend Pagination::Model
self.table_name = 'timelines_project_types'
self.table_name = 'project_types'
acts_as_list
default_scope :order => 'position ASC'
include Timelines::TimestampsCompatibility
include TimestampsCompatibility
has_many :projects, :class_name => 'Project',
:foreign_key => 'timelines_project_type_id'
:foreign_key => 'project_type_id'
has_many :available_project_statuses, :class_name => 'Timelines::AvailableProjectStatus',
has_many :available_project_statuses, :class_name => 'AvailableProjectStatus',
:foreign_key => 'project_type_id',
:dependent => :destroy
has_many :reported_project_statuses, :through => :available_project_statuses
has_many :default_planning_element_types, :class_name => 'Timelines::DefaultPlanningElementType',
has_many :default_planning_element_types, :class_name => 'DefaultPlanningElementType',
:foreign_key => 'project_type_id',
:dependent => :destroy
has_many :planning_element_types, :through => :default_planning_element_types

@ -491,18 +491,14 @@ class Query < ActiveRecord::Base
end
# Returns the issues
# Valid options are :order, :offset, :limit, :include, :conditions
# Valid options are :order, :include, :conditions
def issues(options={})
order_option = [group_by_sort_order, options[:order]].reject {|s| s.blank?}.join(',')
order_option = nil if order_option.blank?
Issue.find :all, :include => ([:status, :project] + (options[:include] || [])).uniq,
:conditions => ::Query.merge_conditions(statement, options[:conditions]),
:order => order_option,
:limit => options[:limit],
:offset => options[:offset]
rescue ::ActiveRecord::StatementInvalid => e
raise ::Query::StatementInvalid.new(e.message)
Issue.where(::Query.merge_conditions(statement, options[:conditions]))
.includes([:status, :project] + (options[:include] || []).uniq)
.order(order_option)
end
# Returns the journals

@ -9,9 +9,9 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::ReportedProjectStatus < Enumeration
class ReportedProjectStatus < Enumeration
extend Timelines::Pagination::Model
extend Pagination::Model
unloadable
@ -21,7 +21,7 @@ class Timelines::ReportedProjectStatus < Enumeration
:order => "name" }
}
has_many :reportings, :class_name => "Timelines::Reporting",
has_many :reportings, :class_name => "Reporting",
:foreign_key => 'reported_project_status_id'
OptionName = :enumeration_reported_project_statuses

@ -9,18 +9,18 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::Reporting < ActiveRecord::Base
class Reporting < ActiveRecord::Base
unloadable
self.table_name = 'timelines_reportings'
self.table_name = 'reportings'
include Timelines::TimestampsCompatibility
include TimestampsCompatibility
belongs_to :project
belongs_to :reporting_to_project, :class_name => 'Project',
:foreign_key => 'reporting_to_project_id'
belongs_to :reported_project_status, :class_name => 'Timelines::ReportedProjectStatus',
belongs_to :reported_project_status, :class_name => 'ReportedProjectStatus',
:foreign_key => 'reported_project_status_id'
attr_accessible :reported_project_status_comment,
@ -31,12 +31,12 @@ class Timelines::Reporting < ActiveRecord::Base
validates_uniqueness_of :reporting_to_project_id, :scope => :project_id
def visible?(user = User.current)
reporting_to_project.timelines_visible?(user) && project.timelines_visible?(user)
reporting_to_project.visible?(user) && project.visible?(user)
end
def possible_reported_project_statuses
reporting_to_project.timelines_project_type.present? ?
reporting_to_project.timelines_project_type.reported_project_statuses :
reporting_to_project.project_type.present? ?
reporting_to_project.project_type.reported_project_statuses :
[]
end
end

@ -9,17 +9,17 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::Scenario < ActiveRecord::Base
class Scenario < ActiveRecord::Base
unloadable
self.table_name = 'timelines_scenarios'
self.table_name = 'scenarios'
include Timelines::TimestampsCompatibility
include TimestampsCompatibility
include ActiveModel::ForbiddenAttributesProtection
belongs_to :project
has_many :alternate_dates, :class_name => 'Timelines::AlternateDate',
has_many :alternate_dates, :class_name => 'AlternateDate',
:foreign_key => 'scenario_id',
:dependent => :delete_all

@ -9,7 +9,7 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class Timelines::Timeline < ActiveRecord::Base
class Timeline < ActiveRecord::Base
class Empty
attr_accessor :id, :name
@ -27,11 +27,11 @@ class Timelines::Timeline < ActiveRecord::Base
serialize :options
self.table_name = 'timelines_timelines'
self.table_name = 'timelines'
default_scope :order => 'name ASC'
include Timelines::TimestampsCompatibility
include TimestampsCompatibility
belongs_to :project, :class_name => "Project"
@ -68,7 +68,7 @@ class Timelines::Timeline < ActiveRecord::Base
"planning_element_responsibles",
"planning_element_types",
"planning_element_time_types",
"planning_element_time_absolute_one",
"planning_element_time_absolute_one",
"planning_element_time_absolute_two",
"planning_element_time_relative_one",
"planning_element_time_relative_two",
@ -170,12 +170,12 @@ class Timelines::Timeline < ActiveRecord::Base
# that are reporting into the project that this timeline is
# referencing.
Timelines::PlanningElementType.find(:all, :order => :name)
PlanningElementType.find(:all, :order => :name)
end
def selected_planning_element_types
resolve_with_none_element(:planning_element_types) do |ary|
Timelines::PlanningElementType.find(ary)
PlanningElementType.find(ary)
end
end
@ -183,25 +183,25 @@ class Timelines::Timeline < ActiveRecord::Base
resolve_with_none_element(:planning_element_time_types) do |ary|
Timelines::PlanningElementType.find(ary)
end
end
end
def available_project_types
Timelines::ProjectType.find(:all)
ProjectType.find(:all)
end
def selected_project_types
resolve_with_none_element(:project_types) do |ary|
Timelines::ProjectType.find(ary)
ProjectType.find(ary)
end
end
def available_project_status
Timelines::ReportedProjectStatus.find(:all, :order => :name)
ReportedProjectStatus.find(:all, :order => :name)
end
def selected_project_status
resolve_with_none_element(:project_status) do |ary|
Timelines::ReportedProjectStatus.find(ary)
ReportedProjectStatus.find(ary)
end
end
@ -274,12 +274,12 @@ class Timelines::Timeline < ActiveRecord::Base
def selected_grouping_project_types
resolve_with_none_element(:grouping_two_selection) do |ary|
Timelines::ProjectType.find(ary)
ProjectType.find(ary)
end
end
def available_grouping_project_types
Timelines::ProjectType.available_grouping_project_types
ProjectType.available_grouping_project_types
end
protected

@ -104,7 +104,7 @@ class User < Principal
validates_confirmation_of :password, :allow_nil => true
validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
validate :password_not_too_short
validate :password_meets_requirements
before_save :encrypt_password
before_create :sanitize_mail_notification_setting
@ -293,11 +293,9 @@ class User < Principal
end
# Generate and set a random password.
def random_password
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
password = chars.shuffle[0,40].join
self.password = password
self.password_confirmation = password
def random_password!
self.password = OpenProject::Passwords::Generator.random_password
self.password_confirmation = self.password
self
end
@ -624,7 +622,7 @@ class User < Principal
u.admin = false
u.status = User::STATUS_LOCKED
u.first_login = false
u.random_password
u.random_password!
end).save
raise 'Unable to create the automatic migration user.' if system_user.new_record?
end
@ -655,12 +653,15 @@ class User < Principal
protected
# Password length validation based on setting
def password_not_too_short
minimum_length = Setting.password_min_length.to_i
if !password.nil? && password.size < minimum_length
errors.add(:password, :too_short, :count => minimum_length)
end
# Password requirement validation based on settings
def password_meets_requirements
# Passwords are stored hashed in self.hashed_password,
# self.password is only set when it was changed after the last
# save. Otherwise, password is nil.
unless self.password.nil? or anonymous?
password_errors = OpenProject::Passwords::Evaluator.errors_for_password(self.password)
password_errors.each { |error| errors.add(:password, error)}
end
end
private

@ -9,7 +9,9 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
api.array :issues, api_meta(:total_count => @issue_count, :offset => @offset, :limit => @limit) do
api.array :issues, api_meta(:total_count => @issues.total_entries,
:offset => @issues.offset,
:limit => @issues.per_page) do
@issues.each do |issue|
api.issue do
api.id issue.id

@ -9,7 +9,9 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
api.array :news, api_meta(:total_count => @news_count, :offset => @offset, :limit => @limit) do
api.array :news, api_meta(:total_count => @newss.total_entries,
:offset => @newss.offset,
:limit => @newss.per_page) do
@newss.each do |news|
api.news do
api.id news.id

@ -9,7 +9,9 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
api.array :projects, api_meta(:total_count => @project_count, :offset => @offset, :limit => @limit) do
api.array :projects, api_meta(:total_count => @projects.total_entries,
:offset => @projects.offset,
:limit => @projects.per_page) do
@projects.each do |project|
api.project do
api.id project.id

@ -9,7 +9,9 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
api.array :users, api_meta(:total_count => @user_count, :offset => @offset, :limit => @limit) do
api.array :users, api_meta(:total_count => @users.total_entries,
:offset => @users.offset,
:limit => @users.per_page) do
@users.each do |user|
api.user do
api.id user.id

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

Loading…
Cancel
Save