Add basic checkout instructions

This commit adds very basic checkout instructions similar
to `openproject-checkout` to repositories.

Issues:

* Uses table style for checkout component, as exact styling is unclear
  * Adds compressed table style with less padding

* Checkout settings are best organized in a Hash with keys per SCM
* vendor. Using default settings, a lot of new setting values would be
  introduced
  * Checks for Hash in SettingsController
  * Currently can't use SettingsHelper, as it requires single setting
    values
This commit is contained in:
Oliver Günther
2015-08-19 09:08:26 +02:00
parent f3564b98af
commit dfb8ac4853
19 changed files with 383 additions and 6 deletions
+14 -2
View File
@@ -36,7 +36,7 @@ See doc/COPYRIGHT.rdoc for more details.
/** Sync SCM vendor select when enabled SCMs are changed */
$('[name="settings[enabled_scm][]"]').change(function () {
$('[name="settings[enabled_scm][]"]').change(function() {
var wasDisabled = !this.checked,
vendor = this.value,
select = $('#settings_repositories_automatic_managed_vendor'),
@@ -51,7 +51,19 @@ See doc/COPYRIGHT.rdoc for more details.
if (wasDisabled && option.prop('selected')) {
select.val('');
}
});
/** Toggle repository checkout fieldsets required when option is disabled */
$('.settings-repositories--checkout-toggle').change(function() {
var wasChecked = this.checked,
fieldset = $(this).closest('fieldset');
fieldset
.find('input,select,textarea')
.filter(':not([type=checkbox])')
.filter(':not([type=hidden])')
.removeAttr('required') // Rails 4.0 still seems to use attribute
.prop('required', wasChecked);
})
});
}(jQuery));
+19
View File
@@ -38,6 +38,25 @@
</form>
```
## Forms: Bordered compressed style (less padding)
```
<form class="form -bordered -compressed">
<div class="form--field -required">
<label class="form--label">Text:</label>
<div class="form--field-container">
<div class="form--text-field-container">
<input type="text" class="form--text-field">
</div>
</div>
</div>
<hr class="form--separator">
<button class="button -highlight">Save</button>
<button class="button">Cancel</button>
</form>
```
## Forms: Standard layout
```
@@ -58,6 +58,9 @@ $form--field-types: (text-field, text-area, select, check-box, radio-button, ran
background-color: $content-form-bg-color
border: $content-form-border
&.-compressed
padding: 10px 20px 0 20px
.form--separator
border: 0
border-bottom: 1px solid $content-form-separator-color
@@ -328,6 +331,9 @@ fieldset.form--fieldset
@extend %form--field-after-container
font-style: italic
&.-no-italic
font-style: normal
.form--field-extra-actions
@extend %form--field-after-container
@include grid-visible-overflow
@@ -308,6 +308,11 @@ class RepositoriesController < ApplicationController
@repository = @project.repository
(render_404; return false) unless @repository
# Prepare checkout instructions
# available on all pages (even empty!)
@instructions = ::Scm::CheckoutInstructionsService.new(@repository)
# Asserts repository availability, or renders an appropriate error
@repository.scm.check_availability!
@path = params[:path] || ''
+2
View File
@@ -48,6 +48,8 @@ class SettingsController < ApplicationController
if value.is_a?(Array)
# remove blank values in array settings
value.delete_if(&:blank?)
elsif value.is_a?(Hash)
value.delete_if { |_, v| v.blank? }
else
value = value.strip
end
+4
View File
@@ -129,6 +129,10 @@ class Repository < ActiveRecord::Base
false
end
def supports_checkout_info?
true
end
def entry(path = nil, identifier = nil)
scm.entry(path, identifier)
end
@@ -0,0 +1,142 @@
#-- encoding: UTF-8
#-- 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.
#++
##
# Implements a repository service for building checkout instructions if supported
class Scm::CheckoutInstructionsService
attr_reader :repository, :user
def initialize(repository, user = User.current)
@repository = repository
@user = user
end
##
# Retrieve the checkout URL using the repository vendor information
# It may additionally set a path parameter, if the repository supports subtree checkout
def checkout_url(path = nil)
repository.scm.checkout_url(repository, checkout_base_url, path)
end
##
# Returns the checkout command from SCM adapter
# (e.g., `git clone`)
def checkout_command
repository.scm.checkout_command
end
##
# Returns the checkout base URL as defined in settings.
def checkout_base_url
checkout_settings['base_url']
end
##
# Returns the instructions defined in the settings.
def instructions
checkout_settings['text']
end
##
# Returns true when the checkout URL may target a subtree of the repository.
def subtree_checkout?
repository.scm.subtree_checkout?
end
##
# Returns whether the repository supports showing checkout information
# and has been configured for it.
def available?
checkout_enabled? &&
repository.supports_checkout_info? &&
checkout_base_url.present?
end
def checkout_enabled?
checkout_settings['enabled'].to_i > 0
end
##
# Determines whether permissions for the given repository
# are available.
def permission?
repository.managed?
end
##
# Returns one of the following permission symbols for the given user
#
# - :readwrite: When user is allowed to read and commit (:commit_access)
# - :read: When user is allowed to checkout the repository, but not commit (:browse_repository)
# - :none: Otherwise
#
# Note that this information is only applicable when the repository is managed,
# because otherwise OpenProject does not control the repository permissions.
# Use +permission?+ to check whether this is the case.
#
def permission
project = repository.project
if user.allowed_to?(:commit_access, project)
:readwrite
elsif user.allowed_to?(:browse_repository, project)
:read
else
:none
end
end
##
# Returns whether the given user may checkout the repository
#
# Note that this information is only applicable when the repository is managed,
# because otherwise OpenProject does not control the repository permissions.
# Use +permission?+ to check whether this is the case.
def may_checkout?
[:readwrite, :read].include?(permission)
end
##
# Returns whether the given user may commit to the repository
#
# Note that this information is only applicable when the repository is managed,
# because otherwise OpenProject does not control the repository permissions.
# Use +permission?+ to check whether this is the case.
def may_commit?
permission == :readwrite
end
private
def checkout_settings
@settings ||= begin
hash = Setting.repository_checkout_data[repository.vendor.to_s]
hash.presence || {}
end
end
end
@@ -0,0 +1,29 @@
<% if instructions.present? && instructions.available? %>
<form class="form -bordered -compressed">
<fieldset class="form--fieldset -collapsible">
<legend class="form--fieldset-legend" title="<%= l('repositories.checkout.instructions') %>"
onclick="toggleFieldset(this);"><%= l('repositories.checkout.instructions') %></legend>
<div class="form--field">
<label class="form--label"><%= l('repositories.checkout.url') %></label>
<div class="form--field-container">
<div class="form--field-affix">
<%= instructions.checkout_command %>
</div>
<div class="form--text-field-container -xwide">
<input type="text" class="form--text-field"
onclick="this.focus(); this.select()"
readonly="readonly" value="<%= instructions.checkout_url %>">
</div>
</div>
<div class="form--field-instructions -no-italic">
<%= simple_format instructions.instructions %>
<% if instructions.permission? %>
<p>
<span><%= l('repositories.checkout.access_permission') %></span>
<strong><%= l("repositories.checkout.access.#{instructions.permission}") %></strong>
<% end %>
</div>
</div>
</fieldset>
</form>
<% end %>
+1
View File
@@ -33,6 +33,7 @@ See doc/COPYRIGHT.rdoc for more details.
message: l('repositories.errors.empty_repository')) %></p>
</div>
</div>
<%= render partial: 'checkout_instructions', locals: { repository: @repository, instructions: @instructions } %>
<%= call_hook(:view_repositories_show_contextual,
{ repository: @repository, project: @project }) %>
<%= call_hook(:repositories_below_empty_warning,
+1
View File
@@ -27,6 +27,7 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<%= render partial: 'checkout_instructions', locals: { repository: @repository, instructions: @instructions } %>
<%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
+3 -1
View File
@@ -87,7 +87,9 @@ See doc/COPYRIGHT.rdoc for more details.
<div class="form--field"><%= setting_text_field :repository_log_display_limit, :size => 6 %></div>
<div class="form--field"><%= setting_check_box :repository_authentication_caching_enabled %></div>
</section>
<fieldset class="form--fieldset"><legend class="form--fieldset-legend"><%= l(:text_work_packages_ref_in_commit_messages) %></legend>
<%= render partial: 'repositories_checkout' %>
<fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%= l(:text_work_packages_ref_in_commit_messages) %></legend>
<div class="form--field">
<%= setting_text_field :commit_ref_keywords, :size => 30 %>
<div class="form--field-instructions"><%= l(:text_comma_separated) %></div>
@@ -0,0 +1,36 @@
<fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%= l('repositories.checkout.instructions') %></legend>
<% OpenProject::Scm::Manager.registered.each do |vendor, klass| %>
<% vendor = vendor.to_s %>
<% setting_base = "settings[repository_checkout_data][#{vendor}]" %>
<% enabled = Setting.repository_checkout_data[vendor]['enabled'].to_i > 0 %>
<fieldset class="form--fieldset -collapsible collapsed">
<legend class="form--fieldset-legend" title="<%= klass.vendor_name %>" onclick="toggleFieldset(this);">
<a href="javascript:"><%= klass.vendor_name %>
<span class="fieldset-toggle-state-label hidden-for-sighted">expanded</span>
</a>
</legend>
<div class="form--field">
<% key = "#{setting_base}[enabled]" %>
<input type="hidden" name="<%= key %>" value="0" id="<% "#{key}_hidden" %>" />
<%= setting_label key, label: :setting_repository_checkout_display %>
<%= styled_check_box_tag key, 1, enabled, class: 'settings-repositories--checkout-toggle' %>
<div class="form--field-instructions"><%= l('repositories.checkout.enable_instructions_text') %></div>
</div>
<div class="form--field">
<% key = "#{setting_base}[base_url]" %>
<%= setting_label key, label: :setting_repository_checkout_base_url %>
<%= styled_text_field_tag key, Setting.repository_checkout_data[vendor]['base_url'],
type: 'url', required: enabled %>
<div class="form--field-instructions"><%= simple_format l('repositories.checkout.base_url_text') %></div>
</div>
<div class="form--field">
<% key = "#{setting_base}[text]" %>
<%= setting_label key, label: :setting_repository_checkout_text %>
<%= styled_text_area_tag key, Setting.repository_checkout_data[vendor]['text'],
required: enabled %>
<div class="form--field-instructions"><%= l('repositories.checkout.text_instructions') %></div>
</div>
</fieldset>
<% end %>
</fieldset>
+15 -1
View File
@@ -1286,7 +1286,18 @@ en:
repositories:
autofetch_information: "Check this if you want repositories to be updated automatically when accessing the repository module page.\nThis encompasses the retrieval of commits from the repository and refreshing the required disk storage."
checkout_instructions: "Checkout instructions"
checkout:
access:
readwrite: 'Read + Write'
read: 'Read-only'
none: 'No checkout access, you may only view the repository through this application.'
access_permission: 'Your permissions on this repository:'
url: "Checkout URL"
base_url_text: "The base URL to use for generating checkout URLs (e.g., https://myserver.example.org/repos/).\nNote: The base URL is only used for rewriting checkout URLs in managed repositories. Other repositories are not altered"
enable_instructions_text: "Displays checkout instructions defined below on all repository-related pages."
instructions: "Checkout instructions"
text_instructions: "This text is displayed alongside the checkout URL for guidance on how to check out the repository."
not_available: "Checkout instructions are not available for this repository"
create_managed_delay: "Please note: The repository is managed, it is created asynchronously on the disk and will be available shortly."
create_successful: "The repository has been registered."
delete_sucessful: "The repository has been deleted."
@@ -1413,6 +1424,9 @@ en:
setting_repositories_encodings: "Repositories encodings"
setting_repository_authentication_caching_enabled: "Enable caching for authentication request of version control software"
setting_repository_storage_cache_minutes: "Repository disk size cache"
setting_repository_checkout_display: "Show checkout instructions"
setting_repository_checkout_base_url: "Checkout base URL"
setting_repository_checkout_text: "Checkout instruction text"
setting_repository_log_display_limit: "Maximum number of revisions displayed on file log"
setting_rest_api_enabled: "Enable REST web service"
setting_self_registration: "Self-registration"
+13
View File
@@ -280,6 +280,19 @@ journal_aggregation_time_minutes:
repository_storage_cache_minutes:
default: 720
format: int
repository_checkout_data:
serialized: true
default:
git:
enabled: 0
text: |-
The data contained in this repository can be downloaded to your computer with Git using the checkout information below.
Please consult the documentation of Git if you need more information on the checkout procedure and available clients.
subversion:
enabled: 0
text: |-
The data contained in this repository can be downloaded to your computer with Subversion using the checkout information below.
Please consult the documentation of Subversion if you need more information on the checkout procedure and available clients.
api_max_page_size:
format: int
default: 500
+2
View File
@@ -33,6 +33,8 @@ module OpenProject
module Scm
module Adapters
class Base
include CheckoutInstructions
attr_accessor :url, :root_url
def self.vendor
@@ -0,0 +1,79 @@
#-- encoding: UTF-8
#-- 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.
#++
module OpenProject
module Scm
module Adapters
module CheckoutInstructions
##
# Returns the checkout URL for the given repository
# based on this adapter's knowledge
def checkout_url(repository, base_url, path)
checkout_url = if local?
::URI.join(with_trailing_slash(base_url),
repository.repository_identifier)
else
url
end
if subtree_checkout? && path.present?
::URI.join(with_trailing_slash(checkout_url), path)
else
checkout_url
end
end
##
# Returns whether the SCM vendor supports subtree checkout
def subtree_checkout?
false
end
##
# Returns the checkout command for this vendor
def checkout_command
raise NotImplementedError
end
private
##
# Ensure URL has a trailing slash.
# Needed for base URL, because URI.join will otherwise
# assume a relative resource.
def with_trailing_slash(url)
url = url.to_s
url << '/' unless url.end_with?('/')
url
end
end
end
end
end
+4
View File
@@ -43,6 +43,10 @@ module OpenProject
@path_encoding = path_encoding.presence || 'UTF-8'
end
def checkout_command
'git clone'
end
def client_command
@client_command ||= self.class.config[:client_command] || 'git'
end
@@ -80,6 +80,14 @@ module OpenProject
@password = password
end
def checkout_command
'svn checkout'
end
def subtree_checkout?
true
end
##
# Checks the status of this repository and throws unless it can be accessed
# correctly by the adapter.
@@ -100,8 +100,6 @@ module OpenProject
"file://#{managed_repository_path}"
end
protected
##
# Repository relative path from scm managed root.
# Will be overridden by including models to, e.g.,