# HG changeset patch # User luisf # Date 1301332626 -3600 # Node ID f7c525dc7585f133ffb81854fb1133760b66e5b7 # Parent e941e6aa2b7c63f46a7448c1008e62380c1bdfc5# Parent 2c9d65eff2528185a9df66276d3e40ef18f34e32 Merge from branch "live" diff -r e941e6aa2b7c -r f7c525dc7585 app/controllers/application_controller.rb --- 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 diff -r e941e6aa2b7c -r f7c525dc7585 app/controllers/attachments_controller.rb --- 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? diff -r e941e6aa2b7c -r f7c525dc7585 app/controllers/files_controller.rb --- 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 diff -r e941e6aa2b7c -r f7c525dc7585 app/controllers/sys_controller.rb --- 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 diff -r e941e6aa2b7c -r f7c525dc7585 app/helpers/repositories_helper.rb --- 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| diff -r e941e6aa2b7c -r f7c525dc7585 app/views/attachments/toggle_active.rhtml --- /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 +%> diff -r e941e6aa2b7c -r f7c525dc7585 app/views/files/index.html.erb --- 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 @@

<%=l(:label_attachment_plural)%>

<% delete_allowed = User.current.allowed_to?(:manage_files, @project) %> +<% active_change_allowed = delete_allowed %> + <%= 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') %> +<% have_file = false %> <% @containers.each do |container| %> <% next if container.attachments.empty? -%> <% if container.is_a?(Version) -%> - <% end -%> <% container.attachments.each do |file| %> - "> - + <%= "active" if file.active? %>"> + + <% if file.active? %> + + <% else %> + @@ -43,4 +65,6 @@
MD5
+ <%= link_to(h(container), {:controller => 'versions', :action => 'show', :id => container}, :class => "icon icon-package") %>
<%= link_to_attachment file, :download => true, :title => file.description %>
+ <% have_file = true %> + <% if active_change_allowed + 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 + %> +
+ <% else -%> + <%= image_tag('fav.png') if file.active? %> + <% end -%> +
<%= link_to_attachment file, :download => true %>
<%= h(file.description) %>
<%= link_to_attachment file, :download => true, :title => file.description %> + <% end %> + <%= format_time(file.created_on) %> <%= number_to_human_size(file.filesize) %> <%= file.downloads %>
+<%= l(:text_files_active_change) if active_change_allowed and have_file %> + <% html_title(l(:label_attachment_plural)) -%> diff -r e941e6aa2b7c -r f7c525dc7585 app/views/projects/settings/_repository.rhtml --- 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 @@
+

+<% if @repository %> + <%= l(:text_settings_repo_explanation) %> + <% if @repository.is_external %> +

<%= l(:text_settings_repo_is_external) %> + <% else %> +

<%= l(:text_settings_repo_is_internal) %> + <% end %> +

-<% if @repository %> - <% if @repository.is_external %> - <%= l(:text_settings_repo_is_external) %> - <% else %> - <%= l(:text_settings_repo_is_internal) %> -<% end %> - <% else %> - <%= l(:text_settings_repo_creation) %> -<% end %> - @@ -28,16 +27,17 @@

<%= label_tag('repository_is_external', l(:label_is_external_repository)) %> <%= check_box :repository, :is_external, :onclick => "toggle_ext_url()" %> - <%= l(:setting_external_repository) %> +
<%= l(:setting_external_repository) %>

<%= 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) %> +
<%= l(:setting_external_repository_url) %>

+

<%= l(:text_settings_repo_need_help) %>

@@ -48,4 +48,9 @@ <%= 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) %> <% end %> + +<% end %> diff -r e941e6aa2b7c -r f7c525dc7585 config/locales/en-GB.yml --- 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.
This will be used in all project-related URLs, and as the repository name. Once saved, the identifier can not 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.
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.
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.
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 diff -r e941e6aa2b7c -r f7c525dc7585 config/locales/en.yml --- 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.
This will be used in all project-related URLs, and as the repository name. Once saved, the identifier can not 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.
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.
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.
You should not have to adjust any settings here.
Please check again in ten minutes, and contact us 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:
Click the star to switch active status for a download on or off.
Active files will be shown more prominently in the download page. + text_settings_repo_creation: Creating repository...

The source code repository for a project will be set up automatically within a few minutes of the project being created.

Please check again in five minutes, and contact us if there is any problem.

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.

If you don't want a repository at all, go to the Modules tab and switch it off there. + text_settings_repo_explanation: External repositories

Normally your project's primary repository will be the Mercurial repository hosted at this site.

However, if you already have your project hosted somewhere else, you can specify your existing external repository's URL here – then this site will track that repository in a read-only “mirror” 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 contact us if you need help deciding how best to set this up.
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 {{location}}
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}}" diff -r e941e6aa2b7c -r f7c525dc7585 config/routes.rb --- 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 diff -r e941e6aa2b7c -r f7c525dc7585 db/migrate/20110303152903_add_active_column_to_attachments.rb --- /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 diff -r e941e6aa2b7c -r f7c525dc7585 extra/soundsoftware/SoundSoftware-salted.pm --- /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/" + + + 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/" + + +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; diff -r e941e6aa2b7c -r f7c525dc7585 extra/soundsoftware/SoundSoftware.pm --- 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; diff -r e941e6aa2b7c -r f7c525dc7585 extra/soundsoftware/convert-external-repos.rb --- /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 + diff -r e941e6aa2b7c -r f7c525dc7585 extra/soundsoftware/reposman-soundsoftware.rb --- 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 diff -r e941e6aa2b7c -r f7c525dc7585 extra/soundsoftware/update-external-repo.sh --- /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 " + 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 diff -r e941e6aa2b7c -r f7c525dc7585 public/stylesheets/application.css --- 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%; } diff -r e941e6aa2b7c -r f7c525dc7585 public/themes/soundsoftware/stylesheets/application.css --- 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; diff -r e941e6aa2b7c -r f7c525dc7585 vendor/plugins/redmine_checkout/app/views/redmine_checkout_hooks/_view_repositories_show_contextual.rhtml --- 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 %> <% end%> + <% if repository.is_external? %> +

+

<%= l(:text_repository_external, :location => repository.external_url) %>

+ <% end %>
@@ -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 %>