changeset 318:f7c525dc7585 luisf

Merge from branch "live"
author luisf <luis.figueira@eecs.qmul.ac.uk>
date Mon, 28 Mar 2011 18:17:06 +0100
parents e941e6aa2b7c (current diff) 2c9d65eff252 (diff)
children 0b6c82dead28
files config/locales/en-GB.yml
diffstat 20 files changed, 990 insertions(+), 1053 deletions(-) [+]
line wrap: on
line diff
--- a/app/controllers/application_controller.rb	Thu Mar 24 13:59:03 2011 +0000
+++ b/app/controllers/application_controller.rb	Mon Mar 28 18:17:06 2011 +0100
@@ -263,6 +263,12 @@
         uri = URI.parse(back_url)
         # do not redirect user to another host or to the login or register page
         if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
+          # soundsoftware: if login page is https but back_url http,
+          # switch back_url to https to ensure cookie validity (#83)
+          if (uri.scheme == "http") && (URI.parse(request.url).scheme == "https")
+            uri.scheme = "https"
+            back_url = uri.to_s
+          end
           redirect_to(back_url)
           return
         end
--- a/app/controllers/attachments_controller.rb	Thu Mar 24 13:59:03 2011 +0000
+++ b/app/controllers/attachments_controller.rb	Mon Mar 28 18:17:06 2011 +0100
@@ -16,9 +16,11 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class AttachmentsController < ApplicationController
+
   before_filter :find_project
   before_filter :file_readable, :read_authorize, :except => :destroy
   before_filter :delete_authorize, :only => :destroy
+  before_filter :active_authorize, :only => :toggle_active
   
   verify :method => :post, :only => :destroy
   
@@ -54,6 +56,12 @@
     redirect_to :controller => 'projects', :action => 'show', :id => @project
   end
   
+  def toggle_active
+    @attachment.active = !@attachment.active?
+    @attachment.save!
+    render :layout => false
+  end
+
 private
   def find_project
     @attachment = Attachment.find(params[:id])
@@ -77,6 +85,10 @@
     @attachment.deletable? ? true : deny_access
   end
   
+  def active_authorize
+    true
+  end
+
   def detect_content_type(attachment)
     content_type = attachment.content_type
     if content_type.blank?
--- a/app/controllers/files_controller.rb	Thu Mar 24 13:59:03 2011 +0000
+++ b/app/controllers/files_controller.rb	Mon Mar 28 18:17:06 2011 +0100
@@ -8,8 +8,9 @@
   include SortHelper
 
   def index
-    sort_init 'filename', 'asc'
+    sort_init 'active', 'asc'
     sort_update 'filename' => "#{Attachment.table_name}.filename",
+		'active' => "#{Attachment.table_name}.active",
                 'created_on' => "#{Attachment.table_name}.created_on",
                 'size' => "#{Attachment.table_name}.filesize",
                 'downloads' => "#{Attachment.table_name}.downloads"
@@ -33,4 +34,5 @@
     end
     redirect_to project_files_path(@project)
   end
+
 end
--- a/app/controllers/sys_controller.rb	Thu Mar 24 13:59:03 2011 +0000
+++ b/app/controllers/sys_controller.rb	Mon Mar 28 18:17:06 2011 +0100
@@ -55,6 +55,20 @@
     render :nothing => true, :status => 404
   end
 
+  def get_external_repo_url
+    project = Project.find(params[:id])
+    if project.repository
+      repo = project.repository
+      if repo.is_external?
+        render :text => repo.external_url, :status => 200
+      else
+        render :nothing => true, :status => 200
+      end
+    end
+  rescue ActiveRecord::RecordNotFound
+    render :nothing => true, :status => 404
+  end
+
   def set_embedded_active
     project = Project.find(params[:id])
     mods = project.enabled_modules
--- a/app/helpers/repositories_helper.rb	Thu Mar 24 13:59:03 2011 +0000
+++ b/app/helpers/repositories_helper.rb	Mon Mar 28 18:17:06 2011 +0100
@@ -145,12 +145,6 @@
     send(method, form, repository) if repository.is_a?(Repository) && respond_to?(method) && method != 'repository_field_tags'
   end
   
-  
-  def ssamr_scm_update(repository)
-    check_box_tag('repository_scm', value = "1", checked = false, onchange => remote_function(:url => { :controller => 'repositories', :action => 'ssamr_edit', :id => @project }, :method => :get, :with => "Form.serialize(this.form)"))
-  
-  end
-  
   def scm_select_tag(repository)
     scm_options = [["--- #{l(:actionview_instancetag_blank_option)} ---", '']]
     Redmine::Scm::Base.all.each do |scm|
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/attachments/toggle_active.rhtml	Mon Mar 28 18:17:06 2011 +0100
@@ -0,0 +1,7 @@
+<%=
+file = Attachment.find(params[:id])
+active_id = "active-" + file.id.to_s
+link_to_remote image_tag(file.active? ? 'fav.png' : 'fav_off.png'),
+  :url => {:controller => 'attachments', :action => 'toggle_active', :project_id => @project.id, :id => file},
+  :update => active_id
+%>
--- a/app/views/files/index.html.erb	Thu Mar 24 13:59:03 2011 +0000
+++ b/app/views/files/index.html.erb	Mon Mar 28 18:17:06 2011 +0100
@@ -5,29 +5,51 @@
 <h2><%=l(:label_attachment_plural)%></h2>
 
 <% delete_allowed = User.current.allowed_to?(:manage_files, @project) %>
+<% active_change_allowed = delete_allowed %>
 
 <table class="list files">
   <thead><tr>
+    <%= sort_header_tag('active', :caption => l(:field_active)) %>
     <%= sort_header_tag('filename', :caption => l(:field_filename)) %>
     <%= sort_header_tag('created_on', :caption => l(:label_date), :default_order => 'desc') %>
     <%= sort_header_tag('size', :caption => l(:field_filesize), :default_order => 'desc') %>
-    <%= sort_header_tag('downloads', :caption => l(:label_downloads_abbr), :default_order => 'desc') %>
+    <%= sort_header_tag('downloads', :caption => l(:field_downloads), :default_order => 'desc') %>
     <th>MD5</th>
     <th></th>
   </tr></thead>
   <tbody>
+<% have_file = false %>
 <% @containers.each do |container| %>	
   <% next if container.attachments.empty? -%>
 	<% if container.is_a?(Version) -%>
   <tr>
-  	<th colspan="6" align="left">
+  	<th colspan="7" align="left">
   		<%= link_to(h(container), {:controller => 'versions', :action => 'show', :id => container}, :class => "icon icon-package") %>
 		</th>
 	</tr>
 	<% end -%>
   <% container.attachments.each do |file| %>		
-  <tr class="file <%= cycle("odd", "even") %>">
-    <td class="filename"><%= link_to_attachment file, :download => true, :title => file.description %></td>
+  <tr class="file <%= cycle("odd", "even") %> <%= "active" if file.active? %>">
+    <td class="active">
+      <% have_file = true %>
+      <% if active_change_allowed
+           active_id = "active-" + file.id.to_s -%>
+        <div id="<%= active_id %>">
+        <%= link_to_remote image_tag(file.active? ? 'fav.png' : 'fav_off.png'),
+              :url => {:controller => 'attachments', :action => 'toggle_active', :project_id => @project.id, :id => file},
+              :update => active_id
+        %>
+        </div>
+      <% else -%>
+        <%= image_tag('fav.png') if file.active? %>
+      <% end -%>
+    </td>
+    <% if file.active? %>
+      <td class="filename active"><%= link_to_attachment file, :download => true %><br><span class="description"><%= h(file.description) %></span></td>
+    <% else %>
+      <td class="filename"><%= link_to_attachment file, :download => true, :title => file.description %>
+    <% end %>
+    </td>
     <td class="created_on"><%= format_time(file.created_on) %></td>
     <td class="filesize"><%= number_to_human_size(file.filesize) %></td>
     <td class="downloads"><%= file.downloads %></td>
@@ -43,4 +65,6 @@
   </tbody>
 </table>
 
+<%= l(:text_files_active_change) if active_change_allowed and have_file %>
+
 <% html_title(l(:label_attachment_plural)) -%>
--- a/app/views/projects/settings/_repository.rhtml	Thu Mar 24 13:59:03 2011 +0000
+++ b/app/views/projects/settings/_repository.rhtml	Mon Mar 28 18:17:06 2011 +0100
@@ -10,17 +10,16 @@
 
 <div class="box tabular">
 
+<p>
+<% if @repository %>
+   <%= l(:text_settings_repo_explanation) %></ br>
+   <% if @repository.is_external %>
+     <p><%= l(:text_settings_repo_is_external) %></ br>
+   <% else %>
+     <p><%= l(:text_settings_repo_is_internal) %></ br>
+   <% end %>
+</p>
 
-<% if @repository %>
-   <% if @repository.is_external %>
-   <%= l(:text_settings_repo_is_external) %></ br>
-   <% else %>
-   <%= l(:text_settings_repo_is_internal) %></ br>
-<% end %>
-   <% else %>
-	<%= l(:text_settings_repo_creation) %></ br>
-<% end %>
-  
 
 
 
@@ -28,16 +27,17 @@
 <p>
 	<%= label_tag('repository_is_external', l(:label_is_external_repository)) %>
 	<%= check_box :repository, :is_external, :onclick => "toggle_ext_url()" %> 
-	<%= l(:setting_external_repository) %>
+	<br/><em><%= l(:setting_external_repository) %></em>
 </p>
 
 
 <p>
 	<%= label_tag('repository_external_url', l(:label_repository_external_url)) %>
-	<%= text_field :repository, :external_url, :disabled => true %> 
-	<%= l(:setting_external_repository_url) %>
+	<%= text_field :repository, :external_url, :disabled => !(@repository and @repository.is_external) %> 
+	<br/><em><%= l(:setting_external_repository_url) %></em>
 </p>
 
+<p><%= l(:text_settings_repo_need_help) %></p>
 
 </div>
 
@@ -48,4 +48,9 @@
 </div>
 
 <%= submit_tag(l(:button_save), :onclick => remote_function(:url => { :controller => 'repositories', :action => 'edit', :id => @project }, :method => :get, :with => "Form.serialize(this.form)")) %>
+
+<% else %>
+   <%= l(:text_settings_repo_creation) %></ br>
 <% end %>
