Merge remote-tracking branch 'op/dev' into reactive_fassade

pull/4769/head
Roman Roelofsen 8 years ago
commit 3d2eddad39
  1. 118
      app/assets/stylesheets/_work_packages_show_view_overwrite.scss
  2. 3
      app/assets/stylesheets/content/_forms.sass
  3. 44
      app/views/roles/_form.html.erb
  4. 1
      config/locales/js-en.yml
  5. 13
      frontend/app/components/wp-display/field-types/wp-display-spent-time-field.directive.html
  6. 56
      frontend/app/components/wp-display/field-types/wp-display-spent-time-field.module.ts
  7. 27
      frontend/app/components/wp-display/wp-display-field/wp-display-field.config.ts
  8. 20
      lib/api/v3/work_packages/work_package_collection_representer.rb

@ -31,26 +31,62 @@ body.controller-work_packages.action-show {
}
.work-packages--show-view {
display: flex;
flex-direction: column;
height: inherit;
#toolbar {
display: flex;
justify-content: flex-end;
flex-wrap: wrap-reverse;
@include clearfix;
}
.toolbar-container {
@include clearfix;
margin-bottom: 20px;
padding-right: 20px;
}
ul#toolbar-items {
@include clearfix;
order: 2;
margin: 0 0 1rem 2rem;
button {
margin-bottom: 0;
}
li {
float: left;
position: relative;
&.toolbar-item:first-of-type {
margin-left: 0;
}
.dropdown {
top: 100% !important;
right: 0px !important;
left: auto !important;
margin-top: 0;
ul li {
float: none;
}
}
}
}
.work-packages--split-view {
height: auto;
flex-shrink: 8;
border-top: 1px solid #ccc;
overflow: hidden;
position: absolute;
padding-right: 20px;
top: 76px;
left: 20px;
right: 0px;
bottom: 0px;
overflow: visible;
// Important for Safari
height: initial;
// Important for Firefox to let 'flex-shrink' work correctly.
min-height: 0;
&[cg-busy] {
overflow: visible;
@ -104,10 +140,6 @@ body.controller-work_packages.action-show {
}
}
.work-packages--split-view {
overflow: visible !important;
}
.work-packages--right-panel {
min-width: 420px;
overflow-y: auto;
@ -146,31 +178,6 @@ body.controller-work_packages.action-show {
ul { padding-left: 2em; }
}
ul#toolbar-items {
@include clearfix;
float: right;
button {
margin-bottom: 0;
}
li {
float: left;
position: relative;
.dropdown {
top: 100% !important;
right: 0px !important;
left: auto !important;
margin-top: 0;
ul li {
float: none;
}
}
}
}
.activity-comment {
margin-top: 15px;
}
@ -203,6 +210,21 @@ body.controller-work_packages.action-show {
}
}
@media only screen and (max-width: 78rem) {
.work-packages--show-view {
// Important for Safari
height: initial;
}
}
@media only screen and (max-width: 679px) {
#toolbar {
#toolbar-items {
margin-left: 0;
}
}
}
#work-packages-index {
.wiki-anchor {
display: none;
@ -210,19 +232,17 @@ body.controller-work_packages.action-show {
}
.work-packages--show-view {
padding-right: 20px;
.subject-header {
float: left;
margin-right: -470px;
margin-top: 0;
padding: 10px 470px 0 0;
width: 100%;
.subject-header-inner {
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
margin: 0;
padding: 0;
min-height: 50px;
width: initial;
align-self: center;
flex-grow: 1;
.wp-table--cell-span {
white-space: normal;
}
li .inline-edit { width: 100%; }

@ -147,6 +147,9 @@ $form--field-types: (text-field, text-area, select, check-box, radio-button, ran
width: 1rem
z-index: 2
.-columns-2
column-count: 2
.form--separator
border: 0
border-bottom: 1px solid $content-form-separator-color

@ -30,10 +30,10 @@ See doc/COPYRIGHT.rdoc for more details.
<%= error_messages_for 'role' %>
<% unless @role.builtin? %>
<section class="form--section">
<div class="form--field"><%= f.text_field :name, required: true %></div>
<div class="form--field"><%= f.check_box :assignable %></div>
<% if @role.new_record? && @roles.any? %>
<div class="form--field"><%= f.text_field :name, required: true %></div>
<div class="form--field"><%= f.check_box :assignable %></div>
<% if @role.new_record? && @roles.any? %>
<div id="member_attributes">
<div class="form--field">
<%= styled_label_tag 'copy_workflow_from', l(:label_copy_workflow_from) %>
<div class="form--field-container">
@ -41,15 +41,15 @@ See doc/COPYRIGHT.rdoc for more details.
content_tag("option") + options_from_collection_for_select(@roles, :id, :name)) %>
</div>
</div>
<% end %>
</section>
</div>
<% end %>
<% end %>
<h3 class="form--section-title"><%= l(:label_permissions) %></h3>
<section class="form--section" id="permissions">
<div class="grid-block small-up-2 medium-up-3 large-up-4">
<% perms_by_module = @permissions.group_by {|p| p.project_module.to_s} %>
<% perms_by_module.keys.sort.each do |mod| %>
<div id="member_permissions">
<% perms_by_module = @permissions.group_by {|p| p.project_module.to_s} %>
<% perms_by_module.keys.sort.each do |mod| %>
<% module_name = mod.blank? ? 'fieldset--' + Project.model_name.human.downcase.gsub(' ', '_') : 'fieldset--' + l_or_humanize(mod, prefix: 'project_module_').downcase.gsub(' ', '_') %>
<fieldset class="form--fieldset -collapsible" id="<%= module_name %>">
<% module_name = mod.blank? ? "form--" + I18n.t('attributes.project') : "form--" + l_or_humanize(mod, prefix: 'project_module_').gsub(' ','_') %>
<div class="grid-section">
<fieldset class="form--fieldset -collapsible" id= "<%= module_name %>">
@ -59,18 +59,20 @@ See doc/COPYRIGHT.rdoc for more details.
(<%= check_all_links module_name %>)
</span>
</div>
<div class="autoscroll">
<div class="-columns-2">
<% perms_by_module[mod].each do |permission| %>
<label class="form--label-with-check-box">
<%= styled_check_box_tag 'role[permissions][]', permission.name, (@role.permissions.include? permission.name) %>
<%= l_or_humanize(permission.name, prefix: 'permission_') %>
</label>
<div class="form--field autoscroll -trailing-label ">
<label class="form--label">
<%= l_or_humanize(permission.name, prefix: 'permission_') %>
</label>
<div class="form--field-container">
<%= styled_check_box_tag 'role[permissions][]', permission.name, (@role.permissions.include? permission.name) %>
</div>
</div>
<% end %>
</div>
</fieldset>
</div>
<% end %>
</div>
<%= check_all_links 'permissions' %>
<%= hidden_field_tag 'role[permissions][]', '' %>
</section>
</fieldset>
<% end %>
</div>

@ -404,6 +404,7 @@ en:
message_error_during_bulk_delete: An error occurred while trying to delete work packages.
message_successful_bulk_delete: Successfully deleted work packages.
message_successful_show_in_fullscreen: "Click here to open this work package in fullscreen view."
message_view_spent_time: "View logged time"
no_value: "No value"
inline_create:
title: 'Click here to add a new work package to this list'

@ -0,0 +1,13 @@
<span ng-if="!$ctrl.field.isEmpty()">
<a role="link"
target="_blank"
title='{{ ::$ctrl.field.text.linkTitle }}'
ng-href="{{ $ctrl.field.timeEntriesLink }}">
{{ $ctrl.displayText }}
</a>
</span>
<span ng-if="$ctrl.field.isEmpty()" title="{{ $ctrl.displayText }}">
{{ $ctrl.displayText }}
</span>

@ -0,0 +1,56 @@
// -- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {WorkPackageResourceInterface} from '../../api/api-v3/hal-resources/work-package-resource.service';
import {DurationDisplayField} from './wp-display-duration-field.module';
export class SpentTimeDisplayField extends DurationDisplayField {
public template: string = '/components/wp-display/field-types/wp-display-spent-time-field.directive.html';
public text: any;
public timeEntriesLink: string;
public isManualRenderer = false;
constructor(public resource: WorkPackageResourceInterface,
public name: string,
public schema) {
super(resource, name, schema);
this.text = {
linkTitle: this.I18n.t('js.work_packages.message_view_spent_time')
};
if (resource.project) {
resource.project.$load().then(project => {
this.timeEntriesLink =
URI
.expand('/projects/{identifier}/time_entries', project)
.toString();
});
}
}
}

@ -26,19 +26,19 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {WorkPackageDisplayFieldService} from "./wp-display-field.service";
import {DisplayField} from "./wp-display-field.module";
import {TextDisplayField} from "../field-types/wp-display-text-field.module";
import {ResourceDisplayField} from "../field-types/wp-display-resource-field.module";
import {StringObjectDisplayField} from "../field-types/wp-display-string-object-field.module";
import {FormattableDisplayField} from "../field-types/wp-display-formattable-field.module";
import {DurationDisplayField} from "../field-types/wp-display-duration-field.module";
import {DateDisplayField} from "../field-types/wp-display-date-field.module";
import {DateTimeDisplayField} from "../field-types/wp-display-datetime-field.module.ts";
import {IdDisplayField} from "../field-types/wp-display-id-field.module";
import {BooleanDisplayField} from "../field-types/wp-display-boolean-field.module";
import {ProgressDisplayField} from "../field-types/wp-display-progress-field.module";
import {openprojectModule} from "../../../angular-modules";
import {WorkPackageDisplayFieldService} from './wp-display-field.service';
import {TextDisplayField} from '../field-types/wp-display-text-field.module';
import {ResourceDisplayField} from '../field-types/wp-display-resource-field.module';
import {StringObjectDisplayField} from '../field-types/wp-display-string-object-field.module';
import {FormattableDisplayField} from '../field-types/wp-display-formattable-field.module';
import {DurationDisplayField} from '../field-types/wp-display-duration-field.module';
import {DateDisplayField} from '../field-types/wp-display-date-field.module';
import {DateTimeDisplayField} from '../field-types/wp-display-datetime-field.module.ts';
import {IdDisplayField} from '../field-types/wp-display-id-field.module';
import {BooleanDisplayField} from '../field-types/wp-display-boolean-field.module';
import {ProgressDisplayField} from '../field-types/wp-display-progress-field.module';
import {openprojectModule} from '../../../angular-modules';
import {SpentTimeDisplayField} from '../field-types/wp-display-spent-time-field.module';
openprojectModule
.run((wpDisplayField:WorkPackageDisplayFieldService) => {
@ -59,5 +59,6 @@ openprojectModule
.addFieldType(DateTimeDisplayField, 'datetime', ['DateTime'])
.addFieldType(BooleanDisplayField, 'boolean', ['Boolean'])
.addFieldType(ProgressDisplayField, 'progress', ['percentageDone'])
.addFieldType(SpentTimeDisplayField, 'spentTime', ['spentTime'])
.addFieldType(IdDisplayField, 'id', ['id']);
});

@ -81,7 +81,7 @@ module API
collection :elements,
getter: -> (*) {
work_packages = eager_loaded_work_packages
work_packages = sorted_and_eager_loaded_work_packages
generated_classes = ::Hash.new do |hash, work_package|
hit = hash.values.find { |klass|
@ -114,19 +114,21 @@ module API
# Eager load elements used in the representer later
# to avoid n+1 queries triggered from each representer.
def eager_loaded_work_packages
def sorted_and_eager_loaded_work_packages
ids_in_order = represented.map(&:id)
work_packages = WorkPackage
.include_spent_hours(current_user)
.preload(element_decorator.to_eager_load)
.where(id: ids_in_order)
.select('work_packages.*')
.to_a
work_packages = eager_loaded_work_packages(ids_in_order)
work_packages.sort_by { |wp| ids_in_order.index(wp.id) }
end
def eager_loaded_work_packages(ids)
WorkPackage
.include_spent_hours(current_user)
.preload(element_decorator.to_eager_load)
.where(id: ids)
.select('work_packages.*')
end
private
def current_user_allowed_to_add_work_packages?

Loading…
Cancel
Save