OpenProject is the leading open source project management software.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openproject/app/views/projects/index.html.erb

374 lines
20 KiB

<%#-- copyright
OpenProject is a project management system.
Copyright (C) 2012-2017 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-2017 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.
++#%>
<% content_for :header_tags do %>
<%= javascript_include_tag "project/description_handling.js" %>
<%= javascript_include_tag "project/filters.js" %>
<% end %>
<% html_title(t(:label_project_plural)) -%>
<%= toolbar title: t(:label_project_plural) do %>
<% if User.current.allowed_to?(:add_project, nil, global: true) %>
<li class="toolbar-item">
<%= link_to new_project_path,
{ class: 'button -alt-highlight',
aria: {label: t(:label_project_new)},
title: t(:label_project_new)} do %>
<%= op_icon('button--icon icon-add') %>
<span class="button--text"><%= Project.model_name.human %></span>
<% end %>
</li>
<% end %>
<li class="toolbar-item">
<button id="projects-filter-toggle-button" title="<%= t(:label_filters_toggle) %>" class="button toolbar-icon <%= filter_set? ? '-active' : '' %>">
<op-icon icon-classes="icon-filter button--icon"></op-icon>
</button>
</li>
<li class="toolbar-item">
<%= link_to t(:label_overall_activity), activities_path, class: 'button' %>
</li>
<% end %>
<%= form_tag({}, method: :get, class: "project-filters #{filter_set? ? '-expanded' : ''}") do %>
<% operators_without_values = ['*', '!*', 't', 'w'] %>
<fieldset class="advanced-filters--container">
<a id="projects-filter-close-button" title="{{ ::I18n.t('js.close_form_title') }}" class="simple-filters--close icon-context icon-close"></a>
<legend><%= t(:label_filter_plural) %></legend>
<ul class="advanced-filters--filters">
<% allowed_filters(@query).each do |filter| %>
<li class="advanced-filters--filter <%= @query.find_active_filter(filter.name) ? '' : 'hidden' %>" filter-name="<%= filter.name %>" filter-type="<%= filter.type %>">
<label class='advanced-filters--filter-name' for="<%= filter.name %>">
<%= filter.human_name %>
</label>
<div class="advanced-filters--filter-operator">
<% selected_operator = (@query
.find_active_filter(filter.name)
.try(:operator) ||
filter.default_operator.symbol) %>
<%= select_tag :operator,
options_from_collection_for_select(
filter.available_operators,
:symbol,
:human_name,
selected_operator),
class: 'advanced-filters--select' %>
</div>
<% value_visibility = operators_without_values.include?(selected_operator) ? 'hidden' : '' %>
<% if %i(list list_optional list_all ).include? filter.type %>
<% is_multi_value = (@query.find_active_filter(filter.name)
.try(:values) &&
@query.find_active_filter(filter.name)
.values.try(:size) > 1) %>
<div class="advanced-filters--filter-value <%= value_visibility %> <%= is_multi_value ? 'multi-value' : '' %>">
<div class="single-select">
<span class="inline-label">
<%= select_tag :value,
options_from_collection_for_select(
filter.allowed_values,
:second,
:first,
@query.find_active_filter(filter.name)
.try(:values)
.try(:first)),
class: 'form--select -slim' %>
<span href="" class="form-label no-decoration-on-hover -transparent multi-select-toggle" tabindex="0">
<span class="icon-context icon-button icon-add icon4" title="<%= t(:label_enable_multi_select) %>" icon-name="add" icon-title="<%= t(:label_enable_multi_select) %>" css-class="icon4">
<span class="hidden-for-sighted"><%= t(:label_enable_multi_select) %></span>
</span>
</span>
</span>
</div>
<div class="multi-select">
<span class="inline-label">
<%= select_tag :value,
options_from_collection_for_select(
filter.allowed_values,
:second,
:first,
@query.find_active_filter(filter.name)
.try(:values)
),
class: 'form--select -slim',
multiple: true %>
<span href="" class="form-label no-decoration-on-hover -transparent multi-select-toggle" tabindex="0">
<span class="icon-context icon-button icon-minus2 icon4" title="<%= t(:label_enable_multi_select) %>" icon-name="minus2" icon-title="<%= t(:label_enable_multi_select) %>" css-class="icon4">
<span class="hidden-for-sighted"><%= t(:label_enable_multi_select) %></span>
</span>
</span>
</span>
</div>
</div>
<% elsif filter.type == :datetime_past %>
<%
value_format = ''
if ['>t-', '<t-', 't-'].include? selected_operator
value_format = 'days-ago'
elsif selected_operator == '=d'
value_format = 'on-date'
elsif selected_operator == '<>d'
value_format = 'between-dates'
end
%>
<div class="advanced-filters--filter-value <%= value_visibility %> <%= value_format %>">
<div class="days-ago">
<div class="form--field-container">
<div class="form--text-field-container -xslim">
<span class="inline-label">
<%= number_field_tag :value, value_format == 'days-ago' ? filter.values.first : '', class: 'advanced-filters--text-field -slim' %>
<label for="value" class="form-label -transparent">days</label>
</span>
</div>
</div>
</div>
<div class="on-date">
<div class="form--field-container">
<div class="form--text-field-container -slim">
<%= text_field_tag :value, value_format == 'on-date' ? filter.values.first : '', id: "on-date-value-#{filter.name}", class: 'advanced-filters--text-field -slim', size: '10' %>
<%= calendar_for("on-date-value-#{filter.name}") %>
</div>
</div>
</div>
<div class="between-dates">
<span><%= t(:label_date_from) %>:</span>
<%= text_field_tag :from_value, value_format == 'between-dates' ? filter.values.first : '', id: "between-dates-from-value-#{filter.name}", class: 'advanced-filters--text-field -slim', size: '10' %>
<%= calendar_for("between-dates-from-value-#{filter.name}") %>
<span><%= t(:label_date_to) %>:</span>
<%= text_field_tag :to_value, value_format == 'between-dates' ? filter.values.second : '', id: "between-dates-to-value-#{filter.name}", class: 'advanced-filters--text-field -slim', size: '10' %>
<%= calendar_for("between-dates-to-value-#{filter.name}") %>
</div>
</div>
<% else %>
<%# All other simple types %>
<div class="advanced-filters--filter-value <%= value_visibility %>">
<% if filter.type == :string %>
<div>
<%= text_field_tag :value, filter.values.first, class: 'advanced-filters--text-field -slim' %>
</div>
<% elsif filter.type == :text %>
<div>
<%= text_field_tag :value, filter.values.first, class: 'advanced-filters--text-field -slim' %>
</div>
<% elsif filter.type == :integer %>
<div>
<%= number_field_tag :value, filter.values.first, class: 'advanced-filters--text-field -slim', step: 'any' %>
</div>
<% elsif filter.type == :float %>
<div>
<%= number_field_tag :value, filter.values.first, class: 'advanced-filters--text-field -slim', step: 'any' %>
</div>
<% end %>
</div>
<% end %>
<% unless filter.name == :status %>
<div class="advanced-filters--remove-filter">
<a href="#" class="filter_rem">
<op-icon icon-classes="icon-close advanced-filters--remove-filter-icon" icon-title="{{I18n.t('js.button_delete')}}"></op-icon>
</a>
</div>
<% end %>
</li>
<% end %>
<li class="advanced-filters--spacer"></li>
<li class="advanced-filters--add-filter">
<!-- Add filters -->
<label for="add_filter_select" aria-hidden="true" class="advanced-filters--add-filter-label ng-binding">
<op-icon icon-classes="icon-add icon4" class="op-icon--wrapper">
<i class="icon-add icon4" aria-hidden="true"></i>
</op-icon>
Add filter:
</label>
<label for="add_filter_select" class="hidden-for-sighted ng-binding">
Add filter
Open this filter with 'ALT' and arrow keys.
To select an entry leave the focus for example by pressing enter. To leave without filter select the first (empty) entry.
</label>
<div class="advanced-filters--add-filter-value">
<%= select_tag "add_filter_select",
options_from_collection_for_select(
allowed_filters(@query),
:name,
:human_name,
disabled: @query.filters.map(&:name)
),
prompt: t(:actionview_instancetag_blank_option),
class: 'advanced-filters--select',
focus: "false",
'aria-invalid': "false" %>
</div>
</li>
<li class="advanced-filters--controls">
<%= submit_tag t('button_filter'), class: 'button -highlight', name: nil %>
</li>
</ul>
</fieldset>
<% end %>
<% if @projects.any? %>
<div class="generic-table--container">
<div class="generic-table--results-container">
<table class="generic-table" id="project-table">
<thead>
<tr>
<%= sort_header_tag('name', caption: Project.model_name.human) %>
<%= sort_header_tag('is_public', caption:Project.human_attribute_name(:is_public)) %>
<% if EnterpriseToken.allows_to?(:custom_fields_in_projects_list) %>
<% @custom_fields.each do |custom_field| %>
<th>
<div class="generic-table--header-outer">
<div class="generic-table--header">
<%= custom_field.name %>
</div>
</div>
</th>
<% end %>
<% end %>
<% if User.current.admin? %><%= sort_header_tag('required_disk_space', caption: I18n.t(:label_required_disk_storage)) %><% end %>
<% if User.current.admin? %><%= sort_header_tag('created_on', caption: Project.human_attribute_name(:created_on)) %><% end %>
<% if User.current.admin? %><%= sort_header_tag('latest_activity_at', caption: Project.human_attribute_name(:latest_activity_at)) %><% end %>
<th class="-right">
<div class="generic-table--header-outer">
<div class="generic-table--header">
<% if params[:expand] == 'all' %>
<%= link_to t(:button_collapse_all), {params: request.query_parameters.except(:expand)} %>
<% else %>
<%= link_to t(:button_expand_all), {params: request.query_parameters.merge(expand: 'all')} %>
<% end %>
</div>
</div>
</th>
</tr>
</thead>
<tbody>
<% project_tree_when_sorted(@projects) do |project, level| %>
<tr class="basics <%= project.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %> <%= params[:expand] == 'all' && project.short_description.present? ? '-no-highlighting -expanded' : '' %>">
<td class="name project--hierarchy <%= project.archived? ? 'archived' : '' %>">
<% if project.archived? %>
<span class="archived-label"><%= t('project.archive.archived') %></span>
<% end %>
<%= link_to_project(project, {}, {}, false) %>
</td>
<td><%= checked_image project.is_public? %></td>
<% if EnterpriseToken.allows_to?(:custom_fields_in_projects_list) %>
<% @custom_fields.each do |custom_field| %>
<td>
<% if project.custom_value_for(custom_field).present? %>
<% if custom_field.field_format == 'string' %>
<%= project.custom_value_for(custom_field).value.to_s %>
<% elsif custom_field.field_format == 'text' %>
<%= project.custom_value_for(custom_field).try(:value).try(:gsub, /\A(.{20}[^\n\r]*).*\z/m, '\1...').try(:strip) %>
<% elsif custom_field.field_format == 'bool' %>
<%= project.custom_value_for(custom_field).formatted_value %>
<% elsif custom_field.field_format == 'list' %>
<%= project.custom_value_for(custom_field).formatted_value %>
<% elsif custom_field.field_format == 'user' %>
<%= project.custom_value_for(custom_field).formatted_value %>
<% elsif custom_field.field_format == 'int' %>
<%= project.custom_value_for(custom_field).value.to_s %>
<% elsif custom_field.field_format == 'float' %>
<%= project.custom_value_for(custom_field).value.to_s %>
<% end %>
<% end %>
</td>
<% end %>
<% end %>
<% if User.current.admin? %><td><%= number_to_human_size(project.required_disk_space, precision: 2) if project.required_disk_space.to_i > 0 %></td><% end %>
<% if User.current.admin? %><td><%= format_date(project.created_on) %></td><% end %>
<% if User.current.admin? %><td><%= format_date(project.latest_activity_at) %></td><% end %>
<td class="buttons">
<% if more_menu_allowed?(project) %>
<!-- toolbar and legacy-actions-more are only for styling purposes.
Because of multiple diverging implementations of dropdowns the normal dropdown classes cannot be used any more without making special rules -->
<ul class="toolbar project-actions">
<li aria-haspopup="true" title="<%= t(:label_more) %>" class="drop-down">
7 years ago
<a class="icon icon-show-more-horizontal" title="<%= t(:label_more) %>" href="#"></a>
<ul style="display:none;" class="legacy-actions-more dropdown-menu">
<%= li_unless_nil(link_to(t(:label_subproject_new),
new_project_path(parent_id: project),
{ class: 'icon-context icon-add',
title: t(:label_subproject_new)})) if User.current.allowed_to?(:add_subprojects, project) %>
<%= li_unless_nil(link_to(t(:label_project_settings), {action: 'settings', id: project}, class: 'icon-context icon-settings') ) %>
<%= li_unless_nil(link_to(t(:button_archive),
archive_project_path(project, status: params[:status]),
data: { confirm: t('project.archive.are_you_sure', name: project.name) },
method: :put,
class: 'icon-context icon-locked') ) if project.active? %>
<%= li_unless_nil(link_to(t(:button_unarchive),
unarchive_project_path(project, status: params[:status]),
method: :put,
class: 'icon-context icon-unlocked') ) if !project.active? && (project.parent.nil? || project.parent.active?)%>
<%= li_unless_nil(link_to(t(:button_copy), copy_from_project_path(project, :admin), class: 'icon-context icon-copy') ) %>
<%= li_unless_nil(link_to(t(:button_delete), confirm_destroy_project_path(project), class: 'icon-context icon-delete') ) %>
</ul>
</li>
</ul>
<% end %>
<% unless project.short_description.empty? %>
<a class="icon collapse icon-arrow-up1" title="<%= t('label_project_hide_details') %>" onclick="toggleDescription(this);"></a>
<a class="icon expand icon-arrow-down1" title="<%= t('label_project_show_details') %>" onclick="toggleDescription(this);"></a>
<% end %>
</td>
</tr>
<% unless project.short_description.empty? %>
<tr class="project-description <%= project.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %> <%= params[:expand] == 'all' ? '-expanded' : '' %>">
<td colspan="<%= @custom_fields.size + (User.current.admin? ? 6 : 3) %>" class="project--hierarchy">
<div class="description-container">
<span class="wiki"><%= format_text(project.short_description, project: project) %></span>
</div>
</td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
<%= pagination_links_full @projects %>
</div>
</div>
<% else %>
<%= no_results_box(action_url: new_project_path, display_action: true) %>
<% end %>
11 years ago
<% if User.current.admin? %>
<p class="information-section">
<%= op_icon('icon-info1') %>
<%= t(:label_projects_storage_information,
count: Project.count,
storage: number_to_human_size(Project.total_projects_size, precision: 2)) %>
</p>
<% end %>