+
+<% end %>
--- a/config/locales/en-GB.yml	Thu Mar 24 13:59:03 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,978 +0,0 @@
-en-GB:
-  direction: ltr
-  date:
-    formats:
-      # Use the strftime parameters for formats.
-      # When no format has been given, it uses default.
-      # You can provide other formats here if you like!
-      default: "%d/%m/%Y"
-      short: "%d %b"
-      long: "%d %B, %Y"
-      
-    day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
-    abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
-      
-    # Don't forget the nil at the beginning; there's no such thing as a 0th month
-    month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
-    abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
-    # Used in date_select and datime_select.
-    order: [ :year, :month, :day ]
-
-  time:
-    formats:
-      default: "%d/%m/%Y %I:%M %p"
-      time: "%I:%M %p"
-      short: "%d %b %H:%M"
-      long: "%d %B, %Y %H:%M"
-    am: "am"
-    pm: "pm"
-      
-  datetime:
-    distance_in_words:
-      half_a_minute: "half a minute"
-      less_than_x_seconds:
-        one:   "less than 1 second"
-        other: "less than {{count}} seconds"
-      x_seconds:
-        one:   "1 second"
-        other: "{{count}} seconds"
-      less_than_x_minutes:
-        one:   "less than a minute"
-        other: "less than {{count}} minutes"
-      x_minutes:
-        one:   "1 minute"
-        other: "{{count}} minutes"
-      about_x_hours:
-        one:   "about 1 hour"
-        other: "about {{count}} hours"
-      x_days:
-        one:   "1 day"
-        other: "{{count}} days"
-      about_x_months:
-        one:   "about 1 month"
-        other: "about {{count}} months"
-      x_months:
-        one:   "1 month"
-        other: "{{count}} months"
-      about_x_years:
-        one:   "about 1 year"
-        other: "about {{count}} years"
-      over_x_years:
-        one:   "over 1 year"
-        other: "over {{count}} years"
-      almost_x_years:
-        one:   "almost 1 year"
-        other: "almost {{count}} years"
-
-  number:
-    format:
-      separator: "."
-      delimiter: " "
-      precision: 3
-
-    currency:
-      format:
-        format: "%u%n"
-        unit: "£"
-    human:
-      format:
-        delimiter: ""
-        precision: 1
-      storage_units:
-        format: "%n %u"
-        units:
-          byte:
-            one: "Byte"
-            other: "Bytes"
-          kb: "kB"
-          mb: "MB"
-          gb: "GB"
-          tb: "TB"
-
-        
-# Used in array.to_sentence.
-  support:
-    array:
-      sentence_connector: "and"
-      skip_last_comma: false
-      
-  activerecord:
-    errors:
-      messages:
-        inclusion: "is not included in the list"
-        exclusion: "is reserved"
-        invalid: "is invalid"
-        confirmation: "doesn't match confirmation"
-        accepted: "must be accepted"
-        empty: "can't be empty"
-        blank: "can't be blank"
-        too_long: "is too long (maximum is {{count}} characters)"
-        too_short: "is too short (minimum is {{count}} characters)"
-        wrong_length: "is the wrong length (should be {{count}} characters)"
-        taken: "has already been taken"
-        not_a_number: "is not a number"
-        not_a_date: "is not a valid date"
-        greater_than: "must be greater than {{count}}"
-        greater_than_or_equal_to: "must be greater than or equal to {{count}}"
-        equal_to: "must be equal to {{count}}"
-        less_than: "must be less than {{count}}"
-        less_than_or_equal_to: "must be less than or equal to {{count}}"
-        odd: "must be odd"
-        even: "must be even"
-        greater_than_start_date: "must be greater than start date"
-        not_same_project: "doesn't belong to the same project"
-        circular_dependency: "This relation would create a circular dependency"
-        cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
-        must_accept_terms_and_conditions: "You must accept the Terms and Conditions"        
-
-  actionview_instancetag_blank_option: Please select
-  
-  general_text_No: 'No'
-  general_text_Yes: 'Yes'
-  general_text_no: 'no'
-  general_text_yes: 'yes'
-  general_lang_name: 'English (British)'
-  general_csv_separator: ','
-  general_csv_decimal_separator: '.'
-  general_csv_encoding: ISO-8859-1
-  general_pdf_encoding: ISO-8859-1
-  general_first_day_of_week: '1'
-  
-  notice_account_updated: Account was successfully updated.
-  notice_account_invalid_creditentials: Invalid user or password
-  notice_account_password_updated: Password was successfully updated.
-  notice_account_wrong_password: Wrong password
-  notice_account_register_done: Account was successfully created. To activate your account, click on the link that was emailed to you.
-  notice_account_unknown_email: Unknown user.
-  notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
-  notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
-  notice_account_activated: Your account has been activated. You can now log in.
-  notice_successful_create: Successful creation.
-  notice_successful_update: Successful update.
-  notice_successful_delete: Successful deletion.
-  notice_successful_connection: Successful connection.
-  notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
-  notice_locking_conflict: Data has been updated by another user.
-  notice_not_authorized: You are not authorised to access this page.
-  notice_email_sent: "An email was sent to {{value}}"
-  notice_email_error: "An error occurred while sending mail ({{value}})"
-  notice_feeds_access_key_reseted: Your RSS access key was reset.
-  notice_api_access_key_reseted: Your API access key was reset.
-  notice_failed_to_save_issues: "Failed to save {{count}} issue(s) on {{total}} selected: {{ids}}."
-  notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
-  notice_account_pending: "Your account was created and is now pending administrator approval."
-  notice_default_data_loaded: Default configuration successfully loaded.
-  notice_unable_delete_version: Unable to delete version.
-  notice_issue_done_ratios_updated: Issue done ratios updated.
-  
-  error_can_t_load_default_data: "Default configuration could not be loaded: {{value}}"
-  error_scm_not_found: "The entry or revision was not found in the repository."
-  error_scm_command_failed: "An error occurred when trying to access the repository: {{value}}"
-  error_scm_annotate: "The entry does not exist or can not be annotated."
-  error_issue_not_found_in_project: 'The issue was not found or does not belong to this project'
-  error_no_tracker_in_project: 'No tracker is associated to this project. Please check the Project settings.'
-  error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
-  error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version can not be reopened'
-  error_can_not_archive_project: This project can not be archived
-  error_issue_done_ratios_not_updated: "Issue done ratios not updated."
-  error_workflow_copy_source: 'Please select a source tracker or role'
-  error_workflow_copy_target: 'Please select target tracker(s) and role(s)'
-  
-  warning_attachments_not_saved: "{{count}} file(s) could not be saved."
-  
-  mail_subject_lost_password: "Your {{value}} password"
-  mail_body_lost_password: 'To change your password, click on the following link:'
-  mail_subject_register: "Your {{value}} account activation"
-  mail_body_register: 'To activate your account, click on the following link:'
-  mail_body_account_information_external: "You can use your {{value}} account to log in."
-  mail_body_account_information: Your account information
-  mail_subject_account_activation_request: "{{value}} account activation request"
-  mail_body_account_activation_request: "A new user ({{value}}) has registered. The account is pending your approval:"
-  mail_subject_reminder: "{{count}} issue(s) due in the next {{days}} days"
-  mail_body_reminder: "{{count}} issue(s) that are assigned to you are due in the next {{days}} days:"
-  mail_subject_wiki_content_added: "'{{id}}' wiki page has been added"
-  mail_body_wiki_content_added: "The '{{id}}' wiki page has been added by {{author}}."
-  mail_subject_wiki_content_updated: "'{{id}}' wiki page has been updated"
-  mail_body_wiki_content_updated: "The '{{id}}' wiki page has been updated by {{author}}."
-  
-  gui_validation_error: 1 error
-  gui_validation_error_plural: "{{count}} errors"
-
-  field_ssamr_user_detail:
-    description: User Description
-    institution: Institution
-
-  field_other_institution: ''
-
-  field_name: Name
-  field_description: Description
-  field_summary: Summary
-  field_is_required: Required
-  field_firstname: First name
-  field_lastname: Last name
-  field_mail: Email
-  field_filename: File
-  field_filesize: Size
-  field_downloads: Downloads
-  field_author: Author
-  field_created_on: Created
-  field_updated_on: Updated
-  field_field_format: Format
-  field_is_for_all: For all projects
-  field_possible_values: Possible values
-  field_regexp: Regular expression
-  field_min_length: Minimum length
-  field_max_length: Maximum length
-  field_value: Value
-  field_category: Category
-  field_title: Title
-  field_project: Project
-  field_issue: Issue
-  field_status: Status
-  field_notes: Notes
-  field_is_closed: Issue closed
-  field_is_default: Default value
-  field_tracker: Tracker
-  field_subject: Subject
-  field_due_date: Due date
-  field_assigned_to: Assignee
-  field_priority: Priority
-  field_fixed_version: Target version
-  field_user: User
-  field_role: Role
-  field_homepage: Homepage
-  field_is_public: Public
-  field_is_private: Private
-  field_parent: Subproject of
-  field_is_in_roadmap: Issues displayed in roadmap
-  field_login: Login
-  field_mail_notification: Email notifications
-  field_admin: Administrator
-  field_last_login_on: Last connection
-  field_language: Language
-  field_effective_date: Date
-  field_password: Password
-  field_new_password: New password
-  field_password_confirmation: Confirmation
-  field_version: Version
-  field_type: Type
-  field_host: Host
-  field_port: Port
-  field_account: Account
-  field_base_dn: Base DN
-  field_attr_login: Login attribute
-  field_attr_firstname: Firstname attribute
-  field_attr_lastname: Lastname attribute
-  field_attr_mail: Email attribute
-  field_onthefly: On-the-fly user creation
-  field_start_date: Start date
-  field_done_ratio: % Done
-  field_auth_source: Authentication mode
-  field_hide_mail: Hide my email address
-  field_comments: Comment
-  field_url: URL
-  field_start_page: Start page
-  field_subproject: Subproject
-  field_hours: Hours
-  field_activity: Activity
-  field_spent_on: Date
-  field_identifier: Identifier
-  field_is_filter: Used as a filter
-  field_issue_to: Related issue
-  field_delay: Delay
-  field_assignable: Issues can be assigned to this role
-  field_redirect_existing_links: Redirect existing links
-  field_estimated_hours: Estimated time
-  field_column_names: Columns
-  field_time_zone: Time zone
-  field_searchable: Searchable
-  field_default_value: Default value
-  field_comments_sorting: Display comments
-  field_parent_title: Parent page
-  field_editable: Editable
-  field_watcher: Watcher
-  field_identity_url: OpenID URL
-  field_content: Content
-  field_group_by: Group results by
-  field_sharing: Sharing
-  
-  setting_external_repository: "In the case you wish to follow an external repository"
-  setting_external_repository_url: "The external repository URL"
-  label_repository_external_url: "External rep URL"
-  setting_app_title: Application title
-  setting_app_subtitle: Application subtitle
-  setting_welcome_text: Welcome text
-  setting_tipoftheday_text: Tip of the Day
-  setting_notifications_text: Notifications
-  setting_default_language: Default language
-  setting_login_required: Authentication required
-  setting_self_registration: Self-registration
-  setting_attachment_max_size: Attachment max. size
-  setting_issues_export_limit: Issues export limit
-  setting_mail_from: Emission email address
-  setting_bcc_recipients: Blind carbon copy recipients (bcc)
-  setting_plain_text_mail: Plain text mail (no HTML)
-  setting_host_name: Host name and path
-  setting_text_formatting: Text formatting
-  setting_wiki_compression: Wiki history compression
-  setting_feeds_limit: Feed content limit
-  setting_default_projects_public: New projects are public by default
-  setting_autofetch_changesets: Autofetch commits
-  setting_sys_api_enabled: Enable WS for repository management
-  setting_commit_ref_keywords: Referencing keywords
-  setting_commit_fix_keywords: Fixing keywords
-  setting_autologin: Autologin
-  setting_date_format: Date format
-  setting_time_format: Time format
-  setting_cross_project_issue_relations: Allow cross-project issue relations
-  setting_issue_list_default_columns: Default columns displayed on the issue list
-  setting_repositories_encodings: Repositories encodings
-  setting_commit_logs_encoding: Commit messages encoding
-  setting_emails_footer: Emails footer
-  setting_protocol: Protocol
-  setting_per_page_options: Objects per page options
-  setting_user_format: Users display format
-  setting_activity_days_default: Days displayed on project activity
-  setting_display_subprojects_issues: Display subprojects issues on main projects by default
-  setting_enabled_scm: Enabled SCM
-  setting_mail_handler_body_delimiters: "Truncate emails after one of these lines"
-  setting_mail_handler_api_enabled: Enable WS for incoming emails
-  setting_mail_handler_api_key: API key
-  setting_sequential_project_identifiers: Generate sequential project identifiers
-  setting_gravatar_enabled: Use Gravatar user icons
-  setting_gravatar_default: Default Gravatar image
-  setting_diff_max_lines_displayed: Max number of diff lines displayed
-  setting_file_max_size_displayed: Max size of text files displayed inline
-  setting_repository_log_display_limit: Maximum number of revisions displayed on file log
-  setting_openid: Allow OpenID login and registration
-  setting_password_min_length: Minimum password length
-  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
-  setting_default_projects_modules: Default enabled modules for new projects
-  setting_issue_done_ratio: Calculate the issue done ratio with
-  setting_issue_done_ratio_issue_field: Use the issue field
-  setting_issue_done_ratio_issue_status: Use the issue status
-  setting_start_of_week: Start calendars on
-  setting_rest_api_enabled: Enable REST web service
-  setting_cache_formatted_text: Cache formatted text
-  
-  permission_add_project: Create project
-  permission_add_subprojects: Create subprojects
-  permission_edit_project: Edit project
-  permission_select_project_modules: Select project modules
-  permission_manage_members: Manage members
-  permission_manage_project_activities: Manage project activities
-  permission_manage_versions: Manage versions
-  permission_manage_categories: Manage issue categories
-  permission_view_issues: View Issues
-  permission_add_issues: Add issues
-  permission_edit_issues: Edit issues
-  permission_manage_issue_relations: Manage issue relations
-  permission_add_issue_notes: Add notes
-  permission_edit_issue_notes: Edit notes
-  permission_edit_own_issue_notes: Edit own notes
-  permission_move_issues: Move issues
-  permission_delete_issues: Delete issues
-  permission_manage_public_queries: Manage public queries
-  permission_save_queries: Save queries
-  permission_view_gantt: View gantt chart
-  permission_view_calendar: View calendar
-  permission_view_issue_watchers: View watchers list
-  permission_add_issue_watchers: Add watchers
-  permission_delete_issue_watchers: Delete watchers
-  permission_log_time: Log spent time
-  permission_view_time_entries: View spent time
-  permission_edit_time_entries: Edit time logs
-  permission_edit_own_time_entries: Edit own time logs
-  permission_manage_news: Manage news
-  permission_comment_news: Comment news
-  permission_manage_documents: Manage documents
-  permission_view_documents: View documents
-  permission_manage_files: Manage downloads
-  permission_view_files: View downloads
-  permission_manage_wiki: Manage wiki
-  permission_rename_wiki_pages: Rename wiki pages
-  permission_delete_wiki_pages: Delete wiki pages
-  permission_view_wiki_pages: View wiki
-  permission_view_wiki_edits: View wiki history
-  permission_edit_wiki_pages: Edit wiki pages
-  permission_delete_wiki_pages_attachments: Delete attachments
-  permission_protect_wiki_pages: Protect wiki pages
-  permission_manage_repository: Manage repository
-  permission_browse_repository: Browse repository
-  permission_view_changesets: View changesets
-  permission_commit_access: Commit access
-  permission_manage_boards: Manage boards
-  permission_view_messages: View messages
-  permission_add_messages: Post messages
-  permission_edit_messages: Edit messages
-  permission_edit_own_messages: Edit own messages
-  permission_delete_messages: Delete messages
-  permission_delete_own_messages: Delete own messages
-  permission_export_wiki_pages: Export wiki pages
-  
-  project_module_issue_tracking: Issue tracking (bugs and feature requests)
-  project_module_time_tracking: Time tracking
-  project_module_news: News
-  project_module_documents: Documents
-  project_module_files: Files for download
-  project_module_wiki: Wiki
-  project_module_repository: Source code repository
-  project_module_boards: Forums
-  project_module_gantt: Gantt chart
-  project_module_calendar: Calendar
-  project_module_embedded: Embedded documentation (Javadoc or Doxygen)
-  
-  label_tipoftheday: Tip of the day
-  label_notifications: Important Message
-  field_terms_and_conditions: 'Terms and Conditions:'
-  accept_terms_and_conditions: 'I have read and agree with the '
-  label_ssamr_description: Research description
-  label_ssamr_details: Other Details
-  label_ssamr_institution: Institution
-  label_user: User
-  label_user_plural: Users
-  label_user_new: New user
-  label_user_anonymous: Anonymous
-  label_project: Project
-  label_project_new: New project
-  label_project_plural: Projects
-  label_my_project_plural: My Projects
-  label_other_project_plural: Other Projects
-  label_x_projects:
-    zero:  no projects
-    one:   1 project
-    other: "{{count}} projects"
-  label_project_all: All Projects
-  label_project_latest: Latest projects
-  label_managers: Managed by
-  label_issue: Issue
-  label_issue_new: New issue
-  label_issue_plural: Issues
-  label_issue_view_all: View all issues
-  label_issues_by: "Issues by {{value}}"
-  label_issue_added: Issue added
-  label_issue_updated: Issue updated
-  label_document: Document
-  label_document_new: New document
-  label_document_plural: Documents
-  label_document_added: Document added
-  label_role: Role
-  label_role_plural: Roles
-  label_role_new: New role
-  label_role_and_permissions: Roles and permissions
-  label_member: Member
-  label_member_new: New member
-  label_member_plural: Members
-  label_tracker: Tracker
-  label_tracker_plural: Trackers
-  label_tracker_new: New tracker
-  label_workflow: Workflow
-  label_issue_status: Issue status
-  label_issue_status_plural: Issue statuses
-  label_issue_status_new: New status
-  label_issue_category: Issue category
-  label_issue_category_plural: Issue categories
-  label_issue_category_new: New category
-  label_custom_field: Custom field
-  label_custom_field_plural: Custom fields
-  label_custom_field_new: New custom field
-  label_enumerations: Enumerations
-  label_enumeration_new: New value
-  label_information: Information
-  label_information_plural: Information
-  label_please_login: Please log in
-  label_register: Register
-  label_login_with_open_id_option: or login with OpenID
-  label_password_lost: Lost password
-  label_home: Home
-  label_home_heading: Welcome!
-  label_my_page: My page
-  label_my_account: My account
-  label_my_projects: My projects
-  label_administration: Administration
-  label_login: Sign in
-  label_logout: Sign out
-  label_help: Help
-  label_reported_issues: Reported issues
-  label_assigned_to_me_issues: Issues assigned to me
-  label_last_login: Last connection
-  label_registered_on: Registered on
-  label_activity: Activity
-  label_overall_activity: Overall activity
-  label_user_activity: "{{value}}'s activity"
-  label_new: New
-  label_logged_as: Logged in as
-  label_environment: Environment
-  label_authentication: Authentication
-  label_auth_source: Authentication mode
-  label_auth_source_new: New authentication mode
-  label_auth_source_plural: Authentication modes
-  label_subproject_plural: Subprojects
-  label_subproject_new: New subproject
-  label_and_its_subprojects: "{{value}} and its subprojects"
-  label_min_max_length: Min - Max length
-  label_list: List
-  label_date: Date
-  label_integer: Integer
-  label_float: Float
-  label_boolean: Boolean
-  label_string: Text
-  label_text: Long text
-  label_attribute: Attribute
-  label_attribute_plural: Attributes
-  label_download: "{{count}} Download"
-  label_download_plural: "{{count}} Downloads"
-  label_no_data: No data to display
-  label_change_status: Change status
-  label_history: History
-  label_attachment: File
-  label_attachment_new: New file
-  label_attachment_delete: Delete file
-  label_attachment_plural: Downloads
-  label_file_added: File added
-  label_report: Report
-  label_report_plural: Reports
-  label_news: News
-  label_news_new: Add news
-  label_news_plural: News
-  label_news_latest: Latest news
-  label_news_view_all: View all news
-  label_news_added: News added
-  label_settings: Settings
-  label_overview: Overview
-  label_version: Version
-  label_version_new: New version
-  label_version_plural: Versions
-  label_close_versions: Close completed versions
-  label_confirmation: Confirmation
-  label_export_to: 'Also available in:'
-  label_read: Read...
-  label_public_projects: Public projects
-  label_open_issues: open
-  label_open_issues_plural: open
-  label_closed_issues: closed
-  label_closed_issues_plural: closed
-  label_x_open_issues_abbr_on_total:
-    zero:  0 open / {{total}}
-    one:   1 open / {{total}}
-    other: "{{count}} open / {{total}}"
-  label_x_open_issues_abbr:
-    zero:  0 open
-    one:   1 open
-    other: "{{count}} open"
-  label_x_closed_issues_abbr:
-    zero:  0 closed
-    one:   1 closed
-    other: "{{count}} closed"
-  label_total: Total
-  label_permissions: Permissions
-  label_current_status: Current status
-  label_new_statuses_allowed: New statuses allowed
-  label_all: all
-  label_none: none
-  label_nobody: nobody
-  label_next: Next
-  label_previous: Previous
-  label_used_by: Used by
-  label_details: Details
-  label_add_note: Add a note
-  label_per_page: Per page
-  label_calendar: Calendar
-  label_months_from: months from
-  label_gantt: Gantt
-  label_internal: Internal
-  label_last_changes: "last {{count}} changes"
-  label_change_view_all: View all changes
-  label_personalize_page: Personalise this page
-  label_comment: Comment
-  label_comment_plural: Comments
-  label_x_comments:
-    zero: no comments
-    one: 1 comment
-    other: "{{count}} comments"
-  label_comment_add: Add a comment
-  label_comment_added: Comment added
-  label_comment_delete: Delete comments
-  label_query: Custom query
-  label_query_plural: Custom queries
-  label_query_new: New query
-  label_filter_add: Add filter
-  label_filter_plural: Filters
-  label_equals: is
-  label_not_equals: is not
-  label_in_less_than: in less than
-  label_in_more_than: in more than
-  label_greater_or_equal: '>='
-  label_less_or_equal: '<='
-  label_in: in
-  label_today: today
-  label_all_time: all time
-  label_yesterday: yesterday
-  label_this_week: this week
-  label_last_week: last week
-  label_last_n_days: "last {{count}} days"
-  label_this_month: this month
-  label_last_month: last month
-  label_this_year: this year
-  label_date_range: Date range
-  label_less_than_ago: less than days ago
-  label_more_than_ago: more than days ago
-  label_ago: days ago
-  label_contains: contains
-  label_not_contains: doesn't contain
-  label_day_plural: days
-  label_repository: Repository
-  label_is_external_repository: External?
-  label_repository_plural: Repositories
-  label_browse: Browse
-  label_modification: "{{count}} change"
-  label_modification_plural: "{{count}} changes"
-  label_branch: Branch
-  label_tag: Tag 
-  label_revision: Revision
-  label_revision_plural: Revisions
-  label_revision_id: "Revision {{value}}"
-  label_associated_revisions: Associated revisions
-  label_added: added
-  label_modified: modified
-  label_copied: copied
-  label_renamed: renamed
-  label_deleted: deleted
-  label_latest_revision: Latest revision
-  label_latest_revision_plural: Latest revisions
-  label_view_revisions: View revisions
-  label_view_all_revisions: View all revisions
-  label_max_size: Maximum size
-  label_sort_highest: Move to top
-  label_sort_higher: Move up
-  label_sort_lower: Move down
-  label_sort_lowest: Move to bottom
-  label_roadmap: Roadmap
-  label_roadmap_due_in: "Due in {{value}}"
-  label_roadmap_overdue: "{{value}} late"
-  label_roadmap_no_issues: No issues for this version
-  label_search: Search
-  label_result_plural: Results
-  label_all_words: All words
-  label_wiki: Wiki
-  label_wiki_edit: Wiki edit
-  label_wiki_edit_plural: Wiki edits
-  label_wiki_page: Wiki page
-  label_wiki_page_plural: Wiki pages
-  label_index_by_title: Index by title
-  label_index_by_date: Index by date
-  label_current_version: Current version
-  label_preview: Preview
-  label_feed_plural: Feeds
-  label_changes_details: Details of all changes
-  label_issue_tracking: Issue tracking
-  label_spent_time: Spent time
-  label_f_hour: "{{value}} hour"
-  label_f_hour_plural: "{{value}} hours"
-  label_time_tracking: Time tracking
-  label_change_plural: Changes
-  label_statistics: Statistics
-  label_commits_per_month: Commits per month
-  label_commits_per_author: Commits per author
-  label_view_diff: View differences
-  label_diff_inline: inline
-  label_diff_side_by_side: side by side
-  label_options: Options
-  label_copy_workflow_from: Copy workflow from
-  label_permissions_report: Permissions report
-  label_watched_issues: Watched issues
-  label_related_issues: Related issues
-  label_applied_status: Applied status
-  label_loading: Loading...
-  label_relation_new: New relation
-  label_relation_delete: Delete relation
-  label_relates_to: related to
-  label_duplicates: duplicates
-  label_duplicated_by: duplicated by
-  label_blocks: blocks
-  label_blocked_by: blocked by
-  label_precedes: precedes
-  label_follows: follows
-  label_end_to_start: end to start
-  label_end_to_end: end to end
-  label_start_to_start: start to start
-  label_start_to_end: start to end
-  label_stay_logged_in: Stay logged in
-  label_disabled: disabled
-  label_show_completed_versions: Show completed versions
-  label_me: me
-  label_board: Forum
-  label_board_new: New forum
-  label_board_plural: Forums
-  label_board_locked: Locked
-  label_board_sticky: Sticky
-  label_topic_plural: Topics
-  label_message_plural: Messages
-  label_message_last: Last message
-  label_message_new: New message
-  label_message_posted: Message added
-  label_reply_plural: Replies
-  label_send_information: Send account information to the user
-  label_year: Year
-  label_month: Month
-  label_week: Week
-  label_date_from: From
-  label_date_to: To
-  label_language_based: "Based on user's language"
-  label_sort_by: "Sort by {{value}}"
-  label_send_test_email: Send a test email
-  label_feeds_access_key: RSS access key
-  label_missing_feeds_access_key: Missing a RSS access key
-  label_feeds_access_key_created_on: "RSS access key created {{value}} ago"
-  label_module_plural: Modules
-  label_added_time_by: "Added by {{author}} {{age}} ago"
-  label_updated_time_by: "Updated by {{author}} {{age}} ago"
-  label_updated_time: "Updated {{value}} ago"
-  label_jump_to_a_project: Jump to a project...
-  label_file_plural: Downloads
-  label_changeset_plural: Changesets
-  label_default_columns: Default columns
-  label_no_change_option: (No change)
-  label_bulk_edit_selected_issues: Bulk edit selected issues
-  label_theme: Theme
-  label_default: Default
-  label_search_titles_only: Search titles only
-  label_user_mail_option_all: "For any event on all my projects"
-  label_user_mail_option_selected: "For any event on the selected projects only..."
-  label_user_mail_option_none: "No events"
-  label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself"
-  label_registration_activation_by_email: account activation by email
-  label_registration_manual_activation: manual account activation
-  label_registration_automatic_activation: automatic account activation
-  label_display_per_page: "Per page: {{value}}"
-  label_age: Age
-  label_change_properties: Change properties
-  label_general: General
-  label_more: More
-  label_scm: SCM
-  label_plugins: Plugins
-  label_ldap_authentication: LDAP authentication
-  label_downloads_abbr: D/L
-  label_optional_description: Optional description
-  label_add_another_file: Add another file
-  label_preferences: Preferences
-  label_chronological_order: In chronological order
-  label_reverse_chronological_order: In reverse chronological order
-  label_planning: Planning
-  label_incoming_emails: Incoming emails
-  label_generate_key: Generate a key
-  label_issue_watchers: Watchers
-  label_example: Example
-  label_display: Display
-  label_sort: Sort
-  label_ascending: Ascending
-  label_descending: Descending
-  label_date_from_to: From {{start}} to {{end}}
-  label_wiki_content_added: Wiki page added
-  label_wiki_content_updated: Wiki page updated
-  label_group: Group
-  label_group_plural: Groups
-  label_group_new: New group
-  label_time_entry_plural: Spent time
-  label_version_sharing_none: Not shared
-  label_version_sharing_descendants: With subprojects
-  label_version_sharing_hierarchy: With project hierarchy
-  label_version_sharing_tree: With project tree
-  label_version_sharing_system: With all projects
-  label_update_issue_done_ratios: Update issue done ratios
-  label_copy_source: Source
-  label_copy_target: Target
-  label_copy_same_as_target: Same as target
-  label_display_used_statuses_only: Only display statuses that are used by this tracker
-  label_api_access_key: API access key
-  label_missing_api_access_key: Missing an API access key
-  label_api_access_key_created_on: "API access key created {{value}} ago"
-  
-  button_login: Login
-  button_submit: Submit
-  button_save: Save
-  button_check_all: Check all
-  button_uncheck_all: Uncheck all
-  button_delete: Delete
-  button_create: Create
-  button_create_and_continue: Create and continue
-  button_test: Test
-  button_edit: Edit
-  button_add: Add
-  button_change: Change
-  button_apply: Apply
-  button_clear: Clear
-  button_lock: Lock
-  button_unlock: Unlock
-  button_download: Download
-  button_list: List
-  button_view: View
-  button_move: Move
-  button_move_and_follow: Move and follow
-  button_back: Back
-  button_cancel: Cancel
-  button_activate: Activate
-  button_sort: Sort
-  button_log_time: Log time
-  button_rollback: Rollback to this version
-  button_watch: Watch
-  button_unwatch: Unwatch
-  button_reply: Reply
-  button_archive: Archive
-  button_unarchive: Unarchive
-  button_reset: Reset
-  button_rename: Rename
-  button_change_password: Change password
-  button_copy: Copy
-  button_copy_and_follow: Copy and follow
-  button_annotate: Annotate
-  button_update: Update
-  button_configure: Configure
-  button_quote: Quote
-  button_duplicate: Duplicate
-  button_show: Show
-  
-  status_active: active
-  status_registered: registered
-  status_locked: locked
-  
-  version_status_open: open
-  version_status_locked: locked
-  version_status_closed: closed
-
-  field_active: Active
-  
-  text_select_mail_notifications: Select actions for which email notifications should be sent.
-  text_regexp_info: eg. ^[A-Z0-9]+$
-  text_min_max_length_info: 0 means no restriction
-  text_project_destroy_confirmation: Are you sure you want to delete this project and related data?
-  text_subprojects_destroy_warning: "Its subproject(s): {{value}} will be also deleted."
-  text_workflow_edit: Select a role and a tracker to edit the workflow
-  text_are_you_sure: Are you sure?
-  text_journal_changed: "{{label}} changed from {{old}} to {{new}}"
-  text_journal_set_to: "{{label}} set to {{value}}"
-  text_journal_deleted: "{{label}} deleted ({{old}})"
-  text_journal_added: "{{label}} {{value}} added"
-  text_tip_issue_begin_day: task beginning this day
-  text_tip_issue_end_day: task ending this day
-  text_tip_issue_begin_end_day: task beginning and ending this day
-  text_project_identifier_info: 'Only lower case letters (a-z), numbers and dashes are allowed.<br /> This will be used in all project-related URLs, and as the repository name. Once saved, the identifier <b>can not</b> be changed.'
-  text_project_homepage_info: 'Link to an external project page.'
-  text_project_name_info: "This will be the name of your project throughout this site.<br /> You can change your project's name at any time, in the project's settings."
-  text_project_visibility_info: "If your project is not public, it will only be visible to users that you have added as project members."
-  text_user_ssamr_description_info: 'Please describe your current research or development interests, within the fields of audio and music.<br/>This information is publicly visible in your profile and you can edit it at any time.'
-  text_issue_parent_issue_info: 'If this is a subtask, please insert its parent task number or write the main task name.'
-  text_caracters_maximum: "{{count}} characters maximum."
-  text_caracters_minimum: "Must be at least {{count}} characters long."
-  text_length_between: "Length between {{min}} and {{max}} characters."
-  text_tracker_no_workflow: No workflow defined for this tracker
-  text_unallowed_characters: Unallowed characters
-  text_comma_separated: Multiple values allowed (comma separated).
-  text_line_separated: Multiple values allowed (one line for each value).
-  text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages
-  text_issue_added: "Issue {{id}} has been reported by {{author}}."
-  text_issue_updated: "Issue {{id}} has been updated by {{author}}."
-  text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content?
-  text_issue_category_destroy_question: "Some issues ({{count}}) are assigned to this category. What do you want to do?"
-  text_issue_category_destroy_assignments: Remove category assignments
-  text_issue_category_reassign_to: Reassign issues to this category
-  text_user_mail_option: "For unselected projects, you will only receive notifications about things you watch or you're involved in (eg. issues you're the author or assignee)."
-  text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
-  text_load_default_configuration: Load the default configuration
-  text_status_changed_by_changeset: "Applied in changeset {{value}}."
-  text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s)?'
-  text_select_project_modules: 'Select modules to enable for this project:'
-  text_default_administrator_account_changed: Default administrator account changed
-  text_file_repository_writable: Attachments directory writable
-  text_plugin_assets_writable: Plugin assets directory writable
-  text_rmagick_available: RMagick available (optional)
-  text_destroy_time_entries_question: "{{hours}} hours were reported on the issues you are about to delete. What do you want to do?"
-  text_destroy_time_entries: Delete reported hours
-  text_assign_time_entries_to_project: Assign reported hours to the project
-  text_reassign_time_entries: 'Reassign reported hours to this issue:'
-  text_user_wrote: "{{value}} wrote:"
-  text_enumeration_destroy_question: "{{count}} objects are assigned to this value."
-  text_enumeration_category_reassign_to: 'Reassign them to this value:'
-  text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
-  text_repository_usernames_mapping: "Select or update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
-  text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
-  text_custom_field_possible_values_info: 'One line for each value'
-  text_wiki_page_destroy_question: "This page has {{descendants}} child page(s) and descendant(s). What do you want to do?"
-  text_wiki_page_nullify_children: "Keep child pages as root pages"
-  text_wiki_page_destroy_children: "Delete child pages and all their descendants"
-  text_wiki_page_reassign_children: "Reassign child pages to this parent page"
-  text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?"
-  text_settings_repo_creation: The repository for a project should be set up automatically within a few minutes of the project being created.<br>You should not have to adjust any settings here; please check again in ten minutes.
-  
-  text_settings_repo_is_internal: The repository for this project is an internal Mercurial Repository, hosted by SoundSoftware.ac.uk. 
-  text_settings_repo_is_external: You are tracking an external repository, with a mirror Mercurial repository hosted by SoundSoftware.ac.uk.
-
-  default_role_manager: Manager
-  default_role_developer: Developer
-  default_role_reporter: Reporter
-  default_tracker_bug: Bug
-  default_tracker_feature: Feature
-  default_tracker_support: Support
-  default_issue_status_new: New
-  default_issue_status_in_progress: In Progress
-  default_issue_status_resolved: Resolved
-  default_issue_status_feedback: Feedback
-  default_issue_status_closed: Closed
-  default_issue_status_rejected: Rejected
-  default_doc_category_user: User documentation
-  default_doc_category_tech: Technical documentation
-  default_priority_low: Low
-  default_priority_normal: Normal
-  default_priority_high: High
-  default_priority_urgent: Urgent
-  default_priority_immediate: Immediate
-  default_activity_design: Design
-  default_activity_development: Development
-  
-  enumeration_issue_priorities: Issue priorities
-  enumeration_doc_categories: Document categories
-  enumeration_activities: Activities (time tracking)
-  enumeration_system_activity: System Activity
-
-  notice_unable_delete_time_entry: Unable to delete time log entry.
-  error_can_not_delete_custom_field: Unable to delete custom field
-  permission_manage_subtasks: Manage subtasks
-  label_profile: Profile
-  error_unable_to_connect: Unable to connect ({{value}})
-  label_overall_spent_time: Overall spent time
-  error_can_not_remove_role: This role is in use and can not be deleted.
-  field_principal: Principal
-  field_parent_issue: Parent task
-  label_my_page_block: My page block
-  text_zoom_out: Zoom out
-  text_zoom_in: Zoom in
-  error_unable_delete_issue_status: Unable to delete issue status
-  label_subtask_plural: Subtasks
-  error_can_not_delete_tracker: This tracker contains issues and can't be deleted.
-  notice_failed_to_save_members: "Failed to save member(s): {{errors}}."
-  label_project_copy_notifications: Send email notifications during the project copy
-  field_time_entries: Log time
-  field_member_of_group: Member of Group
-  field_assigned_to_role: Member of Role
-  button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}"
-  text_are_you_sure_with_children: Delete issue and all child issues?
-  field_text: Text field
-  label_user_mail_option_only_owner: Only for things I am the owner of
-  setting_default_notification_option: Default notification option
-  label_user_mail_option_only_my_events: Only for things I watch or I'm involved in
-  label_user_mail_option_only_assigned: Only for things I am assigned to
-  notice_not_authorized_archived_project: The project you're trying to access has been archived.
-  label_principal_search: "Search by name:"
-  label_user_search: "Search for user:"
-  field_visible: Visible
-  setting_emails_header: Emails header
-
-  label_manager_description: All powers including adding and removing members and adjusting project settings
-  label_developer_description: Can commit to repository and carry out most project editing tasks
-  label_reporter_description: Can submit bug reports; has read access for private projects
-
-  label_set_role_plural: Choose roles for new member
-  notice_added_to_project: 'You have been added to the project "{{project_name}}".'
-  notice_project_homepage: "You can visit the project using the following link: {{project_url}}"
-  mail_subject_added_to_project: "You've been added to a project on {{value}}"
-  
\ No newline at end of file
--- a/config/locales/en.yml	Thu Mar 24 13:59:03 2011 +0000
+++ b/config/locales/en.yml	Mon Mar 28 18:17:06 2011 +0100
@@ -1,14 +1,13 @@
 en:
-  # Text direction: Left-to-Right (ltr) or Right-to-Left (rtl)
   direction: ltr
   date:
     formats:
       # Use the strftime parameters for formats.
       # When no format has been given, it uses default.
       # You can provide other formats here if you like!
-      default: "%m/%d/%Y"
-      short: "%b %d"
-      long: "%B %d, %Y"
+      default: "%d/%m/%Y"
+      short: "%d %b"
+      long: "%d %B, %Y"
       
     day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
     abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
@@ -21,10 +20,10 @@
 
   time:
     formats:
-      default: "%m/%d/%Y %I:%M %p"
+      default: "%d/%m/%Y %I:%M %p"
       time: "%I:%M %p"
       short: "%d %b %H:%M"
-      long: "%B %d, %Y %H:%M"
+      long: "%d %B, %Y %H:%M"
     am: "am"
     pm: "pm"
       
@@ -66,11 +65,15 @@
         other: "almost {{count}} years"
 
   number:
-    # Default format for numbers
     format:
-      separator: "." 
-      delimiter: ""
+      separator: "."
+      delimiter: " "
       precision: 3
+
+    currency:
+      format:
+        format: "%u%n"
+        unit: "£"
     human:
       format:
         delimiter: ""
@@ -86,6 +89,7 @@
           gb: "GB"
           tb: "TB"
 
+        
 # Used in array.to_sentence.
   support:
     array:
@@ -119,7 +123,7 @@
         not_same_project: "doesn't belong to the same project"
         circular_dependency: "This relation would create a circular dependency"
         cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks"
-        must_accept_terms_and_conditions: "You must accept the Terms and Conditions"
+        must_accept_terms_and_conditions: "You must accept the Terms and Conditions"        
 
   actionview_instancetag_blank_option: Please select
   
@@ -132,7 +136,7 @@
   general_csv_decimal_separator: '.'
   general_csv_encoding: ISO-8859-1
   general_pdf_encoding: ISO-8859-1
-  general_first_day_of_week: '7'
+  general_first_day_of_week: '1'
   
   notice_account_updated: Account was successfully updated.
   notice_account_invalid_creditentials: Invalid user or password
@@ -306,9 +310,9 @@
   field_text: Text field
   field_visible: Visible
  
-  setting_external_repository: "In the case you wish to follow an external repository"
-  setting_external_repository_url: "The external repository URL"
-  label_repository_external_url: "External rep URL"
+  setting_external_repository: "Select this if the project's main repository is hosted somewhere else"
+  setting_external_repository_url: "The URL of the existing external repository. Must be publicly accessible without a password"
+  label_repository_external_url: "External repository URL"
   setting_tipoftheday_text: Tip of the Day
   setting_notifications_text: Notifications
   field_terms_and_conditions: 'Terms and Conditions:'
@@ -402,8 +406,8 @@
   permission_comment_news: Comment news
   permission_manage_documents: Manage documents
   permission_view_documents: View documents
-  permission_manage_files: Manage Downloads
-  permission_view_files: View Downloads
+  permission_manage_files: Manage downloads
+  permission_view_files: View downloads
   permission_manage_wiki: Manage wiki
   permission_rename_wiki_pages: Rename wiki pages
   permission_delete_wiki_pages: Delete wiki pages
@@ -438,6 +442,10 @@
   project_module_gantt: Gantt chart
   project_module_embedded: Embedded documentation (Javadoc or Doxygen)
   label_tipoftheday: Tip of the day
+  label_notifications: Important Message
+  field_terms_and_conditions: 'Terms and Conditions:'
+  accept_terms_and_conditions: 'I have read and agree with the '
+  label_ssamr_description: Research description
   label_ssamr_details: Other Details
   label_ssamr_institution: Institution
   label_user: User
@@ -595,7 +603,7 @@
   label_internal: Internal
   label_last_changes: "last {{count}} changes"
   label_change_view_all: View all changes
-  label_personalize_page: Personalize this page
+  label_personalize_page: Personalise this page
   label_comment: Comment
   label_comment_plural: Comments
   label_x_comments:
@@ -634,7 +642,7 @@
   label_not_contains: doesn't contain
   label_day_plural: days
   label_repository: Repository
-  label_is_external_repository: External?
+  label_is_external_repository: Track an external repository
   label_repository_plural: Repositories
   label_browse: Browse
   label_modification: "{{count}} change"
@@ -863,26 +871,27 @@
   version_status_closed: closed
 
   field_active: Active
+  field_current: Current
   
   text_select_mail_notifications: Select actions for which email notifications should be sent.
   text_regexp_info: eg. ^[A-Z0-9]+$
   text_min_max_length_info: 0 means no restriction
-  text_project_destroy_confirmation: Are you sure you want to delete this project and related data ?
+  text_project_destroy_confirmation: Are you sure you want to delete this project and related data?
   text_subprojects_destroy_warning: "Its subproject(s): {{value}} will be also deleted."
   text_workflow_edit: Select a role and a tracker to edit the workflow
-  text_are_you_sure: Are you sure ?
+  text_are_you_sure: Are you sure?
   text_are_you_sure_with_children: "Delete issue and all child issues?"
   text_journal_changed: "{{label}} changed from {{old}} to {{new}}"
   text_journal_set_to: "{{label}} set to {{value}}"
   text_journal_deleted: "{{label}} deleted ({{old}})"
   text_journal_added: "{{label}} {{value}} added"
-  text_tip_issue_begin_day: issue beginning this day
-  text_tip_issue_end_day: issue ending this day
-  text_tip_issue_begin_end_day: issue beginning and ending this day
+  text_tip_issue_begin_day: task beginning this day
+  text_tip_issue_end_day: task ending this day
+  text_tip_issue_begin_end_day: task beginning and ending this day
   text_project_identifier_info: 'Only lower case letters (a-z), numbers and dashes are allowed.<br /> This will be used in all project-related URLs, and as the repository name. Once saved, the identifier <b>can not</b> be changed.'
+  text_project_homepage_info: 'Link to an external project page.'
   text_project_name_info: "This will be the name of your project throughout this site.<br /> You can change your project's name at any time, in the project's settings."
   text_project_visibility_info: "If your project is not public, it will only be visible to users that you have added as project members."
-  text_project_homepage_info: 'Link to an external project page.'
   text_user_ssamr_description_info: 'Please describe your current research or development interests, within the fields of audio and music.<br/>This information is publicly visible in your profile and you can edit it at any time.'
   text_issue_parent_issue_info: 'If this is a subtask, please insert its parent task number or write the main task name.'
   text_caracters_maximum: "{{count}} characters maximum."
@@ -895,21 +904,21 @@
   text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages
   text_issue_added: "Issue {{id}} has been reported by {{author}}."
   text_issue_updated: "Issue {{id}} has been updated by {{author}}."
-  text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content ?
-  text_issue_category_destroy_question: "Some issues ({{count}}) are assigned to this category. What do you want to do ?"
+  text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content?
+  text_issue_category_destroy_question: "Some issues ({{count}}) are assigned to this category. What do you want to do?"
   text_issue_category_destroy_assignments: Remove category assignments
   text_issue_category_reassign_to: Reassign issues to this category
   text_user_mail_option: "For unselected projects, you will only receive notifications about things you watch or you're involved in (eg. issues you're the author or assignee)."
   text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
   text_load_default_configuration: Load the default configuration
   text_status_changed_by_changeset: "Applied in changeset {{value}}."
-  text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s) ?'
+  text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s)?'
   text_select_project_modules: 'Select modules to enable for this project:'
   text_default_administrator_account_changed: Default administrator account changed
   text_file_repository_writable: Attachments directory writable
   text_plugin_assets_writable: Plugin assets directory writable
   text_rmagick_available: RMagick available (optional)
-  text_destroy_time_entries_question: "{{hours}} hours were reported on the issues you are about to delete. What do you want to do ?"
+  text_destroy_time_entries_question: "{{hours}} hours were reported on the issues you are about to delete. What do you want to do?"
   text_destroy_time_entries: Delete reported hours
   text_assign_time_entries_to_project: Assign reported hours to the project
   text_reassign_time_entries: 'Reassign reported hours to this issue:'
@@ -917,7 +926,7 @@
   text_enumeration_destroy_question: "{{count}} objects are assigned to this value."
   text_enumeration_category_reassign_to: 'Reassign them to this value:'
   text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
-  text_repository_usernames_mapping: "Select or update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
+  text_repository_usernames_mapping: "Select the project member associated with each username found in the repository log.\nUsers whose name or email matches that in the repository are mapped automatically."
   text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
   text_custom_field_possible_values_info: 'One line for each value'
   text_wiki_page_destroy_question: "This page has {{descendants}} child page(s) and descendant(s). What do you want to do?"
@@ -927,12 +936,15 @@
   text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?"
   text_zoom_in: Zoom in
   text_zoom_out: Zoom out
-  text_settings_repo_creation: The repository for a project should be set up automatically within a few minutes of the project being created.<br>You should not have to adjust any settings here.<br>Please check again in ten minutes, and <a href="/projects/soundsoftware-site/wiki/Help">contact us</a> if there is any problem.
-  text_settings_repo_is_internal: The repository for this project is an internal Mercurial Repository, hosted by SoundSoftware.ac.uk. 
-  text_settings_repo_is_external: You are tracking an external repository, with a mirror Mercurial repository hosted by SoundSoftware.ac.uk.
+  text_files_active_change: <br>Click the star to switch active status for a download on or off.<br>Active files will be shown more prominently in the download page.
+  text_settings_repo_creation: <b>Creating repository...</b></p><p>The source code repository for a project will be set up automatically within a few minutes of the project being created.</p><p>Please check again in five minutes, and <a href="/projects/soundsoftware-site/wiki/Help">contact us</a> if there is any problem.</p><p>If you wish to use this project to track a repository that is already hosted somewhere else, please wait until the repository has been created here and then return to this settings page to configure it.</p><p>If you don't want a repository at all, go to the Modules tab and switch it off there.
+  text_settings_repo_explanation: <b>External repositories</b><p>Normally your project's primary repository will be the Mercurial repository hosted at this site.<p>However, if you already have your project hosted somewhere else, you can specify your existing external repository's URL here &ndash; then this site will track that repository in a read-only &ldquo;mirror&rdquo; copy.  External Mercurial, git and Subversion repositories can be tracked. Note that you cannot switch to an external repository if you have already made any commits to the repository hosted here.
+  text_settings_repo_is_internal: Currently the repository hosted at this site is the primary repository for this project.
+  text_settings_repo_is_external: Currently the repository hosted at this site is a read-only copy of an external repository.
+  text_settings_repo_need_help: Please <a href="/projects/soundsoftware-site/wiki/Help">contact us</a> if you need help deciding how best to set this up.<br>We can also import complete revision history from other systems into a new primary repository for you if you wish.
+  text_repository_external: "The primary repository for this project is hosted at <code>{{location}}</code><br>This repository is a read-only copy which is updated automatically."
 
-
-  
+ 
   default_role_manager: Manager
   default_role_developer: Developer
   default_role_reporter: Reporter
@@ -965,6 +977,7 @@
   label_reporter_description: Can submit bug reports; has read access for private projects
 
   label_set_role_plural: Choose roles for new member
+
   notice_added_to_project: 'You have been added to the project "{{project_name}}".'
   notice_project_homepage: "You can visit the project using the following link: {{project_url}}"
   mail_subject_added_to_project: "You've been added to a project on {{value}}"
--- a/config/routes.rb	Thu Mar 24 13:59:03 2011 +0000
+++ b/config/routes.rb	Mon Mar 28 18:17:06 2011 +0100
@@ -236,6 +236,7 @@
   map.with_options :controller => 'sys' do |sys|
     sys.connect 'sys/projects.:format', :action => 'projects', :conditions => {:method => :get}
     sys.connect 'sys/projects/:id/repository.:format', :action => 'create_project_repository', :conditions => {:method => :post}
+    sys.connect 'sys/projects/:id/external-repository.:format', :action => 'get_external_repo_url', :conditions => {:method => :get}
     sys.connect 'sys/projects/:id/embedded.:format', :action => 'set_embedded_active', :conditions => { :method => :post }
   end
  
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110303152903_add_active_column_to_attachments.rb	Mon Mar 28 18:17:06 2011 +0100
@@ -0,0 +1,9 @@
+class AddActiveColumnToAttachments < ActiveRecord::Migration
+  def self.up
+    add_column :attachments, :active, :boolean
+  end
+
+  def self.down
+    remove_column :attachments, :active
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/SoundSoftware-salted.pm	Mon Mar 28 18:17:06 2011 +0100
@@ -0,0 +1,470 @@
+package Apache::Authn::SoundSoftware;
+
+=head1 Apache::Authn::SoundSoftware
+
+SoundSoftware - a mod_perl module for Apache authentication against a
+Redmine database and optional LDAP implementing the access control
+rules required for the SoundSoftware.ac.uk repository site.
+
+=head1 SYNOPSIS
+
+This module is closely based on the Redmine.pm authentication module
+provided with Redmine.  It is intended to be used for authentication
+in front of a repository service such as hgwebdir.
+
+Requirements:
+
+1. Clone/pull from repo for public project: Any user, no
+authentication required
+
+2. Clone/pull from repo for private project: Project members only
+
+3. Push to repo for public project: "Permitted" users only (this
+probably means project members who are also identified in the hgrc web
+section for the repository and so will be approved by hgwebdir?)
+
+4. Push to repo for private project: "Permitted" users only (as above)
+
+5. Push to any repo that is tracking an external repo: Refused always
+
+=head1 INSTALLATION
+
+Debian/ubuntu:
+
+  apt-get install libapache-dbi-perl libapache2-mod-perl2 \
+    libdbd-mysql-perl libauthen-simple-ldap-perl libio-socket-ssl-perl
+
+Note that LDAP support is hardcoded "on" in this script (it is
+optional in the original Redmine.pm).
+
+=head1 CONFIGURATION
+
+   ## This module has to be in your perl path
+   ## eg:  /usr/local/lib/site_perl/Apache/Authn/SoundSoftware.pm
+   PerlLoadModule Apache::Authn::SoundSoftware
+
+   # Example when using hgwebdir
+   ScriptAlias / "/var/hg/hgwebdir.cgi/"
+
+   <Location />
+       AuthName "Mercurial"
+       AuthType Basic
+       Require valid-user
+       PerlAccessHandler Apache::Authn::SoundSoftware::access_handler
+       PerlAuthenHandler Apache::Authn::SoundSoftware::authen_handler
+       SoundSoftwareDSN "DBI:mysql:database=redmine;host=localhost"
+       SoundSoftwareDbUser "redmine"
+       SoundSoftwareDbPass "password"
+       Options +ExecCGI
+       AddHandler cgi-script .cgi
+       ## Optional where clause (fulltext search would be slow and
+       ## database dependant).
+       # SoundSoftwareDbWhereClause "and members.role_id IN (1,2)"
+       ## Optional prefix for local repository URLs
+       # SoundSoftwareRepoPrefix "/var/hg/"
+  </Location>
+
+See the original Redmine.pm for further configuration notes.
+
+=cut
+
+use strict;
+use warnings FATAL => 'all', NONFATAL => 'redefine';
+
+use DBI;
+use Digest::SHA1;
+use Authen::Simple::LDAP;
+use Apache2::Module;
+use Apache2::Access;
+use Apache2::ServerRec qw();
+use Apache2::RequestRec qw();
+use Apache2::RequestUtil qw();
+use Apache2::Const qw(:common :override :cmd_how);
+use APR::Pool ();
+use APR::Table ();
+
+my @directives = (
+  {
+    name => 'SoundSoftwareDSN',
+    req_override => OR_AUTHCFG,
+    args_how => TAKE1,
+    errmsg => 'Dsn in format used by Perl DBI. eg: "DBI:Pg:dbname=databasename;host=my.db.server"',
+  },
+  {
+    name => 'SoundSoftwareDbUser',
+    req_override => OR_AUTHCFG,
+    args_how => TAKE1,
+  },
+  {
+    name => 'SoundSoftwareDbPass',
+    req_override => OR_AUTHCFG,
+    args_how => TAKE1,
+  },
+  {
+    name => 'SoundSoftwareDbWhereClause',
+    req_override => OR_AUTHCFG,
+    args_how => TAKE1,
+  },
+  {
+    name => 'SoundSoftwareRepoPrefix',
+    req_override => OR_AUTHCFG,
+    args_how => TAKE1,
+  },
+);
+
+sub SoundSoftwareDSN { 
+    my ($self, $parms, $arg) = @_;
+    $self->{SoundSoftwareDSN} = $arg;
+    my $query = "SELECT 
+                 hashed_password, salt, auth_source_id, permissions
+              FROM members, projects, users, roles, member_roles
+              WHERE 
+                projects.id=members.project_id
+                AND member_roles.member_id=members.id
+                AND users.id=members.user_id 
+                AND roles.id=member_roles.role_id
+                AND users.status=1 
+                AND login=? 
+                AND identifier=? ";
+    $self->{SoundSoftwareQuery} = trim($query);
+}
+
+sub SoundSoftwareDbUser { set_val('SoundSoftwareDbUser', @_); }
+sub SoundSoftwareDbPass { set_val('SoundSoftwareDbPass', @_); }
+sub SoundSoftwareDbWhereClause { 
+    my ($self, $parms, $arg) = @_;
+    $self->{SoundSoftwareQuery} = trim($self->{SoundSoftwareQuery}.($arg ? $arg : "")." ");
+}
+
+sub SoundSoftwareRepoPrefix { 
+    my ($self, $parms, $arg) = @_;
+    if ($arg) {
+	$self->{SoundSoftwareRepoPrefix} = $arg;
+    }
+}
+
+sub trim {
+    my $string = shift;
+    $string =~ s/\s{2,}/ /g;
+    return $string;
+}
+
+sub set_val {
+    my ($key, $self, $parms, $arg) = @_;
+    $self->{$key} = $arg;
+}
+
+Apache2::Module::add(__PACKAGE__, \@directives);
+
+
+my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
+
+sub access_handler {
+    my $r = shift;
+
+    print STDERR "SoundSoftware.pm: In access handler at " . scalar localtime() . "\n";
+
+    unless ($r->some_auth_required) {
+	$r->log_reason("No authentication has been configured");
+	return FORBIDDEN;
+    }
+
+    my $method = $r->method;
+
+    print STDERR "SoundSoftware.pm: Method: $method, uri " . $r->uri . ", location " . $r->location . "\n";
+    print STDERR "SoundSoftware.pm: Accept: " . $r->headers_in->{Accept} . "\n";
+
+    my $dbh = connect_database($r);
+    unless ($dbh) {
+	print STDERR "SoundSoftware.pm: Database connection failed!: " . $DBI::errstr . "\n";
+	return FORBIDDEN;
+    }
+
+    print STDERR "Connected to db, dbh is " . $dbh . "\n";
+
+    my $project_id = get_project_identifier($dbh, $r);
+
+    if (!defined $read_only_methods{$method}) {
+        print STDERR "SoundSoftware.pm: Method is not read-only\n";
+        if (project_repo_is_readonly($dbh, $project_id, $r)) {
+            print STDERR "SoundSoftware.pm: Project repo is read-only, refusing access\n";
+	    return FORBIDDEN;
+        } else {
+	    print STDERR "SoundSoftware.pm: Project repo is read-write, authentication handler required\n";
+            return OK;
+        }
+    }
+
+    my $status = get_project_status($dbh, $project_id, $r);
+
+    $dbh->disconnect();
+    undef $dbh;
+
+    if ($status == 0) { # nonexistent
+	print STDERR "SoundSoftware.pm: Project does not exist, refusing access\n";
+	return FORBIDDEN;
+    } elsif ($status == 1) { # public
+	print STDERR "SoundSoftware.pm: Project is public, no restriction here\n";
+	$r->set_handlers(PerlAuthenHandler => [\&OK])
+    } else { # private
+	print STDERR "SoundSoftware.pm: Project is private, authentication handler required\n";
+    }
+
+    return OK
+}
+
+sub authen_handler {
+    my $r = shift;
+    
+    print STDERR "SoundSoftware.pm: In authentication handler at " . scalar localtime() . "\n";
+
+    my $dbh = connect_database($r);
+    unless ($dbh) {
+        print STDERR "SoundSoftware.pm: Database connection failed!: " . $DBI::errstr . "\n";
+        return AUTH_REQUIRED;
+    }
+    
+    my $project_id = get_project_identifier($dbh, $r);
+    my $realm = get_realm($dbh, $project_id, $r);
+    $r->auth_name($realm);
+
+    my ($res, $redmine_pass) =  $r->get_basic_auth_pw();
+    unless ($res == OK) {
+	$dbh->disconnect();
+	undef $dbh;
+	return $res;
+    }
+    
+    print STDERR "SoundSoftware.pm: User is " . $r->user . ", got password\n";
+
+    my $permitted = is_permitted($dbh, $project_id, $r->user, $redmine_pass, $r);
+    
+    $dbh->disconnect();
+    undef $dbh;
+
+    if ($permitted) {
+	return OK;
+    } else {
+	print STDERR "SoundSoftware.pm: Not permitted\n";
+	$r->note_auth_failure();
+	return AUTH_REQUIRED;
+    }
+}
+
+sub get_project_status {
+    my $dbh = shift;
+    my $project_id = shift;
+    my $r = shift;
+
+    if (!defined $project_id or $project_id eq '') {
+	return 0; # nonexistent
+    }
+    
+    my $sth = $dbh->prepare(
+        "SELECT is_public FROM projects WHERE projects.identifier = ?;"
+    );
+
+    $sth->execute($project_id);
+    my $ret = 0; # nonexistent
+    if (my @row = $sth->fetchrow_array) {
+    	if ($row[0] eq "1" || $row[0] eq "t") {
+	    $ret = 1; # public
+    	} else {
+	    $ret = 2; # private
+	}
+    }
+    $sth->finish();
+    undef $sth;
+
+    $ret;
+}
+
+sub project_repo_is_readonly {
+    my $dbh = shift;
+    my $project_id = shift;
+    my $r = shift;
+
+    if (!defined $project_id or $project_id eq '') {
+        return 0; # nonexistent
+    }
+
+    my $sth = $dbh->prepare(
+        "SELECT repositories.is_external FROM repositories, projects WHERE projects.identifier = ? AND repositories.project_id = projects.id;"
+    );
+
+    $sth->execute($project_id);
+    my $ret = 0; # nonexistent
+    if (my @row = $sth->fetchrow_array) {
+        if (defined($row[0]) && ($row[0] eq "1" || $row[0] eq "t")) {
+            $ret = 1; # read-only (i.e. external)
+        } else {
+            $ret = 0; # read-write
+        }
+    }
+    $sth->finish();
+    undef $sth;
+
+    $ret;
+}
+
+sub is_permitted {
+    my $dbh = shift;
+    my $project_id = shift;
+    my $redmine_user = shift;
+    my $redmine_pass = shift;
+    my $r = shift;
+
+    my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
+
+    my $cfg = Apache2::Module::get_config
+	(__PACKAGE__, $r->server, $r->per_dir_config);
+
+    my $query = $cfg->{SoundSoftwareQuery};
+    my $sth = $dbh->prepare($query);
+    $sth->execute($redmine_user, $project_id);
+
+    my $ret;
+    while (my ($hashed_password, $salt, $auth_source_id, $permissions) = $sth->fetchrow_array) {
+
+	# Test permissions for this user before we verify credentials
+	# -- if the user is not permitted this action anyway, there's
+	# not much point in e.g. contacting the LDAP
+
+	my $method = $r->method;
+
+	if ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/)
+	    || $permissions =~ /:commit_access/) {
+
+	    # User would be permitted this action, if their
+	    # credentials checked out -- test those now
+
+	    print STDERR "SoundSoftware.pm: User $redmine_user has required role, checking credentials\n";
+
+	    unless ($auth_source_id) {
+                my $salted_password = Digest::SHA1::sha1_hex($salt.$pass_digest);
+		if ($hashed_password eq $salted_password) {
+		    print STDERR "SoundSoftware.pm: User $redmine_user authenticated via password\n";
+		    $ret = 1;
+		    last;
+		}
+	    } else {
+		my $sthldap = $dbh->prepare(
+		    "SELECT host,port,tls,account,account_password,base_dn,attr_login FROM auth_sources WHERE id = ?;"
+		    );
+		$sthldap->execute($auth_source_id);
+		while (my @rowldap = $sthldap->fetchrow_array) {
+		    my $ldap = Authen::Simple::LDAP->new(
+			host    => ($rowldap[2] eq "1" || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]" : $rowldap[0],
+			port    => $rowldap[1],
+			basedn  => $rowldap[5],
+			binddn  => $rowldap[3] ? $rowldap[3] : "",
+			bindpw  => $rowldap[4] ? $rowldap[4] : "",
+			filter  => "(".$rowldap[6]."=%s)"
+			);
+		    if ($ldap->authenticate($redmine_user, $redmine_pass)) {
+			print STDERR "SoundSoftware.pm: User $redmine_user authenticated via LDAP\n";
+			$ret = 1;
+		    }
+		}
+		$sthldap->finish();
+		undef $sthldap;
+	    }
+	} else {
+	    print STDERR "SoundSoftware.pm: User $redmine_user lacks required role for this project\n";
+	}
+    }
+
+    $sth->finish();
+    undef $sth;
+
+    $ret;
+}
+
+sub get_project_identifier {
+    my $dbh = shift;
+    my $r = shift;
+
+    my $location = $r->location;
+    my ($repo) = $r->uri =~ m{$location/*([^/]+)};
+
+    return $repo if (!$repo);
+
+    $repo =~ s/[^a-zA-Z0-9\._-]//g;
+
+    # The original Redmine.pm returns the string just calculated as
+    # the project identifier.  That won't do for us -- we may have
+    # (and in fact already do have, in our test instance) projects
+    # whose repository names differ from the project identifiers.
+
+    # This is a rather fundamental change because it means that almost
+    # every request needs more than one database query -- which
+    # prompts us to start passing around $dbh instead of connecting
+    # locally within each function as is done in Redmine.pm.
+
+    my $sth = $dbh->prepare(
+        "SELECT projects.identifier FROM projects, repositories WHERE repositories.project_id = projects.id AND repositories.url LIKE ?;"
+    );
+
+    my $cfg = Apache2::Module::get_config
+	(__PACKAGE__, $r->server, $r->per_dir_config);
+
+    my $prefix = $cfg->{SoundSoftwareRepoPrefix};
+    if (!defined $prefix) { $prefix = '%/'; }
+
+    my $identifier = '';
+
+    $sth->execute($prefix . $repo);
+    my $ret = 0;
+    if (my @row = $sth->fetchrow_array) {
+	$identifier = $row[0];
+    }
+    $sth->finish();
+    undef $sth;
+
+    print STDERR "SoundSoftware.pm: Repository '$repo' belongs to project '$identifier'\n";
+
+    $identifier;
+}
+
+sub get_realm {
+    my $dbh = shift;
+    my $project_id = shift;
+    my $r = shift;
+
+    my $sth = $dbh->prepare(
+        "SELECT projects.name FROM projects WHERE projects.identifier = ?;"
+    );
+
+    my $name = $project_id;
+
+    $sth->execute($project_id);
+    my $ret = 0;
+    if (my @row = $sth->fetchrow_array) {
+	$name = $row[0];
+    }
+    $sth->finish();
+    undef $sth;
+
+    # be timid about characters not permitted in auth realm and revert
+    # to project identifier if any are found
+    if ($name =~ m/[^\w\d\s\._-]/) {
+	$name = $project_id;
+    }
+
+    my $realm = '"Mercurial repository for ' . "'$name'" . '"';
+
+    $realm;
+}
+
+sub connect_database {
+    my $r = shift;
+    
+    my $cfg = Apache2::Module::get_config
+	(__PACKAGE__, $r->server, $r->per_dir_config);
+
+    return DBI->connect($cfg->{SoundSoftwareDSN},
+	                $cfg->{SoundSoftwareDbUser},
+		        $cfg->{SoundSoftwareDbPass});
+}
+
+1;
--- a/extra/soundsoftware/SoundSoftware.pm	Thu Mar 24 13:59:03 2011 +0000
+++ b/extra/soundsoftware/SoundSoftware.pm	Mon Mar 28 18:17:06 2011 +0100
@@ -25,6 +25,8 @@
 
 4. Push to repo for private project: "Permitted" users only (as above)
 
+5. Push to any repo that is tracking an external repo: Refused always
+
 =head1 INSTALLATION
 
 Debian/ubuntu:
@@ -172,21 +174,27 @@
     print STDERR "SoundSoftware.pm: Method: $method, uri " . $r->uri . ", location " . $r->location . "\n";
     print STDERR "SoundSoftware.pm: Accept: " . $r->headers_in->{Accept} . "\n";
 
-    if (!defined $read_only_methods{$method}) {
-	print STDERR "SoundSoftware.pm: Method is not read-only, authentication handler required\n";
-	return OK;
-    }
-
     my $dbh = connect_database($r);
     unless ($dbh) {
 	print STDERR "SoundSoftware.pm: Database connection failed!: " . $DBI::errstr . "\n";
 	return FORBIDDEN;
     }
 
-
-print STDERR "Connected to db, dbh is " . $dbh . "\n";
+    print STDERR "Connected to db, dbh is " . $dbh . "\n";
 
     my $project_id = get_project_identifier($dbh, $r);
+
+    if (!defined $read_only_methods{$method}) {
+        print STDERR "SoundSoftware.pm: Method is not read-only\n";
+        if (project_repo_is_readonly($dbh, $project_id, $r)) {
+            print STDERR "SoundSoftware.pm: Project repo is read-only, refusing access\n";
+	    return FORBIDDEN;
+        } else {
+	    print STDERR "SoundSoftware.pm: Project repo is read-write, authentication handler required\n";
+            return OK;
+        }
+    }
+
     my $status = get_project_status($dbh, $project_id, $r);
 
     $dbh->disconnect();
@@ -271,6 +279,34 @@
     $ret;
 }
 
+sub project_repo_is_readonly {
+    my $dbh = shift;
+    my $project_id = shift;
+    my $r = shift;
+
+    if (!defined $project_id or $project_id eq '') {
+        return 0; # nonexistent
+    }
+
+    my $sth = $dbh->prepare(
+        "SELECT repositories.is_external FROM repositories, projects WHERE projects.identifier = ? AND repositories.project_id = projects.id;"
+    );
+
+    $sth->execute($project_id);
+    my $ret = 0; # nonexistent
+    if (my @row = $sth->fetchrow_array) {
+        if (defined($row[0]) && ($row[0] eq "1" || $row[0] eq "t")) {
+            $ret = 1; # read-only (i.e. external)
+        } else {
+            $ret = 0; # read-write
+        }
+    }
+    $sth->finish();
+    undef $sth;
+
+    $ret;
+}
+
 sub is_permitted {
     my $dbh = shift;
     my $project_id = shift;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/convert-external-repos.rb	Mon Mar 28 18:17:06 2011 +0100
@@ -0,0 +1,173 @@
+#!/usr/bin/env ruby
+
+# == Synopsis
+#
+# convert-external-repos: Update local Mercurial mirrors of external repos,
+# by running an external command for each project requiring an update.
+#
+# == Usage
+#
+#    convert-external-repos [OPTIONS...] -s [DIR] -r [HOST]
+#     
+# == Arguments (mandatory)
+#
+#   -s, --scm-dir=DIR         use DIR as base directory for repositories
+#   -r, --redmine-host=HOST   assume Redmine is hosted on HOST. Examples:
+#                             -r redmine.example.net
+#                             -r http://redmine.example.net
+#                             -r https://example.net/redmine
+#   -k, --key=KEY             use KEY as the Redmine API key
+#   -c, --command=COMMAND     use this command to update each external
+#                             repository: command is called with the name
+#                             of the project, the path to its repo, and
+#                             its external repo url as its three args
+#
+# == Options
+#
+#   --http-user=USER          User for HTTP Basic authentication with Redmine WS
+#   --http-pass=PASSWORD      Password for Basic authentication with Redmine WS
+#   -t, --test                only show what should be done
+#   -h, --help                show help and exit
+#   -v, --verbose             verbose
+#   -V, --version             print version and exit
+#   -q, --quiet               no log
+
+
+require 'getoptlong'
+require 'rdoc/usage'
+require 'find'
+require 'etc'
+
+Version = "1.0"
+
+opts = GetoptLong.new(
+                      ['--scm-dir',      '-s', GetoptLong::REQUIRED_ARGUMENT],
+                      ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
+                      ['--key',          '-k', GetoptLong::REQUIRED_ARGUMENT],
+                      ['--http-user',          GetoptLong::REQUIRED_ARGUMENT],
+                      ['--http-pass',          GetoptLong::REQUIRED_ARGUMENT],
+                      ['--command' ,     '-c', GetoptLong::REQUIRED_ARGUMENT],
+                      ['--test',         '-t', GetoptLong::NO_ARGUMENT],
+                      ['--verbose',      '-v', GetoptLong::NO_ARGUMENT],
+                      ['--version',      '-V', GetoptLong::NO_ARGUMENT],
+                      ['--help'   ,      '-h', GetoptLong::NO_ARGUMENT],
+                      ['--quiet'  ,      '-q', GetoptLong::NO_ARGUMENT]
+                      )
+
+$verbose      = 0
+$quiet        = false
+$redmine_host = ''
+$repos_base   = ''
+$http_user    = ''
+$http_pass    = ''
+$test         = false
+
+def log(text, options={})
+  level = options[:level] || 0
+  puts text unless $quiet or level > $verbose
+  exit 1 if options[:exit]
+end
+
+def system_or_raise(command)
+  raise "\"#{command}\" failed" unless system command
+end
+
+begin
+  opts.each do |opt, arg|
+    case opt
+    when '--scm-dir';        $repos_base   = arg.dup
+    when '--redmine-host';   $redmine_host = arg.dup
+    when '--key';            $api_key      = arg.dup
+    when '--http-user';      $http_user    = arg.dup
+    when '--http-pass';      $http_pass    = arg.dup
+    when '--command';        $command      = arg.dup
+    when '--verbose';        $verbose += 1
+    when '--test';           $test = true
+    when '--version';        puts Version; exit
+    when '--help';           RDoc::usage
+    when '--quiet';          $quiet = true
+    end
+  end
+rescue
+  exit 1
+end
+
+if $test
+  log("running in test mode")
+end
+
+if ($redmine_host.empty? or $repos_base.empty? or $command.empty?)
+  RDoc::usage
+end
+
+unless File.directory?($repos_base)
+  log("directory '#{$repos_base}' doesn't exist", :exit => true)
+end
+
+begin
+  require 'active_resource'
+rescue LoadError
+  log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true)
+end
+
+class Project < ActiveResource::Base
+  self.headers["User-agent"] = "SoundSoftware external repository converter/#{Version}"
+end
+
+log("querying Redmine for projects...", :level => 1);
+
+$redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
+$redmine_host.gsub!(/\/$/, '')
+
+Project.site = "#{$redmine_host}/sys";
+Project.user = $http_user;
+Project.password = $http_pass;
+
+begin
+  # Get all active projects that have the Repository module enabled
+  projects = Project.find(:all, :params => {:key => $api_key})
+rescue => e
+  log("Unable to connect to #{Project.site}: #{e}", :exit => true)
+end
+
+if projects.nil?
+  log('no project found, perhaps you forgot to "Enable WS for repository management"', :exit => true)
+end
+
+log("retrieved #{projects.size} projects", :level => 1)
+
+projects.each do |project|
+  log("treating project #{project.name}", :level => 1)
+
+  if project.identifier.empty?
+    log("\tno identifier for project #{project.name}")
+    next
+  elsif not project.identifier.match(/^[a-z0-9\-]+$/)
+    log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
+    next
+  end
+
+  if !project.respond_to?(:repository) or !project.repository.is_external?
+    log("\tproject #{project.identifier} does not use an external repository");
+    next
+  end
+
+  external_url = project.repository.external_url;
+  log("\tproject #{project.identifier} has external repository url #{external_url}");
+
+  if !external_url.match(/^[a-z][a-z+]{0,8}[a-z]:\/\//)
+    log("\tthis doesn't look like a plausible url to me, skipping")
+    next
+  end
+
+  repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
+
+  unless File.directory?(repos_path)
+    log("\tproject repo directory '#{repos_path}' doesn't exist")
+    next
+  end
+
+  system($command, project.identifier, repos_path, external_url)
+
+end
+  
--- a/extra/soundsoftware/reposman-soundsoftware.rb	Thu Mar 24 13:59:03 2011 +0000
+++ b/extra/soundsoftware/reposman-soundsoftware.rb	Mon Mar 28 18:17:06 2011 +0100
@@ -9,12 +9,12 @@
 #    reposman [OPTIONS...] -s [DIR] -r [HOST]
 #     
 #  Examples:
-#    reposman --svn-dir=/var/svn --redmine-host=redmine.example.net --scm subversion
+#    reposman --scm-dir=/var/svn --redmine-host=redmine.example.net --scm subversion
 #    reposman -s /var/git -r redmine.example.net -u http://svn.example.net --scm git
 #
 # == Arguments (mandatory)
 #
-#   -s, --svn-dir=DIR         use DIR as base directory for svn repositories
+#   -s, --scm-dir=DIR         use DIR as base directory for repositories
 #   -r, --redmine-host=HOST   assume Redmine is hosted on HOST. Examples:
 #                             -r redmine.example.net
 #                             -r http://redmine.example.net
@@ -70,7 +70,7 @@
 SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem )
 
 opts = GetoptLong.new(
-                      ['--svn-dir',      '-s', GetoptLong::REQUIRED_ARGUMENT],
+                      ['--scm-dir',      '-s', GetoptLong::REQUIRED_ARGUMENT],
                       ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
                       ['--key',          '-k', GetoptLong::REQUIRED_ARGUMENT],
                       ['--owner',        '-o', GetoptLong::REQUIRED_ARGUMENT],
@@ -133,7 +133,7 @@
 begin
   opts.each do |opt, arg|
     case opt
-    when '--svn-dir';        $repos_base   = arg.dup
+    when '--scm-dir';        $repos_base   = arg.dup
     when '--redmine-host';   $redmine_host = arg.dup
     when '--key';            $api_key      = arg.dup
     when '--owner';          $svn_owner    = arg.dup; $use_groupid = false;
@@ -174,7 +174,7 @@
 end
 
 unless File.directory?($repos_base)
-  log("directory '#{$repos_base}' doesn't exists", :exit => true)
+  log("directory '#{$repos_base}' doesn't exist", :exit => true)
 end
 
 begin
@@ -184,7 +184,7 @@
 end
 
 class Project < ActiveResource::Base
-  self.headers["User-agent"] = "Redmine repository manager/#{Version}"
+  self.headers["User-agent"] = "SoundSoftware repository manager/#{Version}"
 end
 
 log("querying Redmine for projects...", :level => 1);
@@ -346,5 +346,14 @@
     log("\trepository #{repos_path} created");
   end
 
+  if project.respond_to?(:repository) and project.repository.is_external?
+    external_url = project.repository.external_url;
+    log("\tproject #{project.identifier} has external repository url #{external_url}");
+    if !external_url.match(/^https?:/)
+      # wot about git, svn/svn+ssh, etc? should we just check for a scheme at all?
+      log("\tthis is not an http(s) url: ignoring");
+    end
+  end
+
 end
   
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/update-external-repo.sh	Mon Mar 28 18:17:06 2011 +0100
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+mirrordir="/var/mirror"
+logfile="/var/www/test-cannam/log/update-external-repo.log"
+
+project="$1"
+local_repo="$2"
+remote_repo="$3"
+
+if [ -z "$project" ] || [ -z "$local_repo" ] || [ -z "$remote_repo" ]; then
+    echo "Usage: $0 <project> <local-repo-path> <remote-repo-url>"
+    exit 2
+fi
+
+  # We need to handle different source repository types separately.
+  # 
+  # The convert extension cannot convert directly from a remote git
+  # repo; we'd have to mirror to a local repo first.  Incremental
+  # conversions do work though.  The hg-git plugin will convert
+  # directly from remote repositories, but not via all schemes
+  # (e.g. https is not currently supported).  It's probably easier to
+  # use git itself to clone locally and then convert or hg-git from
+  # there.
+  # 
+  # We can of course convert directly from remote Subversion repos,
+  # but we need to keep track of that -- you can ask to convert into a
+  # repo that has already been used (for Mercurial) and it'll do so
+  # happily; we don't want that.
+  #
+  # Converting from a remote Hg repo should be fine!
+  #
+  # One other thing -- we can't actually tell the difference between
+  # the various SCM types based on URL alone.  We have to try them
+  # (ideally in an order determined by a guess based on the URL) and
+  # see what happens.
+
+project_mirror="$mirrordir/$project"
+mkdir -p "$project_mirror"
+project_repo_mirror="$project_mirror/repo"
+
+  # Some test URLs:
+  # 
+  # http://aimc.googlecode.com/svn/trunk/
+  # http://aimc.googlecode.com/svn/
+  # http://vagar.org/git/flam
+  # https://github.com/wslihgt/IMMF0salience.git
+  # http://hg.breakfastquay.com/dssi-vst/
+  # git://github.com/schacon/hg-git.git
+  # http://svn.drobilla.net/lad (externals!)
+
+# If we are importing from another distributed system, then our aim is
+# to create either a Hg repo or a git repo at $project_mirror, which
+# we can then pull from directly to the Hg repo at $local_repo (using
+# hg-git, in the case of a git repo).
+
+# Importing from SVN, we should use hg convert directly to the target
+# hg repo (or should we?) but keep a record of the last changeset ID
+# we brought in, and test each time whether it matches the last
+# changeset ID actually in the repo
+
+success=""
+
+if [ -d "$project_repo_mirror" ]; then
+
+    # Repo mirror exists: update it
+    echo "$$: Mirror for project $project exists at $project_repo_mirror, updating" 1>&2
+
+    if [ -d "$project_repo_mirror/.hg" ]; then
+	hg --config extensions.convert= convert --datesort "$remote_repo" "$project_repo_mirror" && success=true
+    elif [ -d "$project_repo_mirror/.git" ]; then
+	( cd "$project_repo_mirror" && git fetch "$remote_repo" ) && success=true
+    else 
+	echo "$$: ERROR: Repo mirror dir $project_repo_mirror exists but is not an Hg or git repo" 1>&2
+    fi
+
+else
+
+    # Repo mirror does not exist yet
+    echo "$$: Mirror for project $project does not yet exist at $project_repo_mirror, trying to convert or clone" 1>&2
+
+    case "$remote_repo" in
+	*git*) 
+	    git clone "$remote_repo" "$project_repo_mirror" ||
+	    hg --config extensions.convert= convert --datesort "$remote_repo" "$project_repo_mirror"
+	    ;;
+	*)
+	    hg --config extensions.convert= convert --datesort "$remote_repo" "$project_repo_mirror" ||
+	    git clone "$remote_repo" "$project_repo_mirror" ||
+	    hg clone "$remote_repo" "$project_repo_mirror"
+	    ;;
+    esac && success=true
+
+fi
+	
+echo "Success=$success"
+
+if [ -n "$success" ]; then
+    echo "$$: Update successful, pulling into local repo at $local_repo"
+    if [ -d "$project_repo_mirror/.git" ]; then
+	if [ ! -d "$local_repo" ]; then
+	    hg init "$local_repo"
+	fi
+	( cd "$local_repo" && hg --config extensions.hgext.git= pull "$project_repo_mirror" )
+    else 
+	( cd "$local_repo" && hg pull "$project_repo_mirror" )
+    fi
+fi
--- a/public/stylesheets/application.css	Thu Mar 24 13:59:03 2011 +0000
+++ b/public/stylesheets/application.css	Mon Mar 28 18:17:06 2011 +0100
@@ -157,7 +157,7 @@
 tr.changeset td.committed_on { text-align: center; width: 15%; }
 
 table.files tr.file td { text-align: center; }
-table.files tr.file td.filename { text-align: left; padding-left: 24px; }
+table.files tr.file td.filename { text-align: left; }
 table.files tr.file td.digest { font-size: 80%; }
 
 table.members td.roles, table.memberships td.roles { width: 45%; }
--- a/public/themes/soundsoftware/stylesheets/application.css	Thu Mar 24 13:59:03 2011 +0000
+++ b/public/themes/soundsoftware/stylesheets/application.css	Mon Mar 28 18:17:06 2011 +0100
@@ -37,16 +37,41 @@
 
 body,p,h2,h3,h4,li,table,.wiki h1 { 
   font-family: DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; 
+  line-height: 1.34;
 }
 
 h2,h3,h4,.wiki h1 {
   color: #3e442c;
+  font-weight: bold;
+}
+
+.wiki h2,.wiki h3,.wiki h4 {
+  color: #000;
 }
 
 h2,.wiki h1 {
-    font-size: 1.8em;
+  font-size: 1.8em;
 }
 
+.wiki h2 {
+  margin-top: 1em;
+}
+
+.splitcontentleft p:first-child {
+  margin-top: 0;
+}
+
+div.attachments {
+  margin-top: 2em;
+}
+#wiki_add_attachment {
+  margin-top: 1.5em;
+}
+
+/* Hide these (the paragraph markers that show anchors) -- they confuse more than they help */
+a.wiki-anchor:hover { display: none; }
+h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: none; }
+
 .box {
     padding: 6px;
     margin-bottom: 10px;
@@ -81,6 +106,10 @@
 
 ul.projects .public, ul.projects .private { padding-left: 0.5em; color: #3e442c; font-size: 0.95em }
 
+table.files tr.active td { padding-top: 0.5em; padding-bottom: 0.5em; }
+table.files .file .active { font-weight: bold; }
+table.files .file .description { font-weight: normal; color: #3e442c; }
+
 #top-menu { position: absolute; top: 0; z-index: 1; left: 0px; width: 100%; font-size: 90%; /* height: 2em; */ margin: 0; padding: 0; padding-top: 0.5em; background-color: #3e442c; }
 #top-menu ul { margin-left: 10px; }
 #top-menu a { font-weight: bold; }
@@ -98,7 +127,7 @@
 #project-jump-box { float: right;  margin-right: 6px; margin-top: 5px; color: #000; }
 #project-ancestors-title {
     margin-bottom: 0px;
-    margin-left: 10px;
+    margin-left: 12px;
     margin-top: 6px;
     font-family: GilliusADFNo2, 'Gill Sans', Tahoma, sans-serif;
     font-weight: normal;
--- a/vendor/plugins/redmine_checkout/app/views/redmine_checkout_hooks/_view_repositories_show_contextual.rhtml	Thu Mar 24 13:59:03 2011 +0000
+++ b/vendor/plugins/redmine_checkout/app/views/redmine_checkout_hooks/_view_repositories_show_contextual.rhtml	Mon Mar 28 18:17:06 2011 +0100
@@ -26,6 +26,10 @@
     <% end %>
   </div>
   <% end%>
+  <% if repository.is_external? %>
+    <div class="bottomline"></div>
+    <p><%= l(:text_repository_external, :location => repository.external_url) %></p>
+  <% end %>
 </div>
 <div style="clear: left"></div>
 
@@ -33,4 +37,4 @@
   <%= stylesheet_link_tag 'checkout', :plugin => 'redmine_checkout' %>
   <%= javascript_include_tag 'checkout', :plugin => 'redmine_checkout' %>
   <%= (javascript_include_tag 'ZeroClipboard', :plugin => 'redmine_checkout') if Setting.checkout_use_zero_clipboard? %>
-<% end %>
\ No newline at end of file
+<% end %>