changeset 1520:a1bdbf8a87d5 redmine-2.5-integration

Merge from branch "redmine-2.4-integration"
author Chris Cannam
date Tue, 09 Sep 2014 10:02:18 +0100
parents dffacf8a6908 (current diff) afce8026aaeb (diff)
children 2354ddf5a483
files Gemfile app/controllers/account_controller.rb app/controllers/application_controller.rb app/controllers/issues_controller.rb app/controllers/members_controller.rb app/controllers/my_controller.rb app/controllers/projects_controller.rb app/controllers/repositories_controller.rb app/controllers/users_controller.rb app/helpers/application_helper.rb app/helpers/projects_helper.rb app/helpers/repositories_helper.rb app/models/mailer.rb app/models/news.rb app/models/project.rb app/models/repository.rb app/models/repository/bazaar.rb app/models/repository/cvs.rb app/models/repository/darcs.rb app/models/repository/git.rb app/models/repository/mercurial.rb app/models/repository/subversion.rb app/models/user.rb app/views/account/register.html.erb app/views/my/account.html.erb app/views/projects/_form.html.erb app/views/projects/index.html.erb app/views/projects/new.html.erb app/views/repositories/_dir_list_content.html.erb app/views/repositories/revision.html.erb app/views/search/index.html.erb app/views/settings/_general.html.erb app/views/users/_form.html.erb config/locales/bg.yml config/locales/bs.yml config/locales/ca.yml config/locales/cs.yml config/locales/da.yml config/locales/de.yml config/locales/el.yml config/locales/en-GB.yml config/locales/en.yml config/locales/es.yml config/locales/eu.yml config/locales/fi.yml config/locales/fr.yml config/locales/gl.yml config/locales/he.yml config/locales/hr.yml config/locales/hu.yml config/locales/id.yml config/locales/it.yml config/locales/ja.yml config/locales/ko.yml config/locales/lt.yml config/locales/lv.yml config/locales/mk.yml config/locales/mn.yml config/locales/nl.yml config/locales/no.yml config/locales/pl.yml config/locales/pt-BR.yml config/locales/pt.yml config/locales/ro.yml config/locales/ru.yml config/locales/sk.yml config/locales/sl.yml config/locales/sr-YU.yml config/locales/sr.yml config/locales/sv.yml config/locales/th.yml config/locales/tr.yml config/locales/uk.yml config/locales/vi.yml config/locales/zh-TW.yml config/locales/zh.yml config/settings.yml files/delete.me lib/redmine.rb lib/redmine/scm/adapters/abstract_adapter.rb lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl lib/redmine/scm/adapters/mercurial_adapter.rb log/delete.me public/javascripts/application.js public/stylesheets/application.css test/unit/wiki_test.rb
diffstat 410 files changed, 17982 insertions(+), 1342 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Tue Sep 09 09:29:00 2014 +0100
+++ b/.hgignore	Tue Sep 09 10:02:18 2014 +0100
@@ -32,7 +32,10 @@
 vendor/rails
 *.rbc
 .git/
+*~
+public/themes/soundsoftware/stylesheets/fonts/*
 .bundle
 Gemfile.lock
 Gemfile.local
 
+re:^config\.ru$
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgtags	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,2 @@
+202986dd17e4e02b3622b65cf38f358b67a393e8 bibliography_plugin_alpha
+cf4cc816278aa7cc8f39a465f52304f6e91dd34a last_rails2_version
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.svn/all-wcprops	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 38
+/svn/!svn/ver/6261/branches/1.2-stable
+END
--- a/Gemfile	Tue Sep 09 09:29:00 2014 +0100
+++ b/Gemfile	Tue Sep 09 10:02:18 2014 +0100
@@ -9,6 +9,10 @@
 gem "mime-types"
 gem "awesome_nested_set", "2.1.6"
 
+#cc -- CiteProc v1.0.0 broke our citations (CiteProc.process returns nil).
+# Until I've managed to work out what's up and fix that...
+gem "citeproc", "0.0.6"
+
 # Optional gem for LDAP authentication
 group :ldap do
   gem "net-ldap", "~> 0.3.1"
--- a/app/controllers/account_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/account_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -112,15 +112,21 @@
   # User self-registration
   def register
     (redirect_to(home_url); return) unless Setting.self_registration? || session[:auth_source_registration]
+
     if request.get?
       session[:auth_source_registration] = nil
-      @user = User.new(:language => current_language.to_s)
+      @user = User.new(:language => Setting.default_language)
+
+      @ssamr_user_details = SsamrUserDetail.new
+
     else
       user_params = params[:user] || {}
       @user = User.new
       @user.safe_attributes = user_params
       @user.admin = false
+                  
       @user.register
+      
       if session[:auth_source_registration]
         @user.activate
         @user.login = session[:auth_source_registration][:login]
@@ -137,6 +143,13 @@
           @user.password, @user.password_confirmation = user_params[:password], user_params[:password_confirmation]
         end
 
+        @ssamr_user_details = SsamrUserDetail.new(params[:ssamr_user_details])
+
+        # associates the 2 objects
+        @user.ssamr_user_detail = @ssamr_user_details
+        @selected_institution_id = params[:ssamr_user_details][:institution_id].to_i
+
+        
         case Setting.self_registration
         when '1'
           register_by_email_activation(@user)
@@ -318,6 +331,9 @@
   # Pass a block for behavior when a user fails to save
   def register_manually_by_administrator(user, &block)
     if user.save
+
+       @ssamr_user_details.save!
+
       # Sends an email to the administrators
       Mailer.account_activation_request(user).deliver
       account_pending(user)
--- a/app/controllers/activities_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/activities_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -40,7 +40,22 @@
 
     events = @activity.events(@date_from, @date_to)
 
-    if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, events.first, events.size, User.current, current_language])
+    @institution_name = params[:institution]
+    if !@institution_name.blank?
+      events = events.select do |e|
+        e.respond_to?(:event_author) and e.event_author and
+	  e.event_author.respond_to?(:ssamr_user_detail) and
+          !e.event_author.ssamr_user_detail.nil? and
+          e.event_author.ssamr_user_detail.institution_name == @institution_name
+      end
+      if events.empty?
+        # We don't want to dump into the output any arbitrary string
+        # from the URL that has no matching events
+        @institution_name = ""
+      end
+    end
+
+    if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, @institution_name, events.first, events.size, User.current, current_language])
       respond_to do |format|
         format.html {
           @events_by_day = events.group_by {|event| User.current.time_to_date(event.event_datetime)}
--- a/app/controllers/application_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/application_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -275,14 +275,14 @@
   def find_project
     @project = Project.find(params[:id])
   rescue ActiveRecord::RecordNotFound
-    render_404
+    User.current.logged? ? render_404 : require_login
   end
 
   # Find project of id params[:project_id]
   def find_project_by_project_id
     @project = Project.find(params[:project_id])
   rescue ActiveRecord::RecordNotFound
-    render_404
+    User.current.logged? ? render_404 : require_login
   end
 
   # Find a project based on params[:project_id]
@@ -377,6 +377,21 @@
   def redirect_back_or_default(default, options={})
     back_url = params[:back_url].to_s
     if back_url.present? && valid_back_url?(back_url)
+          # soundsoftware: if back_url is the home page,
+          # change it to My Page (#125)
+          if (uri.path == home_path)
+            if (uri.path =~ /\/$/)
+              uri.path = uri.path + "my"
+            else
+              uri.path = uri.path + "/my"
+            end
+          end
+          # 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"
+          end
+          back_url = uri.to_s
       redirect_to(back_url)
       return
     elsif options[:referer]
--- a/app/controllers/attachments_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/attachments_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -20,6 +20,10 @@
   before_filter :file_readable, :read_authorize, :only => [:show, :download, :thumbnail]
   before_filter :delete_authorize, :only => :destroy
   before_filter :authorize_global, :only => :upload
+  before_filter :active_authorize, :only => :toggle_active
+
+  include AttachmentsHelper
+  helper :attachments
 
   accept_api_auth :show, :download, :upload
 
@@ -48,7 +52,9 @@
   end
 
   def download
-    if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
+    # cc: formerly this happened only if "@attachment.container.is_a?(Version)"
+    # or Project. Not good for us, we want to tally all downloads [by humans]
+    if not user_is_search_bot?
       @attachment.increment_download
     end
 
@@ -116,6 +122,14 @@
     end
   end
 
+  def toggle_active
+    @attachment.active = !@attachment.active?
+    @attachment.save!
+    respond_to do |format|
+      format.js
+    end
+  end
+
 private
   def find_project
     @attachment = Attachment.find(params[:id])
@@ -144,6 +158,10 @@
     @attachment.deletable? ? true : deny_access
   end
 
+  def active_authorize
+    true
+  end
+
   def detect_content_type(attachment)
     content_type = attachment.content_type
     if content_type.blank?
--- a/app/controllers/files_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/files_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -25,8 +25,9 @@
   include SortHelper
 
   def index
-    sort_init 'filename', 'asc'
+    sort_init 'active', 'desc'
     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"
@@ -50,4 +51,5 @@
     end
     redirect_to project_files_path(@project)
   end
+
 end
--- a/app/controllers/issues_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/issues_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -148,7 +148,16 @@
     call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
     @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
     if @issue.save
+      
       call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
+
+      # Also adds the assignee to the watcher's list
+      if params[:issue][:assigned_to_id] && !params[:issue][:assigned_to_id].empty?
+       unless @issue.watcher_ids.include?(params[:issue][:assigned_to_id])
+         @issue.add_watcher(User.find(params[:issue][:assigned_to_id]))
+       end
+      end
+
       respond_to do |format|
         format.html {
           render_attachment_warning_if_needed(@issue)
@@ -392,10 +401,22 @@
         return false
       end
     end
+
+    # tests if the the user assigned_to_id
+    # is in this issues watcher's list
+    # if not, adds it.
+
+    if params[:issue] && params[:issue][:assigned_to_id] && !params[:issue][:assigned_to_id].empty?
+     unless @issue.watched_by?(User.find(params[:issue][:assigned_to_id]))
+       @issue.add_watcher(User.find(params[:issue][:assigned_to_id]))
+     end
+    end
+
     @issue.safe_attributes = issue_attributes
     @priorities = IssuePriority.active
     @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
     true
+
   end
 
   # TODO: Refactor, lots of extra code in here
--- a/app/controllers/members_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/members_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -17,6 +17,7 @@
 
 class MembersController < ApplicationController
   model_object Member
+  menu_item :members
   before_filter :find_model_object, :except => [:index, :create, :autocomplete]
   before_filter :find_project_from_association, :except => [:index, :create, :autocomplete]
   before_filter :find_project_by_project_id, :only => [:index, :create, :autocomplete]
@@ -53,12 +54,22 @@
         attrs = params[:membership].dup
         user_ids = attrs.delete(:user_ids)
         user_ids.each do |user_id|
-          members << Member.new(:role_ids => params[:membership][:role_ids], :user_id => user_id)
+          @new_member = Member.new(:role_ids => params[:membership][:role_ids], :user_id => user_id)
+          members << @new_member
+
+          # send notification to member
+          Mailer.member_added_to_project(@new_member, @project).deliver
         end
       else
-        members << Member.new(:role_ids => params[:membership][:role_ids], :user_id => params[:membership][:user_id])
+        @new_member = Member.new(:role_ids => params[:membership][:role_ids], :user_id => params[:membership][:user_id])
+        members << @new_member
+        
+        # send notification to member
+        Mailer.member_added_to_project(@new_member, @project).deliver
       end
+
       @project.members << members
+
     end
 
     respond_to do |format|
@@ -111,9 +122,9 @@
   end
 
   def autocomplete
-    respond_to do |format|
-      format.js
-    end
+    @principals = Principal.active.not_member_of(@project).like(params[:q]).all(:limit => 100)
+    logger.debug "Query for #{params[:q]} returned #{@principals.size} results"
+    render :layout => false
   end
 
   private
--- a/app/controllers/my_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/my_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -23,18 +23,24 @@
   helper :issues
   helper :users
   helper :custom_fields
+  helper :projects
+  helper :activities
 
   BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
              'issuesreportedbyme' => :label_reported_issues,
              'issueswatched' => :label_watched_issues,
+             'activitymyprojects' => :label_activity_my_recent,
              'news' => :label_news_latest,
+             'tipoftheday' => :label_tipoftheday,
              'calendar' => :label_calendar,
              'documents' => :label_document_plural,
-             'timelog' => :label_spent_time
+             'timelog' => :label_spent_time,
+             'myprojects' => :label_my_projects,
+             'colleagues' => :label_my_colleagues
            }.merge(Redmine::Views::MyPage::Block.additional_blocks).freeze
 
-  DEFAULT_LAYOUT = {  'left' => ['issuesassignedtome'],
-                      'right' => ['issuesreportedbyme']
+  DEFAULT_LAYOUT = {  'left' => ['myprojects', 'activitymyprojects'], 
+                      'right' => ['colleagues', 'tipoftheday', 'issueswatched'] 
                    }.freeze
 
   def index
@@ -52,9 +58,40 @@
   def account
     @user = User.current
     @pref = @user.pref
+    @ssamr_user_details = @user.ssamr_user_detail
+    
+    
+    if @user.ssamr_user_detail == nil
+       @selected_institution_id = nil
+     else
+       @selected_institution_id = @ssamr_user_details.institution_id.to_i
+     end    
+    
     if request.post?
       @user.safe_attributes = params[:user]
       @user.pref.attributes = params[:pref]
+
+      @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
+
+      if @user.ssamr_user_detail == nil
+        @ssamr_user_details = SsamrUserDetail.new()
+        @user.ssamr_user_detail = @ssamr_user_details
+      else
+        @ssamr_user_details = @user.ssamr_user_detail
+      end
+
+      if params[:ssamr_user_details].nil? or params[:ssamr_user_details].empty?
+        @ssamr_user_details.description = @user.ssamr_user_detail.description
+        @ssamr_user_details.institution_id = @user.ssamr_user_detail.institution_id
+        @institution_type = @ssamr_user_details.institution_type
+        @other_institution = @ssamr_user_details.other_institution
+      else
+        @ssamr_user_details.description = params[:ssamr_user_details][:description]
+        @ssamr_user_details.institution_id = params[:ssamr_user_details][:institution_id]
+        @ssamr_user_details.institution_type = params[:ssamr_user_details][:institution_type]
+        @ssamr_user_details.other_institution = params[:ssamr_user_details][:other_institution]
+      end
+                  
       if @user.save
         @user.pref.save
         set_language_if_valid @user.language
--- a/app/controllers/projects_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/projects_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -20,8 +20,8 @@
   menu_item :roadmap, :only => :roadmap
   menu_item :settings, :only => :settings
 
-  before_filter :find_project, :except => [ :index, :list, :new, :create, :copy ]
-  before_filter :authorize, :except => [ :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy]
+  before_filter :find_project, :except => [ :index, :list, :explore, :new, :create, :copy ]
+  before_filter :authorize, :except => [ :index, :list, :explore, :new, :create, :copy, :archive, :unarchive, :destroy]
   before_filter :authorize_global, :only => [:new, :create]
   before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
   accept_rss_auth :index
@@ -44,16 +44,29 @@
   include RepositoriesHelper
   include ProjectsHelper
   helper :members
+  include ActivitiesHelper
+  helper :activities
 
-  # Lists visible projects
+  # Lists visible projects. Paginator is for top-level projects only
+  # (subprojects belong to them)
   def index
     respond_to do |format|
       format.html {
-        scope = Project
-        unless params[:closed]
-          scope = scope.active
-        end
-        @projects = scope.visible.order('lft').all
+        sort_init 'name'
+        sort_update %w(name lft created_on updated_on)
+        @limit = per_page_option
+        @project_count = Project.visible_roots.count
+        @project_pages = Paginator.new self, @project_count, @limit, params['page']
+        @offset ||= @project_pages.current.offset
+        @projects = Project.visible_roots.all(:offset => @offset, :limit => @limit, :order => sort_clause)
+        render :template => 'projects/index', :layout => !request.xhr?
+
+## Redmine 2.2:
+#        scope = Project
+#        unless params[:closed]
+#          scope = scope.active
+#        end
+#        @projects = scope.visible.order('lft').all
       }
       format.api  {
         @offset, @limit = api_offset_and_limit
@@ -67,6 +80,16 @@
     end
   end
 
+  # A different view of projects using explore boxes
+  def explore
+    respond_to do |format|
+      format.html {
+        @projects = Project.visible
+        render :template => 'projects/explore', :layout => !request.xhr?
+      }
+    end
+  end
+
   def new
     @issue_custom_fields = IssueCustomField.sorted.all
     @trackers = Tracker.sorted.all
@@ -80,7 +103,7 @@
     @project = Project.new
     @project.safe_attributes = params[:project]
 
-    if validate_parent_id && @project.save
+    if validate_is_public_key && validate_parent_id && @project.save
       @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
       # Add current user as a project member if current user is not admin
       unless User.current.admin?
@@ -171,6 +194,7 @@
     @issue_category ||= IssueCategory.new
     @member ||= @project.members.new
     @trackers = Tracker.sorted.all
+    @repository ||= @project.repository
     @wiki ||= @project.wiki
   end
 
@@ -199,6 +223,14 @@
     end
   end
 
+  def overview
+    @project.has_welcome_page = params[:has_welcome_page]
+    if @project.save
+      flash[:notice] = l(:notice_successful_update)
+    end
+    redirect_to :action => 'settings', :id => @project, :tab => 'overview'
+  end
+
   def modules
     @project.enabled_module_names = params[:enabled_module_names]
     flash[:notice] = l(:notice_successful_update)
@@ -245,6 +277,19 @@
 
   private
 
+  def validate_is_public_key
+    # Although is_public isn't mandatory in the project model (it gets
+    # defaulted), it must be present in params -- it can be true or
+    # false, but it must be there. This permits us to make forms in
+    # which the user _has_ to select public or private (rather than
+    # defaulting it) if we want to
+    if params.nil? || params[:project].nil? || !params[:project].has_key?(:is_public)
+      @project.errors.add :is_public, :public_or_private
+      return false
+    end
+    true
+  end
+
   # Validates parent_id param according to user's permissions
   # TODO: move it to Project model in a validation that depends on User.current
   def validate_parent_id
--- a/app/controllers/repositories_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/repositories_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -63,6 +63,7 @@
   end
 
   def update
+    params[:repository_scm]='Mercurial'
     attrs = pickup_extra_info
     @repository.safe_attributes = attrs[:attrs]
     if attrs[:attrs_extra].keys.any?
@@ -170,9 +171,7 @@
 
     @content = @repository.cat(@path, @rev)
     (show_error_not_found; return) unless @content
-    if is_raw ||
-         (@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
-         ! is_entry_text_data?(@content, @path)
+    if is_raw
       # Force the download
       send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
       send_type = Redmine::MimeType.of(@path)
@@ -180,6 +179,7 @@
       send_opt[:disposition] = (Redmine::MimeType.is_type?('image', @path) && !is_raw ? 'inline' : 'attachment')
       send_data @content, send_opt
     else
+      @display_raw = ((@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) || !is_entry_text_data?(@content, @path))
       # Prevent empty lines when displaying a file with Windows style eol
       # TODO: UTF-16
       # Is this needs? AttachmentsController reads file simply.
--- a/app/controllers/search_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/search_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -66,6 +66,7 @@
       # no more than 5 tokens to search for
       @tokens.slice! 5..-1 if @tokens.size > 5
 
+      @project_matches = []
       @results = []
       @results_by_type = Hash.new {|h,k| h[k] = 0}
 
@@ -79,6 +80,12 @@
           :before => params[:previous].nil?)
         @results += r
         @results_by_type[s] += c
+        if s == 'projects'
+          r, c = s.singularize.camelcase.constantize.search(@tokens, nil,
+                                                            :all_words => @all_words,
+                                                            :titles_only => 1)
+          @project_matches += r
+        end
       end
       @results = @results.sort {|a,b| b.event_datetime <=> a.event_datetime}
       if params[:previous].nil?
--- a/app/controllers/sys_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/sys_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -23,7 +23,7 @@
     # extra_info attribute from repository breaks activeresource client
     render :xml => p.to_xml(
                        :only => [:id, :identifier, :name, :is_public, :status],
-                       :include => {:repository => {:only => [:id, :url]}}
+                       :include => {:repository => {:only => [:id, :url, :is_external, :external_url]}}
                      )
   end
 
@@ -68,6 +68,55 @@
     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 clear_repository_cache
+    project = Project.find(params[:id])
+    if project.repository
+      project.repository.clear_cache
+    end
+    render :nothing => true, :status => 200
+  rescue ActiveRecord::RecordNotFound
+    render :nothing => true, :status => 404
+  end
+  
+  def set_embedded_active
+    project = Project.find(params[:id])
+    mods = project.enabled_modules
+    enable = (params[:enable] == "1")
+    if mods.detect {|m| m.name == "redmine_embedded"}
+      logger.info "Project #{project.name} currently has Embedded enabled"
+      if !enable
+        logger.info "Disabling Embedded"
+        modnames = mods.all(:select => :name).collect{|m| m.name}.reject{|n| n == "redmine_embedded"}
+        project.enabled_module_names = modnames
+      end
+    else
+      logger.info "Project #{project.name} currently has Embedded disabled"
+      if enable
+        logger.info "Enabling Embedded"
+        modnames = mods.all(:select => :name).collect{|m| m.name}
+        modnames << "redmine_embedded"
+        project.enabled_module_names = modnames
+      end
+    end
+    render :nothing => true, :status => 200
+  rescue ActiveRecord::RecordNotFound
+    render :nothing => true, :status => 404
+  end
+
   protected
 
   def check_enabled
--- a/app/controllers/users_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/users_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -59,6 +59,12 @@
   end
 
   def show
+
+    if @user.ssamr_user_detail != nil
+      @description = @user.ssamr_user_detail.description
+      @institution_name = @user.ssamr_user_detail.institution_name
+    end
+    
     # show projects based on current user visibility
     @memberships = @user.memberships.where(Project.visible_condition(User.current)).all
 
@@ -78,21 +84,34 @@
     end
   end
 
-  def new
+  def new      
     @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
     @user.safe_attributes = params[:user]
     @auth_sources = AuthSource.all
+    @ssamr_user_details = SsamrUserDetail.new
   end
 
   def create
     @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
     @user.safe_attributes = params[:user]
+    @user = User.new(params[:user])    
     @user.admin = params[:user][:admin] || false
     @user.login = params[:user][:login]
     @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
+    # TODO: Similar to My#account
     @user.pref.attributes = params[:pref]
+    @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
+
+    @ssamr_user_details = SsamrUserDetail.new(params[:ssamr_user_details])
+
+    # associates the 2 objects
+    @user.ssamr_user_detail = @ssamr_user_details
+
 
     if @user.save
+      @user.pref.save
+      @ssamr_user_details.save!
+
       Mailer.account_information(@user, @user.password).deliver if params[:send_information]
 
       respond_to do |format|
@@ -121,6 +140,15 @@
 
   def edit
     @auth_sources = AuthSource.all
+    
+    @ssamr_user_details = @user.ssamr_user_detail
+    
+    if @user.ssamr_user_detail == nil
+      @selected_institution_id = nil
+    else
+      @selected_institution_id = @user.ssamr_user_detail.institution_id.to_i    
+    end
+    
     @membership ||= Member.new
   end
 
@@ -136,6 +164,26 @@
     # TODO: Similar to My#account
     @user.pref.attributes = params[:pref]
 
+    if @user.ssamr_user_detail == nil
+      @ssamr_user_details = SsamrUserDetail.new()
+      @user.ssamr_user_detail = @ssamr_user_details
+    else
+      @ssamr_user_details = @user.ssamr_user_detail
+    end
+    
+    if params[:ssamr_user_details].nil? or params[:ssamr_user_details].empty?
+      @ssamr_user_details.description = @user.ssamr_user_detail.description
+      @ssamr_user_details.institution_id = @user.ssamr_user_detail.institution_id
+      @ssamr_user_details.other_institution = @user.ssamr_user_detail.other_institution
+      @ssamr_user_details.institution_type = @user.ssamr_user_detail.institution_type
+
+    else
+      @ssamr_user_details.description = params[:ssamr_user_details][:description]
+      @ssamr_user_details.institution_id = params[:ssamr_user_details][:institution_id]
+      @ssamr_user_details.other_institution = params[:ssamr_user_details][:other_institution]
+      @ssamr_user_details.institution_type = params[:ssamr_user_details][:institution_type]
+    end
+
     if @user.save
       @user.pref.save
 
--- a/app/controllers/welcome_controller.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/controllers/welcome_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -18,9 +18,21 @@
 class WelcomeController < ApplicationController
   caches_action :robots
 
+  include ProjectsHelper
+  helper :projects
+
   def index
-    @news = News.latest User.current
-    @projects = Project.latest User.current
+    @site_project = Project.find_by_identifier "soundsoftware-site"
+    @site_news = []
+    @site_news = News.latest_for(@site_project, 3) if @site_project
+    
+    # tests if user is logged in to generate the tips of the day list
+    if User.current.logged?
+      @tipsoftheday = Setting.tipoftheday_text
+    else
+      @tipsoftheday = ''
+    end
+    
   end
 
   def robots
--- a/app/helpers/activities_helper.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/helpers/activities_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -18,6 +18,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 module ActivitiesHelper
+
   def sort_activity_events(events)
     events_by_group = events.group_by(&:event_group)
     sorted_events = []
@@ -30,4 +31,151 @@
     end
     sorted_events
   end
+
+  def date_of_event(e)
+    if e.respond_to? :updated_at
+      e.updated_at
+    elsif e.respond_to? :updated_on
+      e.updated_on
+    elsif e.respond_to? :created_on
+      e.created_on
+    elsif e.respond_to? :committed_on
+      e.committed_on
+    else
+      nil 
+    end
+  end
+
+  def project_activity_on_events(events)
+
+    # Score each project for which there are any events, by giving
+    # each event a score based on how long ago it was (the more recent
+    # the better). Return a hash mapping project id to score.
+
+    projhash = Hash.new
+    
+    events.each do |e|
+      if e.respond_to?(:project)
+        p = e.project
+        d = date_of_event e
+        if !d.nil?
+          dd = Date.parse d.to_s
+          age = Date.today - dd
+          score = (age < 14 ? 15-age : 1)
+          if projhash.key? p
+            projhash[p] += score
+          else
+            projhash[p] = score
+          end
+        end
+      end
+    end
+    projhash
+  end
+
+  def projects_by_activity(user, count)
+
+    # Return up to count of the user's project ids ordered by that user's
+    # recent activity, omitting any projects for which no activity
+    # occurred in the recent past and any projects not visible to
+    # the current user
+
+    activity = Redmine::Activity::Fetcher.new(User.current, :author => user)
+
+    # Limit scope so as to exclude issues (which non-members can add)
+    activity.scope = [ "changesets", "files", "documents", "news", "wiki_edits", "messages", "time_entries", "publications" ]
+
+    days = Setting.activity_days_default.to_i
+    events = activity.events(Date.today - days, Date.today + 1)
+    projhash = project_activity_on_events(events)
+    projhash.keys.sort_by { |k| -projhash[k] }.first(count)
+  end
+
+  def render_active_colleagues(colleagues)
+
+    s = ""
+
+    start = Time.now
+
+    my_inst = ""
+    if ! User.current.ssamr_user_detail.nil?
+      my_inst = User.current.ssamr_user_detail.institution_name
+    end
+
+    actives = Hash.new
+    for c in colleagues
+      u = User.find_by_id(c)
+      active_projects = projects_by_activity(u, 3)
+      if !active_projects.empty?
+        actives[c] = active_projects
+      end
+    end
+
+    if actives.empty?
+      l(:label_no_active_colleagues)
+    else
+
+      s << "<dl>"
+      for c in actives.keys.sample(10)
+        u = User.find_by_id(c)
+        s << "<dt>"
+        s << avatar(u, :size => '24')
+        s << "<span class='user'>"
+        s << h(u.name)
+        s << "</span>"
+        if !u.ssamr_user_detail.nil?
+          inst = u.ssamr_user_detail.institution_name
+          if inst != "" and inst != my_inst
+            s << " - <span class='institution'>"
+            s << h(u.ssamr_user_detail.institution_name)
+            s << "</span>"
+          end
+        end
+        s << "</dt>"
+        s << "<dd>"
+        s << "<span class='active'>"
+        s << (actives[c].map { |p| link_to_project(p) }.join ", ")
+        s << "</span>"
+      end
+      s << "</dl>"
+
+      finish = Time.now
+      logger.info "render_active_colleagues: took #{finish-start}"
+    
+      s.html_safe
+    end
+  end
+
+  def busy_projects(events, count)
+
+    # Return a list of count projects randomly selected from amongst
+    # the busiest projects represented by the given activity events
+
+    projhash = project_activity_on_events(events)
+
+    # pick N highest values and use cutoff value as selection threshold
+    threshold = projhash.values.sort.last(count).first
+
+    # select projects above threshold and pick N from them randomly
+    busy = projhash.keys.select { |k| projhash[k] >= threshold }.sample(count)
+
+    # return projects rather than just ids
+    busy.map { |pid| Project.find(pid) }
+  end
+
+  def busy_institutions(events, count)
+    authors = events.map do |e|
+      e.event_author unless !e.respond_to?(:event_author) 
+    end.compact
+    institutions = authors.map do |a|
+      if a.respond_to?(:ssamr_user_detail) and !a.ssamr_user_detail.nil? and a.ssamr_user_detail.institution_name != "none"
+        a.ssamr_user_detail.institution_name
+      end
+    end
+    insthash = institutions.compact.sort.group_by { |i| i }
+    insthash = insthash.merge(insthash) { |k,v| v.length }
+    threshold = insthash.values.sort.last(count).first
+    insthash.keys.select { |k| insthash[k] >= threshold }.sample(count)
+  end
+
 end
--- a/app/helpers/application_helper.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/helpers/application_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -363,8 +363,15 @@
 
   def principals_check_box_tags(name, principals)
     s = ''
-    principals.each do |principal|
-      s << "<label>#{ check_box_tag name, principal.id, false, :id => nil } #{h principal}</label>\n"
+
+    principals.sort.each do |principal|
+
+      if principal.type == "User"
+        s << "<label>#{ check_box_tag name, principal.id, false, :id => nil } #{link_to_user principal}</label>\n"
+      else
+        s << "<label>#{ check_box_tag name, principal.id, false, :id => nil } #{h principal} (Group)</label>\n"
+      end
+
     end
     s.html_safe
   end
@@ -486,21 +493,28 @@
 
   def page_header_title
     if @project.nil? || @project.new_record?
-      h(Setting.app_title)
+      a = [h(Setting.app_title), '']
+
     else
+      pname = []
       b = []
       ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
       if ancestors.any?
         root = ancestors.shift
         b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
         if ancestors.size > 2
-          b << "\xe2\x80\xa6"
+          b << '&#8230;'
           ancestors = ancestors[-2, 2]
         end
         b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
+        b = b.join(' &#187; ').html_safe
+        b << (' &#187;'.html_safe)
       end
-      b << h(@project)
-      b.join(" \xc2\xbb ").html_safe
+
+      pname << h(@project)
+
+      a = [pname, b]
+
     end
   end
 
@@ -1314,6 +1328,17 @@
     '<meta name="robots" content="noindex,follow,noarchive" />'.html_safe
   end
 
+  def stylesheet_platform_font_tag
+    agent = request.env['HTTP_USER_AGENT']
+    name = 'fonts-generic'
+    if agent and agent =~ %r{Windows}
+      name = 'fonts-ms'
+    elsif agent and agent =~ %r{Macintosh}
+      name = 'fonts-mac'
+    end
+    stylesheet_link_tag name, :media => 'all'
+  end
+
   # Returns true if arg is expected in the API response
   def include_in_api_response?(arg)
     unless @included_in_api_response
--- a/app/helpers/attachments_helper.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/helpers/attachments_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -44,4 +44,11 @@
       api.created_on attachment.created_on
     end
   end
+
+  # Returns true if user agent appears (approximately) to be a search
+  # bot or crawler
+  def user_is_search_bot?
+    agent = request.env['HTTP_USER_AGENT']
+    agent and agent =~ /(bot|slurp|crawler|spider)\b/i
+  end
 end
--- a/app/helpers/my_helper.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/helpers/my_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -18,6 +18,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 module MyHelper
+
   def calendar_items(startdt, enddt)
     Issue.visible.
       where(:project_id => User.current.projects.map(&:id)).
@@ -26,6 +27,12 @@
       all
   end
 
+  def all_colleagues_of(user)
+    # Return a list of all user ids who have worked with the given user
+    # (on projects that are visible to the current user)
+    user.projects.select { |p| p.visible? }.map { |p| p.members.map { |m| m.user_id } }.flatten.sort.uniq.reject { |i| user.id == i }
+  end
+
   def documents_items
     Document.visible.order("#{Document.table_name}.created_on DESC").limit(10).all
   end
--- a/app/helpers/projects_helper.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/helpers/projects_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -25,8 +25,8 @@
 
   def project_settings_tabs
     tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
+            {:name => 'overview', :action => :edit_project, :partial => 'projects/settings/overview', :label => :label_welcome_page},
             {:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
-            {:name => 'members', :action => :manage_members, :partial => 'projects/settings/members', :label => :label_member_plural},
             {:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural},
             {:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural},
             {:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki},
@@ -60,17 +60,200 @@
     links.join(" | ").html_safe
   end
 
-  # Renders the projects index
+  def render_project_short_description(project)
+    s = ''
+    if (project.short_description)
+      s << "<div class='description'>"
+      s << textilizable(project.short_description, :project => project).gsub(/<[^>]+>/, '')
+      s << "</div>"
+    end
+    s.html_safe
+  end
+  
+  # Renders a tree of projects as a nested set of unordered lists
+  # The given collection may be a subset of the whole project tree
+  # (eg. some intermediate nodes are private and can not be seen)
   def render_project_hierarchy(projects)
-    render_project_nested_lists(projects) do |project|
-      s = link_to_project(project, {}, :class => "#{project.css_classes} #{User.current.member_of?(project) ? 'my-project' : nil}")
-      if project.description.present?
-        s << content_tag('div', textilizable(project.short_description, :project => project), :class => 'wiki description')
+    s = ''
+    if projects.any?
+      ancestors = []
+      original_project = @project
+      projects.each do |project|
+        # set the project environment to please macros.
+        @project = project
+        if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
+          s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
+        else
+          ancestors.pop
+          s << "</li>"
+          while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
+            ancestors.pop
+            s << "</ul></li>\n"
+          end
+        end
+        classes = (ancestors.empty? ? 'root' : 'child')
+        s << "<li class='#{classes}'><div class='#{classes}'>" +
+               link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
+        s << render_project_short_description(project)
+        s << "</div>\n"
+        ancestors << project
       end
-      s
     end
+    s.html_safe
   end
 
+
+  def render_my_project_in_hierarchy(project)
+ 
+    s = ''
+
+    if User.current.member_of?(project)
+
+      # set the project environment to please macros.
+      @project = project
+
+      classes = (project.root? ? 'root' : 'child')
+      
+      s << "<li class='#{classes}'><div class='#{classes}'>" +
+        link_to_project(project, {}, :class => "project my-project")
+      if project.is_public?
+        s << " <span class='public'>" << l(:field_is_public) << "</span>"
+      else
+        s << " <span class='private'>" << l(:field_is_private) << "</span>"
+      end
+      s << render_project_short_description(project)
+      s << "</div>\n"
+
+      cs = ''
+      project.children.each do |child|
+        cs << render_my_project_in_hierarchy(child)
+      end
+
+      if cs != ''
+        s << "<ul class='projects'>\n" << cs << "</ul>\n";
+      end
+
+    end
+
+    s
+
+  end
+
+  # Renders a tree of projects where the current user belongs
+  # as a nested set of unordered lists
+  # The given collection may be a subset of the whole project tree
+  # (eg. some intermediate nodes are private and can not be seen)
+  def render_my_project_hierarchy(projects)
+
+    s = ''
+
+    original_project = @project
+
+    projects.each do |project|
+      if project.root? || !projects.include?(project.parent)
+        s << render_my_project_in_hierarchy(project)
+      end
+    end
+
+    @project = original_project
+
+    if s != ''
+      a = ''
+      a << "<ul class='projects root'>\n"
+      a << s
+      a << "</ul>\n"
+      s = a
+    end
+
+    s.html_safe
+    
+  end
+
+  # Renders a tree of projects.  The given collection may be a subset
+  # of the whole project tree (eg. some intermediate nodes are private
+  # and can not be seen).  We are potentially interested in various
+  # things: the project name, description, manager(s), creation date,
+  # last activity date, general activity level, whether there is
+  # anything actually hosted here for the project, etc.
+  def render_project_table(projects)
+
+    s = ""
+    s << "<div class='autoscroll'>"
+    s << "<table class='list projects'>"
+    s << "<thead><tr>"
+    
+    s << sort_header_tag('name', :caption => l(:field_name))
+    s << "<th class='managers'>" << l(:label_managers) << "</th>"
+    s << sort_header_tag('created_on', :default_order => 'desc')
+    s << sort_header_tag('updated_on', :default_order => 'desc')
+
+    s << "</tr></thead><tbody>"
+
+    original_project = @project
+
+    projects.each do |project|
+      s << render_project_in_table(project, cycle('odd', 'even'), 0)
+    end
+
+    s << "</table>"
+
+    @project = original_project
+
+    s.html_safe
+  end
+
+
+  def render_project_in_table(project, oddeven, level)
+
+    # set the project environment to please macros.
+    @project = project
+
+    classes = (level == 0 ? 'root' : 'child')
+
+    s = ""
+    
+    s << "<tr class='#{oddeven} #{classes} level#{level}'>"
+    s << "<td class='firstcol' align=top><div class='name hosted_here"
+    s << " no_description" if project.description.blank?
+    s << "'>" << link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}");
+    s << "</div>"
+    s << render_project_short_description(project)
+      
+    s << "<td class='managers' align=top>"
+
+    u = project.users_by_role
+    if u
+      u.keys.each do |r|
+        if r.allowed_to?(:edit_project)
+          mgrs = []
+          u[r].sort.each do |m|
+            mgrs << link_to_user(m)
+          end
+          if mgrs.size < 3
+            s << '<nobr>' << mgrs.join(', ') << '</nobr>'
+          else
+            s << mgrs.join(', ')
+          end
+        end
+      end
+    end
+
+    s << "</td>"
+    s << "<td class='created_on' align=top>" << format_date(project.created_on) << "</td>"
+    s << "<td class='updated_on' align=top>" << format_date(project.updated_on) << "</td>"
+    
+    s << "</tr>"
+
+    project.children.each do |child|
+      if child.is_public? or User.current.member_of?(child)
+        s << render_project_in_table(child, oddeven, level + 1)
+      end
+    end
+    
+    s
+  end
+
+
   # Returns a set of options for a select field, grouped by project.
   def version_options_for_select(versions, selected=nil)
     grouped = Hash.new {|h,k| h[k] = []}
@@ -90,4 +273,72 @@
     sharing = 'none' unless Version::VERSION_SHARINGS.include?(sharing)
     l("label_version_sharing_#{sharing}")
   end
+
+  def score_maturity(project)
+    nr_changes = (project.repository.nil? ? 0 : project.repository.changesets.count)
+    downloadables = [project.attachments,
+                     project.versions.collect { |v| v.attachments },
+                     project.documents.collect { |d| d.attachments }].flatten
+    nr_downloadables = downloadables.count
+    nr_downloads = downloadables.map do |d| d.downloads end.sum
+    nr_members = project.members.count
+    nr_publications = if project.respond_to? :publications
+                      then project.publications.count else 0 end
+    Math.log(1 + nr_changes) +
+      Math.log(1 + nr_downloadables) +
+      Math.log(1 + nr_downloads) +
+      Math.sqrt(nr_members > 1 ? (nr_members - 1) : 0) +
+      Math.sqrt(nr_publications * 2)
+  end
+
+  def all_maturity_scores()
+    phash = Hash.new
+    pp = Project.visible(User.anonymous)
+    pp.each do |p| 
+      phash[p] = score_maturity p
+    end
+    phash
+  end
+
+  def top_level_maturity_scores()
+    phash = Hash.new
+    pp = Project.visible_roots(User.anonymous)
+    pp.each do |p| 
+      phash[p] = score_maturity p
+    end
+    phash
+  end
+
+  def mature_projects(count)
+    phash = all_maturity_scores
+    scores = phash.values.sort
+    threshold = scores[scores.length / 2]
+    if threshold == 0 then threshold = 1 end
+    phash.keys.select { |k| phash[k] > threshold }.sample(count)
+  end
+
+  def varied_mature_projects(count)
+    phash = top_level_maturity_scores
+    scores = phash.values.sort
+    threshold = scores[scores.length / 2]
+    if threshold == 0 then threshold = 1 end
+    mhash = Hash.new
+    phash.keys.shuffle.select do |k|
+      if phash[k] < threshold or k.description == "" then
+        false
+      else
+        u = k.users_by_role
+        mgrs = []
+        u.keys.each do |r|
+          if r.allowed_to?(:edit_project)
+            u[r].each { |m| mgrs << m }
+          end
+        end
+        novel = (mhash.keys & mgrs).empty?
+        mgrs.each { |m| mhash[m] = 1 }
+        novel and not mgrs.empty?
+      end
+    end.sample(count)
+  end
+
 end
--- a/app/helpers/repositories_helper.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/helpers/repositories_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -294,4 +294,24 @@
     end
     max_space
   end
+
+  # Generates a link to a downloadable archive for a revision
+  # Options:
+  # * :text - Link text (default to the formatted revision)
+  def link_to_revision_archive(repository, revision, project, options={})
+    method = repository.class.name.demodulize.underscore + "_link_to_revision_archive"
+    if repository.is_a?(Repository) &&
+        respond_to?(method) && method != 'link_to_revision_archive'
+      send(method, repository, revision, project, options)
+    end
+  end
+
+  def mercurial_link_to_revision_archive(repository, revision, project, options={})
+    text = options.delete(:text) || format_revision(revision)
+    rev = revision.respond_to?(:identifier) ? revision.identifier : revision
+    if rev.blank? then rev = 'tip' end
+    content_tag('a', h(text),
+        { :href => "/hg/#{project.identifier}/archive/#{rev}.zip" }.merge(options));
+  end
+
 end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/models/institution.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,2 @@
+class Institution < ActiveRecord::Base
+end
--- a/app/models/mailer.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/models/mailer.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -27,6 +27,30 @@
     { :host => Setting.host_name, :protocol => Setting.protocol }
   end
 
+  # Builds a mail for notifying the specified member that they were
+  # added to a project
+  def member_added_to_project(member, project)
+
+    principal = Principal.find(member.user_id)
+
+    users = []
+    if principal.type == "User"
+      users = [User.find(member.user_id)]
+    else
+      users = Principal.find(member.user_id).users      
+    end
+
+    users.map do |user|
+
+      set_language_if_valid user.language
+      @project_url = url_for(:controller => 'projects', :action => 'show', :id => project.id)
+      @project_name = project.name
+      mail :to => user.mail,
+        :subject => l(:mail_subject_added_to_project, Setting.app_title)
+
+    end
+  end
+  
   # Builds a mail for notifying to_users and cc_users about a new issue
   def issue_add(issue, to_users, cc_users)
     redmine_headers 'Project' => issue.project.identifier,
@@ -493,3 +517,17 @@
     Rails.logger
   end
 end
+
+# Patch TMail so that message_id is not overwritten
+
+### NB: Redmine 2.2 no longer uses TMail I think? This function has
+### been removed there
+
+module TMail
+  class Mail
+    def add_message_id( fqdn = nil )
+      self.message_id ||= ::TMail::new_message_id(fqdn)
+    end
+  end
+end
+
--- a/app/models/news.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/models/news.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -82,4 +82,9 @@
       Mailer.news_added(self).deliver
     end
   end
+
+  # returns latest news for a specific project
+  def self.latest_for(project, count = 5)
+    find(:all, :limit => count, :conditions => [ "#{News.table_name}.project_id = #{project.id}", Project.allowed_to_condition(User.current, :view_news) ], :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC") 
+  end
 end
--- a/app/models/project.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/models/project.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -90,6 +90,7 @@
   scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
   scope :all_public, lambda { where(:is_public => true) }
   scope :visible, lambda {|*args| where(Project.visible_condition(args.shift || User.current, *args)) }
+  scope :visible_roots, lambda {|*args| where(Project.root_visible_by(args.shift || User.current, *args)) }
   scope :allowed_to, lambda {|*args|
     user = User.current
     permission = nil
@@ -162,6 +163,10 @@
     allowed_to_condition(user, :view_project, options)
   end
 
+  def self.root_visible_by(user, options={})
+    return "#{Project.table_name}.parent_id IS NULL AND " + visible_condition(user, options)
+  end
+  
   # Returns a SQL conditions string used to find all projects for which +user+ has the given +permission+
   #
   # Valid options:
@@ -542,8 +547,22 @@
   end
 
   # Returns a short description of the projects (first lines)
-  def short_description(length = 255)
-    description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
+  def short_description(length = 200)
+
+    ## The short description is used in lists, e.g. Latest projects,
+    ## My projects etc.  It should be no more than a line or two with
+    ## no text formatting.
+
+    ## Original Redmine code: this truncates to the CR that is more
+    ## than "length" characters from the start.
+    # description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
+
+    ## That can leave too much text for us, and also we want to omit
+    ## images and the like.  Truncate instead to the first CR that
+    ## follows _any_ non-blank text, and to the next word break beyond
+    ## "length" characters if the result is still longer than that.
+    ##
+    description.gsub(/![^\s]+!/, '').gsub(/^(\s*[^\n\r]*).*$/m, '\1').gsub(/^(.{#{length}}[^\.;:,-]*).*$/m, '\1 ...').strip if description
   end
 
   def css_classes
@@ -676,7 +695,8 @@
     'custom_field_values',
     'custom_fields',
     'tracker_ids',
-    'issue_custom_field_ids'
+    'issue_custom_field_ids',
+    'has_welcome_page'
 
   safe_attributes 'enabled_module_names',
     :if => lambda {|project, user| project.new_record? || user.allowed_to?(:select_project_modules, project) }
--- a/app/models/repository.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/models/repository.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -51,6 +51,8 @@
     'password',
     'path_encoding',
     'log_encoding',
+    'is_external',
+    'external_url',
     'is_default'
 
   safe_attributes 'url',
@@ -367,6 +369,10 @@
     nil
   end
 
+  def clear_cache
+    clear_changesets
+  end
+    
   def self.scm_adapter_class
     nil
   end
--- a/app/models/repository/bazaar.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/models/repository/bazaar.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -15,7 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/bazaar_adapter'
+require_dependency 'redmine/scm/adapters/bazaar_adapter'
 
 class Repository::Bazaar < Repository
   attr_protected :root_url
--- a/app/models/repository/cvs.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/models/repository/cvs.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -15,7 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/cvs_adapter'
+require_dependency 'redmine/scm/adapters/cvs_adapter'
 require 'digest/sha1'
 
 class Repository::Cvs < Repository
--- a/app/models/repository/darcs.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/models/repository/darcs.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -15,7 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/darcs_adapter'
+require_dependency 'redmine/scm/adapters/darcs_adapter'
 
 class Repository::Darcs < Repository
   validates_presence_of :url, :log_encoding
--- a/app/models/repository/filesystem.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/models/repository/filesystem.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -18,7 +18,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/filesystem_adapter'
+require_dependency 'redmine/scm/adapters/filesystem_adapter'
 
 class Repository::Filesystem < Repository
   attr_protected :root_url
--- a/app/models/repository/git.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/models/repository/git.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -16,7 +16,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/git_adapter'
+require_dependency 'redmine/scm/adapters/git_adapter'
 
 class Repository::Git < Repository
   attr_protected :root_url
--- a/app/models/repository/mercurial.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/models/repository/mercurial.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -15,7 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/mercurial_adapter'
+require_dependency 'redmine/scm/adapters/mercurial_adapter'
 
 class Repository::Mercurial < Repository
   # sort changesets by revision number
@@ -24,7 +24,7 @@
            :foreign_key => 'repository_id'
 
   attr_protected        :root_url
-  validates_presence_of :url
+  # validates_presence_of :url
 
   # number of changesets to fetch at once
   FETCH_AT_ONCE = 100
--- a/app/models/repository/subversion.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/models/repository/subversion.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -15,7 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/subversion_adapter'
+require_dependency 'redmine/scm/adapters/subversion_adapter'
 
 class Repository::Subversion < Repository
   attr_protected :root_url
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/models/ssamr_user_detail.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,24 @@
+class SsamrUserDetail < ActiveRecord::Base
+  belongs_to :user
+
+  validates_presence_of :description
+
+  validate :check_institution
+
+  def check_institution()
+    errors.add(:institution_id, "Please insert an institution") if
+      institution_id.blank? and other_institution.blank?
+  end
+
+  def institution_name()
+    if not self.institution_type.nil?
+      if self.institution_type
+        Institution.find(self.institution_id).name
+      else
+        self.other_institution
+      end
+    else
+      ""
+    end
+  end
+end
--- a/app/models/user.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/models/user.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -86,6 +86,11 @@
   scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
   scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
 
+  has_one :ssamr_user_detail, :dependent => :destroy, :class_name => 'SsamrUserDetail'
+  accepts_nested_attributes_for :ssamr_user_detail
+  
+  has_one :author
+
   acts_as_customizable
 
   attr_accessor :password, :password_confirmation, :generate_password
@@ -99,6 +104,7 @@
   validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
   validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false
   validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false
+
   # Login must contain letters, numbers, underscores only
   validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i
   validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT
@@ -114,6 +120,8 @@
   before_destroy :remove_references_before_destroy
   after_save :update_notified_project_ids
 
+  validates_acceptance_of :terms_and_conditions, :on => :create, :message => :must_accept_terms_and_conditions
+
   scope :in_group, lambda {|group|
     group_id = group.is_a?(Group) ? group.id : group.to_i
     where("#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id)
@@ -151,6 +159,10 @@
     write_attribute(:mail, arg.to_s.strip)
   end
 
+  def description=(arg)
+    write_attribute(:description, arg.to_s.strip)
+  end
+    
   def identity_url=(url)
     if url.blank?
       write_attribute(:identity_url, '')
--- a/app/views/account/register.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/account/register.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -1,5 +1,10 @@
+<%= javascript_include_tag "ssamr_institutions" %>
+<%= javascript_include_tag "ssamr_registration" %>
+
 <h2><%=l(:label_register)%> <%=link_to l(:label_login_with_open_id_option), signin_url if Setting.openid? %></h2>
 
+<%= l(:text_who_can_register).html_safe %>
+
 <%= labelled_form_for @user, :url => register_path do |f| %>
 <%= error_messages_for 'user' %>
 
@@ -17,10 +22,30 @@
 <p><%= f.text_field :lastname, :required => true %></p>
 <p><%= f.text_field :mail, :required => true %></p>
 
-<% unless @user.force_default_language? %>
+<!-- We only support English in this site
 <p><%= f.select :language, lang_options_for_select %></p>
+-->
+
+<%= labelled_fields_for :ssamr_user_details, @ssamr_user_details do |fields| %>
+<h3><%=l(:label_ssamr_details)%></h3>
+
+<p>
+  <%= fields.text_area :description, :rows => 3, :cols => 40, :required => true, :class => 'wiki-edit' %>
+  <em> <%=l(:text_user_ssamr_description_info).html_safe%></em>
+</p>
+
+<p><label for="institution"><%=l("field_ssamr_user_detail.institution")%> <span class="required">*</span></label>
+  <%= fields.radio_button :institution_type, true, :checked => true %>
+  <%= fields.collection_select(:institution_id, Institution.find(:all, :order => "institutions.order"), :id, :name,  {:selected => @selected_institution_id, :prompt => true}).gsub('&amp;', '&').html_safe  %>
+</p>
+
+<p>
+  <%= fields.radio_button :institution_type, false %> Other:
+  <%= fields.text_field(:other_institution) %>
+</p>
 <% end %>
 
+
 <% if Setting.openid? %>
   <p><%= f.text_field :identity_url  %></p>
 <% end %>
@@ -30,5 +55,10 @@
 <% end %>
 </div>
 
+<%= check_box :user, :terms_and_conditions %> <%= l(:accept_terms_and_conditions) %> <%= link_to("Terms and Conditions", "https://code.soundsoftware.ac.uk/projects/soundsoftware-site/wiki/TandCs", {:target => "_blank"}) %>.
+<br />
+<br />
+
 <%= submit_tag l(:button_submit) %>
 <% end %>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/activities/_busy.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,50 @@
+<% events = @events_by_day %>
+<% if (events.nil?) 
+     activity = Redmine::Activity::Fetcher.new(User.anonymous)
+     days = Setting.activity_days_default.to_i
+     events = activity.events(Date.today - days, Date.today + 1)
+   end
+%>
+
+<% if events.empty? %>
+
+<% else %>
+
+   <ul>
+
+   <% 
+      for project in busy_projects(events, 5)
+   %>
+
+   <li class="busy">
+     <span class="title">
+       <% if !project.root? %>
+         <% project.ancestors.each do |p| %>
+           <%= h(p) %>&nbsp;&#187;
+         <% end %>
+       <% end %>
+       <%= link_to_project project %>
+     </span>
+     <% if !project.is_public? %>
+       <span class="private"><%= l(:field_is_private) %></span>
+     <% end %>
+     <span class='managers'>
+     <%
+	u = project.users_by_role
+	if ! u.empty? %>
+        <%=
+	   mgmt_roles = u.keys.select{ |r| r.allowed_to?(:edit_project) }
+	   managers = mgmt_roles.map{ |r| u[r] }.flatten.sort.uniq
+	   managers.map{ |m| m.name }.join(', ')
+	 %><%
+	end
+	%>
+	</span>
+
+     <%= render_project_short_description project %>
+   </li>
+
+    <% end %>
+  </ul>
+
+<% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/activities/_busy_institution.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,28 @@
+<% events = @events_by_day %>
+<% if (events.nil?) 
+     activity = Redmine::Activity::Fetcher.new(User.anonymous)
+     days = Setting.activity_days_default.to_i
+     events = activity.events(Date.today - days, Date.today + 1)
+   end
+%>
+
+<% if events.empty? %>
+
+<% else %>
+
+   <ul>
+
+   <% 
+      for institution in busy_institutions(events, 5)
+   %>
+
+   <li class="busy">
+     <span class="title">
+       <%= link_to h(institution), { :controller => 'activities', :institution => institution } %>
+     </span>
+   </li>
+
+    <% end %>
+  </ul>
+<% end %>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/activities/_recent.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,65 @@
+<% events = @events_by_day %>
+<% max = 5 %>
+<% if (events.nil?) 
+     activity = Redmine::Activity::Fetcher.new(User.current, :project => @project)
+     
+     if @project
+        # Don't show news (duplicated with News box) or wiki edits (too
+	# tedious) in project front page
+        activity.scope = [ "changesets", "files", "issues", "documents" ]
+     end
+     
+     events = activity.events(Date.today - 28, Date.today + 1)
+     
+     if defined? user
+       events = events.select { |e| user.member_of? e.project }
+     end
+     
+     events = events.first(max)
+
+   end
+%>
+
+<div id="activity">
+
+<% if @project.nil? %>
+   <%= content_tag('h3', l(:label_activity_my_recent)) %>
+   <div class="activity box">
+<% end %>
+
+<% if events.empty? %>
+
+   <% if @project.nil? %>
+     <p><%= l(:label_activity_my_recent_none) %></p>
+   <% end %>
+
+<% else %>
+
+   <% if !@project.nil? %>
+     <div class="activity box">
+     <%= content_tag('h3', l(:label_activity_recent)) %>
+   <% end %>
+
+   <dl>
+   <% events.sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| -%>
+     <dt class="<%= User.current.logged? && e.respond_to?(:event_author) && User.current == e.event_author ? 'me' : nil %>">
+	<%= avatar(e.event_author, :size => "24") if e.respond_to?(:event_author) %>
+     <span class="time"><%= format_time(e.event_datetime) %></span>
+     <%= content_tag('span', link_to_project(e.project), :class => 'project') if @project.nil? || @project != e.project %>
+     <% if e.respond_to?(:event_author) %>
+       <span class="author"><%= e.event_author %></span>
+     <% end %>
+     </dt>
+     <dd><%= link_to format_activity_title(e.event_title), e.event_url %>
+     <span class="description"><%= format_activity_description(e.event_description) %></span>
+     </dd>
+   <% end -%>
+   </dl>
+
+   </div>
+
+<% end %>
+
+<% if events.empty? and @project.nil? %></div><% end %>
+
+</div>
--- a/app/views/activities/index.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/activities/index.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -1,4 +1,14 @@
-<h2><%= @author.nil? ? l(:label_activity) : l(:label_user_activity, link_to_user(@author)).html_safe %></h2>
+<h2><%=
+  if @author.nil?
+    if @institution_name.blank?
+      l(:label_activity)
+    else
+      l(:label_institution_activity, h(@institution_name))
+    end
+  else
+    l(:label_user_activity, link_to_user(@author)).html_safe
+  end
+  %></h2>
 <p class="subtitle"><%= l(:label_date_from_to, :start => format_date(@date_to - @days), :end => format_date(@date_to-1)) %></p>
 
 <div id="activity">
--- a/app/views/attachments/_links.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/attachments/_links.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -7,7 +7,7 @@
                 :id => attachment, :filename => attachment.filename %>
   <% end %>
   <%= h(" - #{attachment.description}") unless attachment.description.blank? %>
-  <span class="size">(<%= number_to_human_size attachment.filesize %>)</span>
+  <span class="size_and_count"><%= number_to_human_size attachment.filesize %><%= ", " + l(:label_x_downloads, :count => attachment.downloads) unless attachment.downloads == 0 %></span>
   <% if options[:deletable] %>
     <%= link_to image_tag('delete.png'), attachment_path(attachment),
                                          :data => {:confirm => l(:text_are_you_sure)},
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/attachments/toggle_active.js.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1 @@
+$('#active-<%= @attachment.id %>').html('<%= link_to image_tag(@attachment.active? ? 'fav.png' : 'fav_off.png'), {:controller => 'attachments', :action => 'toggle_active', :project_id => @project.id, :id => @attachment}, :remote => true %>');
--- a/app/views/files/index.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/files/index.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -5,29 +5,51 @@
 <h2><%=l(:label_attachment_plural)%></h2>
 
 <% delete_allowed = User.current.allowed_to?(:manage_files, @project) %>
+<% active_change_allowed = delete_allowed %>
 
 <table class="list files">
   <thead><tr>
+    <%= sort_header_tag('active', :caption => l(:field_active)) %>
     <%= sort_header_tag('filename', :caption => l(:field_filename)) %>
     <%= sort_header_tag('created_on', :caption => l(:label_date), :default_order => 'desc') %>
     <%= sort_header_tag('size', :caption => l(:field_filesize), :default_order => 'desc') %>
-    <%= sort_header_tag('downloads', :caption => l(:label_downloads_abbr), :default_order => 'desc') %>
+    <%= sort_header_tag('downloads', :caption => l(:field_downloads), :default_order => 'desc') %>
     <th>MD5</th>
     <th></th>
   </tr></thead>
   <tbody>
+<% have_file = false %>
 <% @containers.each do |container| %>
   <% next if container.attachments.empty? -%>
   <% if container.is_a?(Version) -%>
   <tr>
-    <th colspan="6">
+    <th colspan="7">
       <%= link_to(h(container), {:controller => 'versions', :action => 'show', :id => container}, :class => "icon icon-package") %>
     </th>
   </tr>
   <% end -%>
   <% container.attachments.each do |file| %>
-  <tr class="file <%= cycle("odd", "even") %>">
-    <td class="filename"><%= link_to_attachment file, :download => true, :title => file.description %></td>
+  <tr class="file <%= cycle("odd", "even") %> <%= "active" if file.active? %>">
+    <td class="active">
+      <% have_file = true %>
+      <% if active_change_allowed
+           active_id = "active-" + file.id.to_s -%>
+        <div id="<%= active_id %>">
+        <%= link_to image_tag(file.active? ? 'fav.png' : 'fav_off.png'),
+              {:controller => 'attachments', :action => 'toggle_active', :project_id => @project.id, :id => file},
+	      :remote => true
+        %>
+        </div>
+      <% else -%>
+        <%= image_tag('fav.png') if file.active? %>
+      <% end -%>
+    </td>
+    <% if file.active? %>
+      <td class="filename active"><%= link_to_attachment file, :download => true %><br><span class="description"><%= h(file.description) %></span></td>
+    <% else %>
+      <td class="filename"><%= link_to_attachment file, :download => true, :title => file.description %>
+    <% end %>
+    </td>
     <td class="created_on"><%= format_time(file.created_on) %></td>
     <td class="filesize"><%= number_to_human_size(file.filesize) %></td>
     <td class="downloads"><%= file.downloads %></td>
@@ -43,4 +65,6 @@
   </tbody>
 </table>
 
+<%= l(:text_files_active_change).html_safe if active_change_allowed and have_file %>
+
 <% html_title(l(:label_attachment_plural)) -%>
--- a/app/views/layouts/base.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/layouts/base.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -3,11 +3,12 @@
 <head>
 <meta charset="utf-8" />
 <title><%=h html_title %></title>
-<meta name="description" content="<%= Redmine::Info.app_name %>" />
-<meta name="keywords" content="issue,bug,tracker" />
+<meta name="description" content="A repository for software developed and published by audio and music researchers in the UK." />
+<meta name="keywords" content="audio,music,software,research,UK,sound,repository,code,redmine" />
 <%= csrf_meta_tag %>
 <%= favicon %>
 <%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2', 'application', :media => 'all' %>
+<%= stylesheet_platform_font_tag %>
 <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
 <%= javascript_heads %>
 <%= heads_for_theme %>
@@ -27,21 +28,42 @@
     <%= render_menu :top_menu if User.current.logged? || !Setting.login_required? -%>
 </div>
 
-<div id="header">
+<%= tag('div', {:id => 'header', :class => (display_main_menu?(@project) ? 'header-project' : 'header-general')}, true) %>
     <% if User.current.logged? || !Setting.login_required? %>
-    <div id="quick-search">
+    <div id="project-search-jump">
+      <div id="quick-search">
         <%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
         <%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %>
         <label for='q'>
           <%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>:
         </label>
-        <%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %>
-        <% end %>
+          <%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %>
+          <% end %>
+      </div>
+
+      <div id="project-jump-box">
         <%= render_project_jump_box %>
+      </div>
     </div>
     <% end %>
 
-    <h1><%= page_header_title %></h1>
+    <% unless page_header_title[1].empty? %>
+      <h3 id="project-ancestors-title"><%= page_header_title[1] %></h3>
+    <% end %>  
+
+    <h1 id="project-title"
+      <% unless page_header_title[1].empty? %>
+        style="margin-top: 0px; "
+      <% end %>  
+      <% if !@project.nil? and @project.name.length > 35 %>
+        class="long-title"
+      <% end %>
+    ><% if display_main_menu?(@project) %>
+       <%= link_to_project(@project) %>
+     <% else %> 
+       <%= page_header_title[0] %>
+     <% end %>
+    </h1>
 
     <% if display_main_menu?(@project) %>
     <div id="main-menu">
@@ -70,7 +92,7 @@
 
 <div id="footer">
   <div class="bgl"><div class="bgr">
-    Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2014 Jean-Philippe Lang
+    <small>Operated by <a href="http://soundsoftware.ac.uk/">SoundSoftware.ac.uk</a> &ndash; <a href="mailto:info@soundsoftware.ac.uk">contact us</a><br>Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %></small>
   </div></div>
 </div>
 </div>
--- a/app/views/mailer/account_activation_request.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/mailer/account_activation_request.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -1,2 +1,12 @@
 <p><%= l(:mail_body_account_activation_request, h(@user.login)) %></p>
-<p><%= link_to h(@url), @url %></p>
+<p><%= link_to @url, @url %></p>
+
+<h3><%= l(:label_ssamr_details) %></h3>
+<p>
+  <%=l(:field_name)%>: <%= h @user.name %><br />
+  <%=l(:field_mail)%>: <%= h @user.mail %>
+</p>
+
+<h4><%= l(:label_ssamr_description ) %></h4>
+<%= textilizable @user.ssamr_user_detail['description'] %>
+
--- a/app/views/mailer/account_activation_request.text.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/mailer/account_activation_request.text.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -1,2 +1,11 @@
 <%= l(:mail_body_account_activation_request, @user.login) %>
 <%= @url %>
+
+*<%= l(:label_ssamr_details) %>*
+<%=l(:field_name)%>: <%= h @user.name %>
+<%=l(:field_mail)%>: <%= h @user.mail %>
+
+*<%= l(:label_ssamr_description ) %>*
+<%= h @user.ssamr_user_detail['description'] %>
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/mailer/member_added_to_project.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,3 @@
+<p><%= l(:notice_added_to_project, :project_name => @project_name) %></p>
+<p><%= l(:notice_project_homepage, :project_url => @project_url) %></p>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/mailer/member_added_to_project.text.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,2 @@
+<%= l(:notice_added_to_project, :project_name => @project_name) %>
+<%= l(:notice_project_homepage, :project_url => @project_url)  %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/members/_editlist.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,73 @@
+<%= error_messages_for 'member' %>
+
+<% roles = Role.find_all_givable
+   members = @project.member_principals.find(:all, :include => [:roles, :principal]).sort %>
+
+<% if members.any? %>
+<table class="list members">
+	<thead><tr>
+	  <th><%= l(:label_user) %></th>
+	  <th><%= l(:label_role_plural) %></th>
+          <th style="width:15%"></th>
+	</tr></thead>
+	<tbody>
+	<% members.each do |member| %>
+          <% next if member.new_record? %>
+          <tr id="member-<%= member.id %>" class="<%= cycle 'odd', 'even' %> member">
+          <td class="<%= member.principal.class.name.downcase %>"><%= link_to_user member.principal %></td>
+          <td class="roles">
+            <span id="member-<%= member.id %>-roles"><%=h member.roles.sort.collect(&:to_s).join(', ') %></span>
+	    <%= form_for(member, {:as => :membership, :remote => true, :url => membership_path(member),
+                                         :method => :put,
+                                         :html => { :id => "member-#{member.id}-roles-form", :class => 'hol' }}
+              ) do |f| %>
+            <p><% roles.each do |role| %>
+              <label><%= check_box_tag 'membership[role_ids][]', role.id, member.roles.include?(role), :disabled => member.member_roles.detect {|mr| mr.role_id == role.id && !mr.inherited_from.nil?} %> <%=h role %></label><br />
+             <% end %></p>
+        <%= hidden_field_tag 'membership[role_ids][]', '' %>
+        <p><%= submit_tag l(:button_change), :class => "small" %>
+        <%= link_to_function l(:button_cancel),
+                             "$('#member-#{member.id}-roles').show(); $('#member-#{member.id}-roles-form').hide(); return false;"
+             %></p>
+    <% end %>
+  </td>
+  <td class="buttons">
+      <%= link_to_function l(:button_edit),
+                           "$('#member-#{member.id}-roles').hide(); $('#member-#{member.id}-roles-form').show(); return false;",
+                           :class => 'icon icon-edit' %>
+      <%= delete_link membership_path(member),
+                      :data => (!User.current.admin? && member.include?(User.current) ? {:confirm => l(:text_own_membership_delete_confirmation)} : {}) if member.deletable? %>
+  </td>
+	</tr>
+<% end; reset_cycle %>
+	</tbody>
+</table>
+<% else %>
+<p class="nodata"><%= l(:label_no_data) %></p>
+<% end %>
+
+<% content_for :sidebar do %>
+<% principals = Principal.active.find(:all, :limit => 100, :order => 'type, login, lastname ASC') - @project.principals %>
+
+<% if roles.any? && principals.any? %>
+ <%= form_for(@member, {:as => :membership, :url => project_memberships_path(@project), :method => :post}) do |f| %>
+    <h3><%=l(:label_member_new)%></h3>
+    
+		<p><%= label_tag "principal_search", l(:label_principal_search) %><%= text_field_tag 'principal_search', nil %></p>
+		<%= javascript_tag "observeSearchfield('principal_search', 'principals', '#{ escape_javascript autocomplete_project_memberships_path(@project) }')" %>
+
+		<div id="principals">
+		<% if params[:q] && params[:q].length > 1 %>
+  		  <%= principals_check_box_tags 'membership[user_ids][]', @principals %>
+		<% end %>
+		</div>
+		
+    <p><%= l(:label_set_role_plural) %>:</p>
+    <% roles.each do |role| %>
+    	<label><%= check_box_tag 'membership[role_ids][]', role.id %> <%=h role %> </label><div style="margin-left: 2em; margin-bottom: 0.5em"><i><%=l( 'label_' + role.name.downcase + "_description").to_sym %></i></div>
+   	<% end %>
+   	
+    <p><%= submit_tag l(:button_add), :id => 'member-add-submit' %></p>
+  <% end %>
+<% end %>
+<% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/members/_list.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,26 @@
+
+<% roles = Role.find_all_givable
+   members = @project.member_principals.find(:all, :include => [:roles, :principal]).sort %>
+
+<% if members.any? %>
+
+<div id="members">
+<dl>
+<% members.each do |member| %>
+<dt id="member-<%= member.id %>" class="member <%= member.principal.class.name.downcase %> <%= User.current.logged? && User.current == member.user ? 'me' : nil %>">
+<%= avatar(member.user, :size => "24") %>
+<span class="user"><%= link_to_user member.principal %></span>
+<span class="email"><%= member.user.mail if !member.user.pref.hide_mail %></span>
+<dd id="member-<%= member.id %>" class="roles <%= member.principal.class.name.downcase %> <%= User.current.logged? && User.current == member.user ? 'me' : nil %>">
+<span class="roles" id="member-<%= member.id %>-roles"><%=h member.roles.sort.collect(&:to_s).join(', ') %></span>
+</dd>
+<% end %>
+</dl>
+<% else %>
+<p class="nodata"><%= l(:label_no_data) %></p>
+<% end %>
+</div>
+
+<% content_for :sidebar do %>
+<% end %>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/members/index.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,13 @@
+
+<h2><%=l(:label_member_plural)%></h2> 
+
+<% editable = authorize_for('members', 'update') %>
+
+<div id="memberlist">
+<% if editable %>
+<%= render :partial => 'editlist' %>
+<% else %>
+<%= render :partial => 'list' %>
+<% end %>
+</div>
+
--- a/app/views/my/account.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/my/account.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -1,3 +1,5 @@
+<%= javascript_include_tag "ssamr_institutions" %>
+
 <div class="contextual">
 <%= link_to(l(:button_change_password), {:action => 'password'}, :class => 'icon icon-passwd') if @user.change_password_allowed? %>
 <%= call_hook(:view_my_account_contextual, :user => @user)%>
@@ -26,9 +28,33 @@
   <% @user.custom_field_values.select(&:editable?).each do |value| %>
     <p><%= custom_field_tag_with_label :user, value %></p>
   <% end %>
+
   <%= call_hook(:view_my_account, :user => @user, :form => f) %>
 </fieldset>
 
+<%= labelled_fields_for :ssamr_user_details, @user.ssamr_user_detail do |fields| %>
+<h3><%=l(:label_ssamr_details)%></h3>
+<div class="box tabular">
+       <p>
+         <%= fields.text_area :description, :rows => 3, :cols => 25, :required => true, :class => 'wiki-edit' %>
+       </p>
+
+            <p><label for="institution"><%=l("field_ssamr_user_detail.institution")%> <span class="required">*</span></label>
+			<nobr>
+              <%= fields.radio_button :institution_type, true %>
+              <%= fields.collection_select(:institution_id, Institution.find(:all, :order => "institutions.order"), :id, :name, {:selected => @selected_institution_id, :prompt => true} ).gsub('&amp;', '&').html_safe %>
+            </nobr>
+			</p>
+
+            <p>
+				<nobr>
+               		<%= fields.radio_button :institution_type, false %> Other:
+               		<%= fields.text_field :other_institution, :size => 19 %>
+            	</nobr>
+            </p>
+</div>
+<% end %>
+
 <%= submit_tag l(:button_save) %>
 </div>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/my/blocks/_activitymyprojects.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,4 @@
+
+<%= render :partial => 'activities/recent', :locals => { :user => User.current } %>
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/my/blocks/_colleagues.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,13 @@
+
+<% colleagues = all_colleagues_of(@user) %>
+
+<% if !colleagues.empty? %>
+<div id="activity">
+  <h3><%=l(:label_my_colleagues)%></h3>
+  <div class="activity box" id="active-colleagues">
+    <%= render_active_colleagues(colleagues).html_safe %>
+  </div>
+</div>
+<% end %>
+
+
--- a/app/views/my/blocks/_issueswatched.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/my/blocks/_issueswatched.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -6,3 +6,4 @@
 
 <% watched_issues = issueswatched_items %>
 <%= render :partial => 'issues/list_simple', :locals => { :issues => watched_issues } %>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/my/blocks/_myprojects.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,16 @@
+<%  @user_projects = User.current.projects.all(:order => :name) %>
+
+<% if @user_projects.empty? %>
+<h3><%=l(:label_my_projects)%></h3>
+<div class="box">
+  <div class="tip"><%= l(:label_have_no_projects)%> <%= link_to(l(:label_project_new), {:controller => 'projects', :action => 'new'}) if User.current.allowed_to?(:add_project, nil, :global => true) %></div>
+</div>
+<% else %>
+<h3><%=l(:label_my_projects)%> (<%= @user_projects.count %>)</h3>
+<div class="box">
+<%=
+   render :partial => 'projects/my', :locals => { :user => User.current }
+ %>
+</div>
+<% end %>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/my/blocks/_tipoftheday.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,4 @@
+    <h3><%=l(:label_tipoftheday)%></h3>
+    <div class="tipoftheday box">
+          <div class="tip"><%= textilizable Setting.tipoftheday_text %></div>
+    </div>
--- a/app/views/my/page.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/my/page.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -1,5 +1,10 @@
+<% content_for :header_tags do %>
+    <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %>
+<% end %>
+
 <div class="contextual">
     <%= link_to l(:label_personalize_page), :action => 'page_layout' %>
+    <%= ('| ' + link_to(l(:label_project_new), {:controller => 'projects', :action => 'new'}, :class => 'icon icon-add')).html_safe if User.current.allowed_to?(:add_project, nil, :global => true) %>
 </div>
 
 <h2><%=l(:label_my_page)%></h2>
--- a/app/views/news/_news.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/news/_news.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -1,6 +1,13 @@
-<p><%= link_to_project(news.project) + ': ' unless @project %>
-<%= link_to h(news.title), news_path(news) %>
-<%= "(#{l(:label_x_comments, :count => news.comments_count)})" if news.comments_count > 0 %>
-<br />
+<div id="news">
+<dt>
+<span class="time"><%= format_time(news.created_on) %></span>
+<% project ||= @project %>
+<% if !project %>
+<span class="project"><%= link_to_project(news.project) %></span>
+<% end %>
+<span class="headline"><%= link_to h(news.title), news_path(news) %></span>
+<span class="comments"><%= "(#{l(:label_x_comments, :count => news.comments_count)})" if news.comments_count > 0 %></span>
+</dt><dd>
 <% unless news.summary.blank? %><span class="summary"><%=h news.summary %></span><br /><% end %>
-<span class="author"><%= authoring news.created_on, news.author %></span></p>
+</dd>
+</div>
--- a/app/views/projects/_form.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/projects/_form.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -2,15 +2,37 @@
 
 <div class="box tabular">
 <!--[form:project]-->
-<p><%= f.text_field :name, :required => true, :size => 60 %></p>
+<p><%= f.text_field :name, :required => true, :size => 60 %>
+  <br />
+  <em class="info"> <%= l(:text_project_name_info).html_safe %></em>
+</p>
 
 <p><%= f.text_area :description, :rows => 8, :class => 'wiki-edit' %></p>
 <p><%= f.text_field :identifier, :required => true, :size => 60, :disabled => @project.identifier_frozen?, :maxlength => Project::IDENTIFIER_MAX_LENGTH %>
 <% unless @project.identifier_frozen? %>
+  <br />
   <em class="info"><%= l(:text_length_between, :min => 1, :max => Project::IDENTIFIER_MAX_LENGTH) %> <%= l(:text_project_identifier_info).html_safe %></em>
 <% end %></p>
-<p><%= f.text_field :homepage, :size => 60 %></p>
-<p><%= f.check_box :is_public %></p>
+
+<p><%= f.text_field :homepage, :size => 60 %>
+<br />
+  <em class="info"> <%= l(:text_project_homepage_info) %></em>
+</p>
+<p>
+<label for="project_is_public_1"><%= l(:field_public_or_private)%> <span class="required">*</span></label>
+<%
+   # if the project hasn't been created fully yet, then we don't
+   # want to set either public or private (make the user decide)
+   initialised = !@project.id.nil?
+%>
+  <%= f.radio_button :is_public, 1, :checked => (initialised && @project.is_public?) %>
+  <%= l(:text_project_public_info) %>
+<br />
+  <%= f.radio_button :is_public, 0, :checked => (initialised && !@project.is_public?) %>
+  <%= l(:text_project_private_info) %>
+<br>
+  <em class="info"><%= l(:text_project_visibility_info) %></em>
+</p>
 
 <% unless @project.allowed_parents.compact.empty? %>
     <p><%= label(:project, :parent_id, l(:field_parent)) %><%= parent_project_select_tag(@project) %></p>
@@ -40,7 +62,12 @@
 </fieldset>
 <% end %>
 
-<% if @project.new_record? || @project.module_enabled?('issue_tracking') %>
+<% if @project.new_record? %>
+<% @trackers.each do |tracker| %>
+    <%= hidden_field_tag 'project[tracker_ids][]', tracker.id %>
+<% end %>
+<%= hidden_field_tag 'project[tracker_ids][]', '' %>
+<% elsif @project.module_enabled?('issue_tracking') %>
 <% unless @trackers.empty? %>
 <fieldset class="box tabular" id="project_trackers"><legend><%=l(:label_tracker_plural)%></legend>
 <% @trackers.each do |tracker| %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/projects/_latest.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,19 @@
+    <ul>
+    <% for project in Project.latest(User.current) %>
+	<li class="latest">
+	<span class="title">
+	  <% if !project.root? %>
+	    <% project.ancestors.each do |p| %>
+	      <%= h(p) %>&nbsp;&#187;
+	    <% end %>
+	  <% end %>
+	<%= link_to_project project %>
+	</span>
+	<% if !project.is_public? %>
+	   <span class="private"><%= l(:field_is_private) %></span>
+	<% end %>
+	<span class="time"><%= format_time(project.created_on)%></span>
+	<%= render_project_short_description project %>
+      </li>
+    <% end %>
+    </ul>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/projects/_mature.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,38 @@
+
+   <ul>
+
+   <% 
+      for project in varied_mature_projects(7)
+   %>
+
+   <li class="busy">
+     <span class="title">
+       <% if !project.root? %>
+         <% project.ancestors.each do |p| %>
+           <%= h(p) %>&nbsp;&#187;
+         <% end %>
+       <% end %>
+       <%= link_to_project project %>
+     </span>
+     <% if !project.is_public? %>
+       <span class="private"><%= l(:field_is_private) %></span>
+     <% end %>
+     <span class='managers'>
+     <%
+	u = project.users_by_role
+	if ! u.empty? %>
+        <%=
+	   mgmt_roles = u.keys.select{ |r| r.allowed_to?(:edit_project) }
+	   managers = mgmt_roles.map{ |r| u[r] }.flatten.sort.uniq
+	   managers.map{ |m| m.name }.join(', ')
+	 %><%
+	end
+	%>
+	</span>
+
+     <%= render_project_short_description project %>
+   </li>
+
+    <% end %>
+  </ul>
+
--- a/app/views/projects/_members_box.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/projects/_members_box.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -1,8 +1,8 @@
   <% if @users_by_role.any? %>
-  <div class="members box">
+  <div id="memberbox"><div class="box">
     <h3><%=l(:label_member_plural)%></h3>
     <p><% @users_by_role.keys.sort.each do |role| %>
     <%=h role %>: <%= @users_by_role[role].sort.collect{|u| link_to_user u}.join(", ").html_safe %><br />
     <% end %></p>
-  </div>
+  </div></div>
   <% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/projects/_my.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1 @@
+<%= render_my_project_hierarchy(@user_projects) %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/projects/explore.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,60 @@
+<% content_for :header_tags do %>
+    <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %>
+<% end %>
+
+<div class="contextual">
+    <%= link_to l(:label_project_all), { :controller => 'projects', :action => 'index' }%>
+    <%= ('| ' + link_to(l(:label_search_projects), { :controller => 'search', :action => 'index', :projects => 1 })).html_safe %>
+    <%= ('| ' + link_to(l(:label_project_new), {:controller => 'projects', :action => 'new'}, :class => 'icon icon-add')).html_safe if User.current.allowed_to?(:add_project, nil, :global => true) %>
+</div>
+
+<h2><%= l(:label_explore_projects) %></h2>
+
+<div class="threecolumnleft">
+<% cache(:action => 'explore', :action_suffix => 'tags') do %>
+  <div class="tags box">
+  <h3><%=l(:label_project_tags_all)%></h3>
+    <%= render :partial => 'projects/tagcloud' %>
+  </div>
+<% end %>
+</div>
+
+<div class="threecolumnright">
+  <div class="projects box">
+  <h3><%=l(:label_project_latest)%></h3>
+    <%= render :partial => 'projects/latest' %>
+    <%= link_to l(:label_projects_more), { :controller => 'projects' }, :class => 'more' %>
+  </div>
+</div>
+
+<div class="threecolumnleft">
+  <% cache(:action => 'explore', :action_suffix => 'busy_institutions') do %>
+  <div class="institutions box">
+  <h3><%=l(:label_institutions_busy)%></h3>
+    <%= render :partial => 'activities/busy_institution' %>
+    <%= link_to l(:label_overall_activity), { :controller => 'activities', :action => 'index' }, :class => 'more' %>
+  </div>
+  <% end %>
+</div>
+
+<div class="threecolumnright">
+  <% cache(:action => 'explore', :action_suffix => 'busy_projects') do %>
+  <div class="projects box">
+  <h3><%=l(:label_projects_busy)%></h3>
+    <%= render :partial => 'activities/busy' %>
+    <%= link_to l(:label_overall_activity), { :controller => 'activities', :action => 'index' }, :class => 'more' %>
+  </div>
+  <% end %>
+</div>
+
+<div class="threecolumnmid">
+  <% cache(:action => 'explore', :action_suffix => 'mature_projects') do %>
+  <div class="projects box">
+  <h3><%=l(:label_projects_mature)%></h3>
+    <%= render :partial => 'projects/mature' %>
+    <%= link_to l(:label_projects_more), { :controller => 'projects' }, :class => 'more' %>
+  </div>
+  <% end %>
+</div>
+
+<% html_title(l(:label_explore_projects)) -%>
--- a/app/views/projects/index.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/projects/index.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -6,17 +6,14 @@
     <%= render_project_action_links %>
 </div>
 
-<h2><%= l(:label_project_plural) %></h2>
+<h2>
+<%= l("label_project_all") %>
+</h2>
 
-<div id="projects-index">
-<%= render_project_hierarchy(@projects) %>
-</div>
+<%= render_project_table(@projects) %>
 
-<% if User.current.logged? %>
-<p style="text-align:right;">
-<span class="my-project"><%= l(:label_my_projects) %></span>
-</p>
-<% end %>
+<p class="pagination"><%= pagination_links_full @project_pages, @project_count %></p>
+
 
 <% other_formats_links do |f| %>
   <%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
--- a/app/views/projects/new.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/projects/new.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -3,5 +3,4 @@
 <%= labelled_form_for @project do |f| %>
 <%= render :partial => 'form', :locals => { :f => f } %>
 <%= submit_tag l(:button_create) %>
-<%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
 <% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/projects/settings/_overview.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,30 @@
+
+<%= form_for @project,
+            :url => { :action => 'overview', :id => @project.id },
+            :html => {:id => 'overview-form'} do |f| %>
+
+<div class="box tabular">
+
+<p><%= l(:text_has_welcome_page_info, { :overview_link => link_to(l(:label_overview), { :controller => 'projects', :action => 'show' } ) } ).html_safe %></p>
+
+<% if @project.module_enabled? :wiki %>
+
+<p><%= link_to(l(:button_welcome_page_edit), {:controller => 'wiki', :action => 'edit', :project_id => @project, :id => Wiki.titleize("Overview")}, :class => 'icon icon-edit') %>
+
+<% else %>
+
+<p><%= l(:text_has_welcome_page_wiki_disabled, { :modules_link => link_to(l(:label_module_plural), { :controller => 'projects', :action => 'settings', :tab => 'modules' } ) } ).html_safe %></p>
+
+<% end %>
+
+<p><label for="has_welcome_page"><%= l(:label_has_welcome_page) %></label>
+<%= check_box_tag 'has_welcome_page', 1, @project.has_welcome_page? -%>
+<br/><em><%= l(:setting_has_welcome_page) %></em>
+
+</p>
+
+</div>
+
+<%= submit_tag l(:button_save) %>
+
+<% end %>
--- a/app/views/projects/settings/_repositories.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/projects/settings/_repositories.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -1,41 +1,61 @@
-<% if @project.repositories.any? %>
-<table class="list">
-  <thead>
-    <tr>
-      <th><%= l(:field_identifier) %></th>
-      <th><%= l(:field_repository_is_default) %></th>
-      <th><%= l(:label_scm) %></th>
-      <th><%= l(:label_repository) %></th>
-      <th></th>
-    </tr>
-  </thead>
-  <tbody>
-  <% @project.repositories.sort.each do |repository| %>
-    <tr class="<%= cycle 'odd', 'even' %>">
-      <td class="name">
-      <%= link_to repository.identifier, 
-            {:controller => 'repositories', :action => 'show',:id => @project, :repository_id => repository.identifier_param} if repository.identifier.present? %>
-      </td>
-      <td><%= checked_image repository.is_default? %></td>
-      <td><%=h repository.scm_name %></td>
-      <td><%=h repository.url %></td>
-      <td class="buttons">
-      <% if User.current.allowed_to?(:manage_repository, @project) %>
-        <%= link_to(l(:label_user_plural), committers_repository_path(repository),
+
+<%= javascript_include_tag 'repository' %>
+
+<% if @repository %>
+
+<%= form_for @repository,
+       :as => :repository,
+       :remote => true,
+       :url => { :controller => 'repositories', :action => 'update', :id => @repository },
+       :method => 'put' do |f| %>
+
+<%= error_messages_for 'repository' %>
+
+<div class="box tabular">
+
+<p>
+   <%= l(:text_settings_repo_explanation).html_safe %></ br>
+   <% if @repository.is_external %>
+     <p><%= l(:text_settings_repo_is_external).html_safe %></ br>
+   <% else %>
+     <p><%= l(:text_settings_repo_is_internal).html_safe %></ br>
+   <% end %>
+</p>
+
+
+<p>
+	<%= label_tag('repository_is_external', l(:label_is_external_repository)) %>
+	<%= check_box :repository, :is_external, :onclick => "toggle_ext_url()" %> 
+	<br/><em><%= l(:setting_external_repository).html_safe %></em>
+</p>
+
+<p>
+	<%= label_tag('repository_external_url', l(:label_repository_external_url)) %>
+	<%= text_field :repository, :external_url, :disabled => !(@repository and @repository.is_external) %> 
+	<br/><em><%= l(:setting_external_repository_url).html_safe %></em>
+</p>
+
+<p><%= l(:text_settings_repo_need_help).html_safe %></p>
+
+</div>
+
+<div class="contextual">
+<% if @repository && !@repository.new_record? %>
+<%= link_to(l(:label_user_plural),
+                {
+                :controller => 'repositories',
+                :action     => 'committers',
+                :id         => @repository
+                },
              :class => 'icon icon-user') %>
-        <%= link_to(l(:button_edit), edit_repository_path(repository),
-             :class => 'icon icon-edit') %>
-        <%= delete_link repository_path(repository) %>
-      <% end %>
-      </td>
-    </tr>
-  <% end %>
-  </tbody>
-</table>
-<% else %>
-<p class="nodata"><%= l(:label_no_data) %></p>
+<% end %>
+</div>
+
+<%= submit_tag(l(:button_save)) %>
+
 <% end %>
 
-<% if User.current.allowed_to?(:manage_repository, @project) %>
-  <p><%= link_to l(:label_repository_new), new_project_repository_path(@project), :class => 'icon icon-add' %></p>
+<% else %>
+   <%= l(:text_settings_repo_creation).html_safe %></ br>
 <% end %>
+
--- a/app/views/projects/show.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/projects/show.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -11,6 +11,34 @@
   <% end %>
 </div>
 
+<% if @project.has_welcome_page %>
+<% page = @project.wiki.find_page("Overview") %>
+<% end %>
+
+<% if page %>
+
+<% if @project.module_enabled? :wiki %>
+<% if User.current.allowed_to?(:edit_wiki_pages, @project) %>
+<div class="contextual">
+<%= link_to(l(:button_welcome_page_edit_this), {:controller => 'wiki', :action => 'edit', :project_id => @project, :id => Wiki.titleize("Overview")}, :class => 'icon icon-edit') %>
+</div>
+<% end %>
+<% end %>
+
+<div class="contextual" style="clear: right">
+<ul>
+<% unless @project.homepage.blank? %><li><%=l(:field_homepage)%>: <%= link_to h(@project.homepage), @project.homepage %></li><% end %>
+<% if @subprojects.any? %>
+	<li><%=l(:label_subproject_plural)%>:
+	    <%= @subprojects.collect{|p| link_to(h(p), :action => 'show', :id => p)}.join(", ").html_safe %></li>
+<% end %>
+</ul>
+</div>
+
+<%= render(:partial => "wiki/content", :locals => {:content => page.content_for_version()}) %>
+
+<% else %>
+
 <h2><%=l(:label_overview)%></h2>
 
 <% unless @project.active? %>
@@ -38,7 +66,8 @@
   <% end %>
   </ul>
 
-  <% if User.current.allowed_to?(:view_issues, @project) %>
+  <% if User.current.allowed_to?(:view_issues, @project) and @open_issues_by_tracker.values.any? %>
+
   <div class="issues box">
     <h3><%=l(:label_issue_tracking)%></h3>
     <ul>
@@ -59,7 +88,9 @@
       <% end %>
     </p>
   </div>
+
   <% end %>
+
   <%= call_hook(:view_projects_show_left, :project => @project) %>
 </div>
 
@@ -73,10 +104,14 @@
     <p><%= link_to l(:label_news_view_all), project_news_index_path(@project) %></p>
   </div>
   <% end %>
+
+  <%= render :partial => 'activities/recent' %>
+
   <%= call_hook(:view_projects_show_right, :project => @project) %>
 </div>
 
 <% content_for :sidebar do %>
+    <%= call_hook(:view_projects_show_sidebar_top, :project => @project) %>
     <% if @total_hours.present? %>
     <h3><%= l(:label_spent_time) %></h3>
     <p><span class="icon icon-time"><%= l_hours(@total_hours) %></span></p>
@@ -90,8 +125,10 @@
     <%= call_hook(:view_projects_show_sidebar_bottom, :project => @project) %>
 <% end %>
 
+<% end %>
+
 <% content_for :header_tags do %>
 <%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :id => @project, :format => 'atom', :key => User.current.rss_key}) %>
 <% end %>
 
-<% html_title(l(:label_overview)) -%>
+<% html_title('') -%>
--- a/app/views/repositories/_dir_list_content.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/repositories/_dir_list_content.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -17,7 +17,7 @@
                        :parent_id => tr_id)) %>');">&nbsp;</span>
 <% end %>
 <%=  link_to h(ent_name),
-          {:action => (entry.is_dir? ? 'show' : 'changes'), :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(ent_path), :rev => @rev},
+          {:action => (entry.is_dir? ? 'show' : 'entry'), :id => @project, :repository_id => @repository.identifier_param, :path => to_path_param(ent_path), :rev => @rev},
           :class => (entry.is_dir? ? 'icon icon-folder' : "icon icon-file #{Redmine::MimeType.css_class_of(ent_name)}")%>
 </td>
 <td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
--- a/app/views/repositories/_navigation.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/repositories/_navigation.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -6,6 +6,8 @@
             {:action => 'stats', :id => @project, :repository_id => @repository.identifier_param},
             :class => 'icon icon-stats' if @repository.supports_all_revisions? %>
 
+<%= link_to_revision_archive(@repository, @rev, @project, { :text => l(:label_download_revision), :class => 'icon icon-package' }) %>
+
 <%= form_tag({:action => controller.action_name,
              :id => @project,
              :repository_id => @repository.identifier_param,
@@ -32,3 +34,5 @@
     <%= text_field_tag 'rev', @rev, :size => 8 %>
   <% end %>
 <% end -%>
+
+
--- a/app/views/repositories/entry.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/repositories/entry.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -8,7 +8,15 @@
 
 <%= render :partial => 'link_to_functions' %>
 
-<%= render :partial => 'common/file', :locals => {:filename => @path, :content => @content} %>
+<% if @display_raw %>
+   <% if @content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte %>
+     <em><%= l(:text_data_too_large) %></em>
+   <% else %>
+     <em><%= l(:text_binary_data) %></em>
+   <% end %>
+<% else %>
+  <%= render :partial => 'common/file', :locals => {:filename => @path, :content => @content} %>
+<% end %>
 
 <% content_for :header_tags do %>
 <%= stylesheet_link_tag "scm" %>
--- a/app/views/repositories/revision.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/repositories/revision.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -1,11 +1,15 @@
 <div class="contextual">
+
+  <%= link_to_revision_archive(@repository, @changeset, @project, { :text => l(:label_download_revision), :class => 'icon icon-package' }) %>
+  &nbsp;&nbsp;
+
   &#171;
   <% unless @changeset.previous.nil? -%>
     <%= link_to_revision(@changeset.previous, @repository, :text => l(:label_previous)) %>
   <% else -%>
     <%= l(:label_previous) %>
   <% end -%>
-|
+
   <% unless @changeset.next.nil? -%>
     <%= link_to_revision(@changeset.next, @repository, :text => l(:label_next)) %>
   <% else -%>
@@ -22,6 +26,7 @@
     <%= text_field_tag 'rev', @rev, :size => 8 %>
     <%= submit_tag 'OK', :name => nil %>
   <% end %>
+
 </div>
 
 <h2><%= avatar(@changeset.user, :size => "24") %><%= l(:label_revision) %> <%= format_revision(@changeset) %></h2>
--- a/app/views/search/index.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/search/index.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -21,11 +21,22 @@
 <% end %>
 </div>
 
+<% if @project_matches and !@project_matches.empty? and !@pagination_previous_date %>
+   <h3><%= l(:label_matching_project_plural) %> (<%= @project_matches.count %>)</h3>
+    <dl id="search-results">
+      <% @project_matches.each do |e| %>
+        <dt class="<%= e.event_type %>"><%= content_tag('span', h(e.project), :class => 'project') unless @project == e.project %> <%= link_to highlight_tokens(truncate(e.event_title, :length => 255), @tokens), e.event_url %></dt>
+        <dd><span class="description"><%= e.short_description %></span>
+      <% end %>
+    </dl>
+<% end %>
+
 <% if @results %>
+    
+    <h3><%= l(:label_result_plural) %> (<%= @results_by_type.values.sum %>)</h3>
     <div id="search-results-counts">
       <%= render_results_by_type(@results_by_type) unless @scope.size == 1 %>
     </div>
-    <h3><%= l(:label_result_plural) %> (<%= @results_by_type.values.sum %>)</h3>
     <dl id="search-results">
       <% @results.each do |e| %>
         <dt class="<%= e.event_type %>">
--- a/app/views/settings/_general.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/settings/_general.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -3,9 +3,15 @@
 <div class="box tabular settings">
 <p><%= setting_text_field :app_title, :size => 30 %></p>
 
+<p><%= setting_text_area :notifications_text, :cols => 60, :rows => 5, :class => 'wiki-edit' %></p>
+ <%= wikitoolbar_for 'settings_notifications_text' %>
+
 <p><%= setting_text_area :welcome_text, :cols => 60, :rows => 5, :class => 'wiki-edit' %></p>
 <%= wikitoolbar_for 'settings_welcome_text' %>
 
+<p><%= setting_text_area :tipoftheday_text, :cols => 60, :rows => 5, :class => 'wiki-edit' %></p>
+<%= wikitoolbar_for 'settings_tipoftheday_text' %>
+
 <p><%= setting_text_field :attachment_max_size, :size => 6 %> <%= l(:"number.human.storage_units.units.kb") %></p>
 
 <p><%= setting_text_field :per_page_options, :size => 20 %>
--- a/app/views/users/_form.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/users/_form.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -1,3 +1,7 @@
+<% content_for :header_tags do %>
+  <%= javascript_include_tag "ssamr_institutions" %>
+<% end %>
+
 <%= error_messages_for 'user' %>
 
 <div id="user_form">
@@ -24,6 +28,33 @@
   <%= call_hook(:view_users_form, :user => @user, :form => f) %>
 </fieldset>
 
+<div class="box tabular">
+	<h3><%=l(:label_ssamr_details)%></h3>
+          <%= labelled_fields_for :ssamr_user_detail, @user.ssamr_user_detail do |ssamr_user_detail| %>
+            <p>
+              <%= ssamr_user_detail.text_area :description, :rows => 3, :cols => 40, :required => true, :class => 'wiki-edit'  %>
+            </p>
+
+            <p><label for="institution"><%=l("field_ssamr_user_detail.institution")%> <span class="required">*</span></label>
+			 <nobr>
+              <%= ssamr_user_detail.radio_button :institution_type, true %>
+              <%= ssamr_user_detail.collection_select(:institution_id, Institution.find(:all, :order => "institutions.order"), :id, :name, {:selected => @selected_institution_id, :prompt => true} ).gsub('&amp;', '&').html_safe  %>
+             </nobr>
+			</p>
+
+
+
+            <p>
+				<nobr>
+               <%= ssamr_user_detail.radio_button :institution_type, false %> Other:
+               <%= ssamr_user_detail.text_field :other_institution %>
+				</nobr>
+            </p>
+          <% end %>
+</div>
+
+
+
 <fieldset class="box tabular">
   <legend><%=l(:label_authentication)%></legend>
   <% unless @auth_sources.empty? %>
--- a/app/views/users/show.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/users/show.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -20,6 +20,13 @@
   <% end %>
 </ul>
 
+<h3><%=l(:label_ssamr_description)%></h3>
+<%= textilizable @description %>
+
+<h3><%=l(:label_ssamr_institution)%></h3>
+<p><%= h @institution_name %></p>
+
+
 <% unless @memberships.empty? %>
 <h3><%=l(:label_project_plural)%></h3>
 <ul>
--- a/app/views/welcome/index.html.erb	Tue Sep 09 09:29:00 2014 +0100
+++ b/app/views/welcome/index.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -1,39 +1,28 @@
-<h2><%= l(:label_home) %></h2>
 
-<div class="splitcontentleft">
-  <%= textilizable Setting.welcome_text %>
-  <% if @news.any? %>
-  <div class="news box">
-  <h3><%=l(:label_news_latest)%></h3>
-    <%= render :partial => 'news/news', :collection => @news %>
-    <%= link_to l(:label_news_view_all), :controller => 'news' %>
+<% if not Setting.notifications_text.empty? %>
+  <div class="notifications flash newsalert">
+    <%= textilizable Setting.notifications_text %> 
   </div>
+<% end %>
+
+<div id="welcomepagenews">
+  <% if @site_news.any? %>
+    <div class="news box">
+       <h3><%=l(:label_news_site_latest)%></h3>
+	<%= render :partial => 'news/news', :locals => { :project => @site_project }, :collection => @site_news %>
+	<%= link_to l(:label_news_more), { :controller => 'news', :project_id => @site_project.identifier, :action => 'index' } %>
+    </div>
   <% end %>
-  <%= call_hook(:view_welcome_index_left, :projects => @projects) %>
 </div>
 
-<div class="splitcontentright">
-    <% if @projects.any? %>
-  <div class="projects box">
-  <h3><%=l(:label_project_latest)%></h3>
-    <ul>
-    <% for project in @projects %>
-      <% @project = project %>
-      <li>
-      <%= link_to_project project %> (<%= format_time(project.created_on) %>)
-      <%= textilizable project.short_description, :project => project %>
-      </li>
-    <% end %>
-    <% @project = nil %>
-    </ul>
-  </div>
-  <% end %>
-    <%= call_hook(:view_welcome_index_right, :projects => @projects) %>
+<div id="welcomepage">
+<%= textilizable Setting.welcome_text %>
 </div>
-
+  
 <% content_for :header_tags do %>
 <%= auto_discovery_link_tag(:atom, {:controller => 'news', :action => 'index', :key => User.current.rss_key, :format => 'atom'},
                                    :title => "#{Setting.app_title}: #{l(:label_news_latest)}") %>
 <%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :key => User.current.rss_key, :format => 'atom'},
                                    :title => "#{Setting.app_title}: #{l(:label_activity)}") %>
 <% end %>
+
--- a/config/application.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/application.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -29,6 +29,10 @@
     # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
     # config.time_zone = 'Central Time (US & Canada)'
 
+    # Avoid deprecation warning (may want to change this later if future
+    # Redmine updates are OK with it)
+    config.i18n.enforce_available_locales = false
+
     # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
     # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
     # config.i18n.default_locale = :de
--- a/config/locales/bg.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/bg.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -547,6 +547,7 @@
   label_login_with_open_id_option: или вход чрез OpenID
   label_password_lost: Забравена парола
   label_home: Начало
+  label_home_heading: Начало
   label_my_page: Лична страница
   label_my_account: Профил
   label_my_projects: Проекти, в които участвам
--- a/config/locales/bs.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/bs.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -440,6 +440,7 @@
   label_login_with_open_id_option: ili prijava sa OpenID-om
   label_password_lost: Izgubljena lozinka
   label_home: Početna stranica
+  label_home_heading: Početna stranica
   label_my_page: Moja stranica
   label_my_account: Moj korisnički račun
   label_my_projects: Moji projekti
--- a/config/locales/ca.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/ca.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -476,6 +476,7 @@
   label_login_with_open_id_option: "o entra amb l'OpenID"
   label_password_lost: Contrasenya perduda
   label_home: Inici
+  label_home_heading: Inici
   label_my_page: La meva pàgina
   label_my_account: El meu compte
   label_my_projects: Els meus projectes
--- a/config/locales/cs.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/cs.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -489,6 +489,7 @@
   label_login_with_open_id_option: nebo se přihlašte s OpenID
   label_password_lost: Zapomenuté heslo
   label_home: Úvodní
+  label_home_heading: Úvodní
   label_my_page: Moje stránka
   label_my_account: Můj účet
   label_my_projects: Moje projekty
--- a/config/locales/da.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/da.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -369,6 +369,7 @@
   label_register: Registrér
   label_password_lost: Glemt kodeord
   label_home: Forside
+  label_home_heading: Forside
   label_my_page: Min side
   label_my_account: Min konto
   label_my_projects: Mine projekter
--- a/config/locales/de.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/de.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -828,6 +828,172 @@
     one:   1 Projekt
     other: "%{count} Projekte"
   label_year: Jahr
+  label_project_all: Alle Projekte
+  label_project_latest: Neueste Projekte
+  label_issue: Ticket
+  label_issue_new: Neues Ticket
+  label_issue_plural: Tickets
+  label_issue_view_all: Alle Tickets anzeigen
+  label_issues_by: "Tickets von %{value}"
+  label_issue_added: Ticket hinzugefügt
+  label_issue_updated: Ticket aktualisiert
+  label_document: Dokument
+  label_document_new: Neues Dokument
+  label_document_plural: Dokumente
+  label_document_added: Dokument hinzugefügt
+  label_role: Rolle
+  label_role_plural: Rollen
+  label_role_new: Neue Rolle
+  label_role_and_permissions: Rollen und Rechte
+  label_member: Mitglied
+  label_member_new: Neues Mitglied
+  label_member_plural: Mitglieder
+  label_tracker: Tracker
+  label_tracker_plural: Tracker
+  label_tracker_new: Neuer Tracker
+  label_workflow: Workflow
+  label_issue_status: Ticket-Status
+  label_issue_status_plural: Ticket-Status
+  label_issue_status_new: Neuer Status
+  label_issue_category: Ticket-Kategorie
+  label_issue_category_plural: Ticket-Kategorien
+  label_issue_category_new: Neue Kategorie
+  label_custom_field: Benutzerdefiniertes Feld
+  label_custom_field_plural: Benutzerdefinierte Felder
+  label_custom_field_new: Neues Feld
+  label_enumerations: Aufzählungen
+  label_enumeration_new: Neuer Wert
+  label_information: Information
+  label_information_plural: Informationen
+  label_please_login: Anmelden
+  label_register: Registrieren
+  label_login_with_open_id_option: oder mit OpenID anmelden
+  label_password_lost: Kennwort vergessen
+  label_home: Hauptseite
+  label_home_heading: Hauptseite
+  label_my_page: Meine Seite
+  label_my_account: Mein Konto
+  label_my_projects: Meine Projekte
+  label_my_page_block: Bereich "Meine Seite"
+  label_administration: Administration
+  label_login: Anmelden
+  label_logout: Abmelden
+  label_help: Hilfe
+  label_reported_issues: Gemeldete Tickets
+  label_assigned_to_me_issues: Mir zugewiesen
+  label_last_login: Letzte Anmeldung
+  label_registered_on: Angemeldet am
+  label_activity: Aktivität
+  label_overall_activity: Aktivitäten aller Projekte anzeigen
+  label_user_activity: "Aktivität von %{value}"
+  label_new: Neu
+  label_logged_as: Angemeldet als
+  label_environment: Umgebung
+  label_authentication: Authentifizierung
+  label_auth_source: Authentifizierungs-Modus
+  label_auth_source_new: Neuer Authentifizierungs-Modus
+  label_auth_source_plural: Authentifizierungs-Arten
+  label_subproject_plural: Unterprojekte
+  label_subproject_new: Neues Unterprojekt
+  label_and_its_subprojects: "%{value} und dessen Unterprojekte"
+  label_min_max_length: Länge (Min. - Max.)
+  label_list: Liste
+  label_date: Datum
+  label_integer: Zahl
+  label_float: Fließkommazahl
+  label_boolean: Boolean
+  label_string: Text
+  label_text: Langer Text
+  label_attribute: Attribut
+  label_attribute_plural: Attribute
+  label_download: "%{count} Download"
+  label_download_plural: "%{count} Downloads"
+  label_no_data: Nichts anzuzeigen
+  label_change_status: Statuswechsel
+  label_history: Historie
+  label_attachment: Datei
+  label_attachment_new: Neue Datei
+  label_attachment_delete: Anhang löschen
+  label_attachment_plural: Dateien
+  label_file_added: Datei hinzugefügt
+  label_report: Bericht
+  label_report_plural: Berichte
+  label_news: News
+  label_news_new: News hinzufügen
+  label_news_plural: News
+  label_news_latest: Letzte News
+  label_news_view_all: Alle News anzeigen
+  label_news_added: News hinzugefügt
+  label_settings: Konfiguration
+  label_overview: Übersicht
+  label_version: Version
+  label_version_new: Neue Version
+  label_version_plural: Versionen
+  label_close_versions: Vollständige Versionen schließen
+  label_confirmation: Bestätigung
+  label_export_to: "Auch abrufbar als:"
+  label_read: Lesen...
+  label_public_projects: Öffentliche Projekte
+  label_open_issues: offen
+  label_open_issues_plural: offen
+  label_closed_issues: geschlossen
+  label_closed_issues_plural: geschlossen
+  label_x_open_issues_abbr_on_total:
+    zero:  0 offen / %{total}
+    one:   1 offen / %{total}
+    other: "%{count} offen / %{total}"
+  label_x_open_issues_abbr:
+    zero:  0 offen
+    one:   1 offen
+    other: "%{count} offen"
+  label_x_closed_issues_abbr:
+    zero:  0 geschlossen
+    one:   1 geschlossen
+    other: "%{count} geschlossen"
+  label_total: Gesamtzahl
+  label_permissions: Berechtigungen
+  label_current_status: Gegenwärtiger Status
+  label_new_statuses_allowed: Neue Berechtigungen
+  label_all: alle
+  label_none: kein
+  label_nobody: Niemand
+  label_next: Weiter
+  label_previous: Zurück
+  label_used_by: Benutzt von
+  label_details: Details
+  label_add_note: Kommentar hinzufügen
+  label_per_page: Pro Seite
+  label_calendar: Kalender
+  label_months_from: Monate ab
+  label_gantt: Gantt-Diagramm
+  label_internal: Intern
+  label_last_changes: "%{count} letzte Änderungen"
+  label_change_view_all: Alle Änderungen anzeigen
+  label_personalize_page: Diese Seite anpassen
+  label_comment: Kommentar
+  label_comment_plural: Kommentare
+  label_x_comments:
+    zero: keine Kommentare
+    one: 1 Kommentar
+    other: "%{count} Kommentare"
+  label_comment_add: Kommentar hinzufügen
+  label_comment_added: Kommentar hinzugefügt
+  label_comment_delete: Kommentar löschen
+  label_query: Benutzerdefinierte Abfrage
+  label_query_plural: Benutzerdefinierte Berichte
+  label_query_new: Neuer Bericht
+  label_filter_add: Filter hinzufügen
+  label_filter_plural: Filter
+  label_equals: ist
+  label_not_equals: ist nicht
+  label_in_less_than: in weniger als
+  label_in_more_than: in mehr als
+  label_greater_or_equal: ">="
+  label_less_or_equal: "<="
+  label_in: an
+  label_today: heute
+  label_all_time: gesamter Zeitraum
+>>>>>>> other
   label_yesterday: gestern
 
   mail_body_account_activation_request: "Ein neuer Benutzer (%{value}) hat sich registriert. Sein Konto wartet auf Ihre Genehmigung:"
--- a/config/locales/el.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/el.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -439,6 +439,7 @@
   label_login_with_open_id_option: ή συνδεθείτε με OpenID
   label_password_lost: Ανάκτηση κωδικού πρόσβασης
   label_home: Αρχική σελίδα
+  label_home_heading: Αρχική σελίδα
   label_my_page: Η σελίδα μου
   label_my_account: Ο λογαριασμός μου
   label_my_projects: Τα έργα μου
--- a/config/locales/en-GB.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/en-GB.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -1,1108 +1,6 @@
 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"
+  dummy_translation_key: Dummy translation value
 
-    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_hours:
-        one:   "1 hour"
-        other: "%{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: 3
-      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:
-      template:
-        header:
-          one:    "1 error prohibited this %{model} from being saved"
-          other:  "%{count} errors prohibited this %{model} from being saved"
-      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 cannot be linked to one of its subtasks"
-        earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues"
-
-  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: UTF-8
-  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. An email containing the instructions to activate your account was sent to %{email}.
-  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_not_authorized_archived_project: The project you're trying to access has been archived.
-  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 Atom 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_failed_to_save_members: "Failed to save member(s): %{errors}."
-  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_unable_delete_time_entry: Unable to delete time log entry.
-  notice_issue_done_ratios_updated: Issue done ratios updated.
-  notice_gantt_chart_truncated: "The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})"
-
-  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 cannot be annotated."
-  error_scm_annotate_big_text_file: "The entry cannot be annotated, as it exceeds the maximum text file size."
-  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_delete_custom_field: Unable to delete custom field
-  error_can_not_delete_tracker: "This tracker contains issues and cannot be deleted."
-  error_can_not_remove_role: "This role is in use and cannot be deleted."
-  error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version cannot be reopened'
-  error_can_not_archive_project: This project cannot 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)'
-  error_unable_delete_issue_status: 'Unable to delete issue status'
-  error_unable_to_connect: "Unable to connect (%{value})"
-  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}."
-
-
-  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_principal: Principal
-  field_role: Role
-  field_homepage: Homepage
-  field_is_public: Public
-  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_entries: Log time
-  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
-  field_parent_issue: Parent task
-  field_member_of_group: "Assignee's group"
-  field_assigned_to_role: "Assignee's role"
-  field_text: Text field
-  field_visible: Visible
-  field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text"
-
-  setting_app_title: Application title
-  setting_app_subtitle: Application subtitle
-  setting_welcome_text: Welcome text
-  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_emails_header: Email header
-  setting_emails_footer: Email 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
-  setting_default_notification_option: Default notification option
-  setting_commit_logtime_enabled: Enable time logging
-  setting_commit_logtime_activity_id: Activity for logged time
-  setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
-  setting_issue_group_assignment: Allow issue assignment to groups
-  setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
-
-  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_view_documents: View documents
-  permission_manage_files: Manage files
-  permission_view_files: View files
-  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 forums
-  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
-  permission_manage_subtasks: Manage subtasks
-
-  project_module_issue_tracking: Issue tracking
-  project_module_time_tracking: Time tracking
-  project_module_news: News
-  project_module_documents: Documents
-  project_module_files: Files
-  project_module_wiki: Wiki
-  project_module_repository: Repository
-  project_module_boards: Forums
-  project_module_calendar: Calendar
-  project_module_gantt: Gantt
-
-  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_x_projects:
-    zero:  no projects
-    one:   1 project
-    other: "%{count} projects"
-  label_project_all: All Projects
-  label_project_latest: Latest projects
-  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_role_anonymous: Anonymous
-  label_role_non_member: Non member
-  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_my_page: My page
-  label_my_account: My account
-  label_my_projects: My projects
-  label_my_page_block: My page block
-  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_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: Files
-  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_news_comment_added: Comment added to a news
-  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_my_queries: My custom queries
-  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_repository_plural: Repositories
-  label_browse: Browse
-  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_overall_spent_time: Overall 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: Atom access key
-  label_missing_feeds_access_key: Missing a Atom access key
-  label_feeds_access_key_created_on: "Atom 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: Files
-  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_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"
-  label_user_mail_option_only_owner: "Only for things I am the owner of"
-  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"
-  label_profile: Profile
-  label_subtask_plural: Subtasks
-  label_project_copy_notifications: Send email notifications during the project copy
-  label_principal_search: "Search for user or group:"
-  label_user_search: "Search for user:"
-
-  button_login: Login
-  button_submit: Submit
-  button_save: Save
-  button_check_all: Check all
-  button_uncheck_all: Uncheck all
-  button_collapse_all: Collapse all
-  button_expand_all: Expand all
-  button_delete: Delete
-  button_create: Create
-  button_create_and_continue: Create and continue
-  button_test: Test
-  button_edit: Edit
-  button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
-  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_changed_no_detail: "%{label} updated"
-  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, dashes and underscores are allowed, must start with a lower case letter.<br />Once saved, the identifier cannot be changed.'
-  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_time_logged_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/configuration.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_zoom_in: Zoom in
-  text_zoom_out: Zoom out
-  text_warn_on_leaving_unsaved: "The current page contains unsaved text that will be lost if you leave this page."
-
-  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
-  label_additional_workflow_transitions_for_assignee: Additional transitions allowed when the user is the assignee
-  label_additional_workflow_transitions_for_author: Additional transitions allowed when the user is the author
-  label_bulk_edit_selected_time_entries: Bulk edit selected time entries
-  text_time_entries_destroy_confirmation: Are you sure you want to delete the selected time entr(y/ies)?
-  label_issue_note_added: Note added
-  label_issue_status_updated: Status updated
-  label_issue_priority_updated: Priority updated
-  label_issues_visibility_own: Issues created by or assigned to the user
-  field_issues_visibility: Issues visibility
-  label_issues_visibility_all: All issues
-  permission_set_own_issues_private: Set own issues public or private
-  field_is_private: Private
-  permission_set_issues_private: Set issues public or private
-  label_issues_visibility_public: All non private issues
-  text_issues_destroy_descendants_confirmation: This will also delete %{count} subtask(s).
-  field_commit_logs_encoding: Commit messages encoding
-  field_scm_path_encoding: Path encoding
-  text_scm_path_encoding_note: "Default: UTF-8"
-  field_path_to_repository: Path to repository
-  field_root_directory: Root directory
-  field_cvs_module: Module
-  field_cvsroot: CVSROOT
-  text_mercurial_repository_note: Local repository (e.g. /hgrepo, c:\hgrepo)
-  text_scm_command: Command
-  text_scm_command_version: Version
-  label_git_report_last_commit: Report last commit for files and directories
-  text_scm_config: You can configure your SCM commands in config/configuration.yml. Please restart the application after editing it.
-  text_scm_command_not_available: SCM command is not available. Please check settings on the administration panel.
-  notice_issue_successful_create: Issue %{id} created.
-  label_between: between
-  label_diff: diff
-  text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo)
-  description_query_sort_criteria_direction: Sort direction
-  description_project_scope: Search scope
-  description_filter: Filter
-  description_user_mail_notification: Mail notification settings
-  description_date_from: Enter start date
-  description_message_content: Message content
-  description_available_columns: Available Columns
-  description_date_range_interval: Choose range by selecting start and end date
-  description_issue_category_reassign: Choose issue category
-  description_search: Searchfield
-  description_notes: Notes
-  description_date_range_list: Choose range from list
-  description_choose_project: Projects
-  description_date_to: Enter end date
-  description_query_sort_criteria_attribute: Sort attribute
-  description_wiki_subpages_reassign: Choose new parent page
-  description_selected_columns: Selected Columns
-  label_parent_revision: Parent
-  label_child_revision: Child
-  button_edit_section: Edit this section
-  setting_repositories_encodings: Attachments and repositories encodings
-  description_all_columns: All Columns
-  button_export: Export
-  label_export_options: "%{export_format} export options"
-  error_attachment_too_big: This file cannot be uploaded because it exceeds the maximum allowed file size (%{max_size})
-  notice_failed_to_save_time_entries: "Failed to save %{count} time entrie(s) on %{total} selected: %{ids}."
-  label_x_issues:
-    zero:  0 issue
-    one:   1 issue
-    other: "%{count} issues"
-  label_repository_new: New repository
-  field_repository_is_default: Main repository
-  label_copy_attachments: Copy attachments
-  label_item_position: "%{position} of %{count}"
-  label_completed_versions: Completed versions
-  field_multiple: Multiple values
-  setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed
-  text_issue_conflict_resolution_add_notes: Add my notes and discard my other changes
-  text_issue_conflict_resolution_overwrite: Apply my changes anyway (previous notes will be kept but some changes may be overwritten)
-  notice_issue_update_conflict: The issue has been updated by an other user while you were editing it.
-  text_issue_conflict_resolution_cancel: Discard all my changes and redisplay %{link}
-  permission_manage_related_issues: Manage related issues
-  field_auth_source_ldap_filter: LDAP filter
-  label_search_for_watchers: Search for watchers to add
-  notice_account_deleted: Your account has been permanently deleted.
-  setting_unsubscribe: Allow users to delete their own account
-  button_delete_my_account: Delete my account
-  text_account_destroy_confirmation: |-
-    Are you sure you want to proceed?
-    Your account will be permanently deleted, with no way to reactivate it.
-  error_session_expired: Your session has expired. Please login again.
-  text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours."
-  setting_session_lifetime: Session maximum lifetime
-  setting_session_timeout: Session inactivity timeout
-  label_session_expiration: Session expiration
-  permission_close_project: Close / reopen the project
-  label_show_closed_projects: View closed projects
-  button_close: Close
-  button_reopen: Reopen
-  project_status_active: active
-  project_status_closed: closed
-  project_status_archived: archived
-  text_project_closed: This project is closed and read-only.
-  notice_user_successful_create: User %{id} created.
-  field_core_fields: Standard fields
-  field_timeout: Timeout (in seconds)
-  setting_thumbnails_enabled: Display attachment thumbnails
-  setting_thumbnails_size: Thumbnails size (in pixels)
-  label_status_transitions: Status transitions
-  label_fields_permissions: Fields permissions
-  label_readonly: Read-only
-  label_required: Required
-  text_repository_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.'
-  field_board_parent: Parent forum
-  label_attribute_of_project: Project's %{name}
-  label_attribute_of_author: Author's %{name}
-  label_attribute_of_assigned_to: Assignee's %{name}
-  label_attribute_of_fixed_version: Target version's %{name}
-  label_copy_subtasks: Copy subtasks
-  label_copied_to: copied to
-  label_copied_from: copied from
-  label_any_issues_in_project: any issues in project
-  label_any_issues_not_in_project: any issues not in project
-  field_private_notes: Private notes
-  permission_view_private_notes: View private notes
-  permission_set_notes_private: Set notes as private
-  label_no_issues_in_project: no issues in project
-  label_any: all
-  label_last_n_weeks: last %{count} weeks
-  setting_cross_project_subtasks: Allow cross-project subtasks
-  label_cross_project_descendants: With subprojects
-  label_cross_project_tree: With project tree
-  label_cross_project_hierarchy: With project hierarchy
-  label_cross_project_system: With all projects
-  button_hide: Hide
-  setting_non_working_week_days: Non-working days
-  label_in_the_next_days: in the next
-  label_in_the_past_days: in the past
-  label_attribute_of_user: User's %{name}
-  text_turning_multiple_off: If you disable multiple values, multiple values will be
-    removed in order to preserve only one value per item.
-  label_attribute_of_issue: Issue's %{name}
-  permission_add_documents: Add documents
-  permission_edit_documents: Edit documents
-  permission_delete_documents: Delete documents
-  label_gantt_progress_line: Progress line
-  setting_jsonp_enabled: Enable JSONP support
-  field_inherit_members: Inherit members
-  field_closed_on: Closed
-  field_generate_password: Generate password
-  setting_default_projects_tracker_ids: Default trackers for new projects
-  label_total_time: Total
-  notice_account_not_activated_yet: You haven't activated your account yet. If you want
-    to receive a new activation email, please <a href="%{url}">click this link</a>.
-  notice_account_locked: Your account is locked.
-  label_hidden: Hidden
-  label_visibility_private: to me only
-  label_visibility_roles: to these roles only
-  label_visibility_public: to any users
-  field_must_change_passwd: Must change password at next logon
-  notice_new_password_must_be_different: The new password must be different from the
-    current password
-  setting_mail_handler_excluded_filenames: Exclude attachments by name
-  text_convert_available: ImageMagick convert available (optional)
   label_link: Link
   label_only: only
   label_drop_down_list: drop-down list
--- a/config/locales/en.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/en.yml	Tue Sep 09 10:02:18 2014 +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]
@@ -24,10 +23,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"
 
@@ -74,9 +73,13 @@
   number:
     format:
       separator: "."
-      delimiter: ""
+      delimiter: " "
       precision: 3
 
+    currency:
+      format:
+        format: "%u%n"
+        unit: "£"
     human:
       format:
         delimiter: ""
@@ -130,6 +133,8 @@
         circular_dependency: "This relation would create a circular dependency"
         cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks"
         earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues"
+        must_accept_terms_and_conditions: "You must accept the Terms and Conditions"        
+        public_or_private: "You must select either public or private"
 
   actionview_instancetag_blank_option: Please select
 
@@ -142,7 +147,7 @@
   general_csv_decimal_separator: '.'
   general_csv_encoding: ISO-8859-1
   general_pdf_encoding: UTF-8
-  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
@@ -171,7 +176,7 @@
   notice_failed_to_save_time_entries: "Failed to save %{count} time entrie(s) on %{total} selected: %{ids}."
   notice_failed_to_save_members: "Failed to save member(s): %{errors}."
   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_account_pending: "Your account is now awaiting administrator approval. You will receive a notification email when your account has been activated."
   notice_default_data_loaded: Default configuration successfully loaded.
   notice_unable_delete_version: Unable to delete version.
   notice_unable_delete_time_entry: Unable to delete time log entry.
@@ -220,6 +225,15 @@
   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
@@ -260,6 +274,7 @@
   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
@@ -337,7 +352,13 @@
   field_inherit_members: Inherit members
   field_generate_password: Generate password
   field_must_change_passwd: Must change password at next logon
+  field_public_or_private: "Public or Private?"
 
+  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"
+  field_terms_and_conditions: 'Terms and Conditions:'
+  accept_terms_and_conditions: 'I have read, and agree to, the '
   setting_app_title: Application title
   setting_app_subtitle: Application subtitle
   setting_welcome_text: Welcome text
@@ -393,9 +414,12 @@
   setting_rest_api_enabled: Enable REST web service
   setting_cache_formatted_text: Cache formatted text
   setting_default_notification_option: Default notification option
+  setting_has_welcome_page: Select this to replace the project overview page with your welcome page
   setting_commit_logtime_enabled: Enable time logging
   setting_commit_logtime_activity_id: Activity for logged time
   setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
+  setting_tipoftheday_text: Tip of the Day
+  setting_notifications_text: Notifications
   setting_issue_group_assignment: Allow issue assignment to groups
   setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
   setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed
@@ -450,8 +474,8 @@
   permission_add_documents: Add documents
   permission_edit_documents: Edit documents
   permission_delete_documents: Delete documents
-  permission_manage_files: Manage files
-  permission_view_files: View files
+  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
@@ -475,34 +499,55 @@
   permission_manage_subtasks: Manage subtasks
   permission_manage_related_issues: Manage related issues
 
-  project_module_issue_tracking: Issue tracking
+  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
+  project_module_news: News updates
+  project_module_documents: Documents for download
+  project_module_files: Files for download (such as software builds)
   project_module_wiki: Wiki
-  project_module_repository: Repository
+  project_module_repository: Source code repository
   project_module_boards: Forums
   project_module_calendar: Calendar
-  project_module_gantt: Gantt
-
+  project_module_gantt: Gantt chart
+  project_module_redmine_embedded: Embedded documentation (Javadoc, Doxygen or MATLAB)
+  label_tipoftheday: Tip of the day
+  label_notifications: Important Message
+  label_ssamr_description: Research description
+  label_ssamr_details: Other Details
+  label_ssamr_institution: Institution
+  label_ssamr_description: Research description
   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_new: Start a new project
   label_project_plural: Projects
+  label_my_project_plural: My Projects
+  label_other_project_plural: Other Projects
+  label_have_no_projects: You are not a member of any projects.
   label_x_projects:
     zero:  no projects
     one:   1 project
     other: "%{count} projects"
+  label_x_downloads:
+    zero:  never downloaded
+    one:   downloaded once
+    other: "downloaded %{count} times"
   label_project_all: All Projects
   label_project_latest: Latest projects
+  label_projects_more: More projects
+  label_project_tags_all: Popular tags
+  label_projects_busy: Busy projects
+  label_projects_mature: Featured
+  label_search_projects: Search projects
+  label_institutions_busy: Active institutions
+  label_managers: Managed by
   label_issue: Issue
   label_issue_new: New issue
   label_issue_plural: Issues
   label_issue_view_all: View all issues
+  label_issue_view_all_watched: View all watched issues
   label_issues_by: "Issues by %{value}"
   label_issue_added: Issue added
   label_issue_updated: Issue updated
@@ -520,7 +565,7 @@
   label_role_anonymous: Anonymous
   label_role_non_member: Non member
   label_member: Member
-  label_member_new: New member
+  label_member_new: Add new member
   label_member_plural: Members
   label_tracker: Tracker
   label_tracker_plural: Trackers
@@ -544,8 +589,9 @@
   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_account: Account
   label_my_projects: My projects
   label_my_page_block: My page block
   label_administration: Administration
@@ -557,8 +603,12 @@
   label_last_login: Last connection
   label_registered_on: Registered on
   label_activity: Activity
+  label_activity_recent: Recent activity
+  label_activity_my_recent: Recent activity in my projects
+  label_activity_my_recent_none: No recent activity
   label_overall_activity: Overall activity
   label_user_activity: "%{value}'s activity"
+  label_institution_activity: "Activity from %{value}"
   label_new: New
   label_logged_as: Logged in as
   label_environment: Environment
@@ -585,7 +635,7 @@
   label_attachment: File
   label_attachment_new: New file
   label_attachment_delete: Delete file
-  label_attachment_plural: Files
+  label_attachment_plural: Downloads
   label_file_added: File added
   label_report: Report
   label_report_plural: Reports
@@ -593,6 +643,8 @@
   label_news_new: Add news
   label_news_plural: News
   label_news_latest: Latest news
+  label_news_site_latest: Site news
+  label_news_more: More news
   label_news_view_all: View all news
   label_news_added: News added
   label_news_comment_added: Comment added to a news
@@ -647,7 +699,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:
@@ -695,8 +747,12 @@
   label_day_plural: days
   label_repository: Repository
   label_repository_new: New repository
+  label_is_external_repository: Track an external repository
   label_repository_plural: Repositories
   label_browse: Browse
+  label_explore_projects: Explore projects
+  label_modification: "%{count} change"
+  label_modification_plural: "%{count} changes"
   label_branch: Branch
   label_tag: Tag
   label_revision: Revision
@@ -712,6 +768,7 @@
   label_latest_revision_plural: Latest revisions
   label_view_revisions: View revisions
   label_view_all_revisions: View all revisions
+  label_download_revision: Download as Zip
   label_max_size: Maximum size
   label_sort_highest: Move to top
   label_sort_higher: Move up
@@ -722,7 +779,8 @@
   label_roadmap_overdue: "%{value} late"
   label_roadmap_no_issues: No issues for this version
   label_search: Search
-  label_result_plural: Results
+  label_result_plural: Search results
+  label_matching_project_plural: Matching project names
   label_all_words: All words
   label_wiki: Wiki
   label_wiki_edit: Wiki edit
@@ -802,8 +860,9 @@
   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_time_ago: "%{age} ago"
   label_jump_to_a_project: Jump to a project...
-  label_file_plural: Files
+  label_file_plural: Downloads
   label_changeset_plural: Changesets
   label_default_columns: Default columns
   label_no_change_option: (No change)
@@ -868,8 +927,10 @@
   label_profile: Profile
   label_subtask_plural: Subtasks
   label_project_copy_notifications: Send email notifications during the project copy
-  label_principal_search: "Search for user or group:"
+  label_principal_search: "Search by name:"
   label_user_search: "Search for user:"
+  label_welcome_page: "Welcome page"
+  label_has_welcome_page: "Use your own welcome page"
   label_additional_workflow_transitions_for_author: Additional transitions allowed when the user is the author
   label_additional_workflow_transitions_for_assignee: Additional transitions allowed when the user is the assignee
   label_issues_visibility_all: All issues
@@ -879,6 +940,9 @@
   label_parent_revision: Parent
   label_child_revision: Child
   label_export_options: "%{export_format} export options"
+  label_my_colleagues: "Active projects for people I'm working with"
+  label_working_in: Working in 
+  label_no_active_colleagues: No recent activity
   label_copy_attachments: Copy attachments
   label_copy_subtasks: Copy subtasks
   label_item_position: "%{position} of %{count}"
@@ -925,7 +989,7 @@
   button_expand_all: Expand all
   button_delete: Delete
   button_create: Create
-  button_create_and_continue: Create and continue
+  button_create_and_continue: Create and Add Another
   button_test: Test
   button_edit: Edit
   button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
@@ -963,6 +1027,8 @@
   button_duplicate: Duplicate
   button_show: Show
   button_hide: Hide
+  button_welcome_page_edit: Create or edit welcome page
+  button_welcome_page_edit_this: Edit this page
   button_edit_section: Edit this section
   button_export: Export
   button_delete_my_account: Delete my account
@@ -982,6 +1048,7 @@
   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]+$
@@ -998,10 +1065,18 @@
   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_project_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter.<br />Once saved, the identifier cannot be changed.'
+  text_project_identifier_info: 'The system identifier that will form the unique part of the URL for your project.<br/>Only lower case letters (a-z), numbers, dashes and underscores are allowed. Must start with a letter.<br />Once saved, the identifier cannot be changed.'
+  text_project_homepage_info: 'Optional link to an external project page.'
   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_project_name_info: "This will be the name of your project as it appears throughout this site.<br /> You can change it at any time, in the project's settings."
+  text_project_public_info: "Public: visible to anybody browsing the site."
+  text_project_private_info: "Private: visible only to you, and to users you have added as project members."
+  text_project_visibility_info: "You can change whether your project is public or private later if you wish."
+  text_user_ssamr_description_info: 'Please describe your current research or development interests.<br/>This information will be used at registration to determine that you are a real person &ndash; so please be descriptive, or your application may be delayed or rejected.<br/>After registration, the description 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_tracker_no_workflow: No workflow defined for this tracker
   text_unallowed_characters: Unallowed characters
   text_comma_separated: Multiple values allowed (comma separated).
@@ -1035,7 +1110,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/configuration.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?"
@@ -1046,6 +1121,8 @@
   text_zoom_in: Zoom in
   text_zoom_out: Zoom out
   text_warn_on_leaving_unsaved: "The current page contains unsaved text that will be lost if you leave this page."
+  text_files_active_change: <br>Click the star to switch active status for a download on or off.<br>Active files will be shown more prominently in the download page.
+  text_settings_repo_creation: <b>Creating repository...</b></p><p>The source code repository for a project will be set up automatically within a few minutes of the project being created.</p><p>Please reload this page in five minutes, and <a href="/projects/soundsoftware-site/wiki/Help">contact us</a> if there is any problem.</p><p>If you wish to use this project to track a repository that is already hosted somewhere else, please wait until the repository has been created here and then return to this settings page to configure it.</p><p>If you don't want a repository at all, go to the Modules tab and switch it off there.
   text_scm_path_encoding_note: "Default: UTF-8"
   text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo)
   text_mercurial_repository_note: Local repository (e.g. /hgrepo, c:\hgrepo)
@@ -1060,6 +1137,14 @@
   text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours."
   text_project_closed: This project is closed and read-only.
   text_turning_multiple_off: "If you disable multiple values, multiple values will be removed in order to preserve only one value per item."
+  text_settings_repo_explanation: <b>External repositories</b><p>Normally your project's primary repository will be the Mercurial repository hosted at this site.<p>However, if you already have your project hosted somewhere else, you can specify your existing external repository's URL here &ndash; then this site will track that repository in a read-only &ldquo;mirror&rdquo; copy.  External Mercurial, git and Subversion repositories can be tracked. Note that you cannot switch to an external repository if you have already made any commits to the repository hosted here.
+  text_settings_repo_is_internal: Currently the repository hosted at this site is the primary repository for this project.
+  text_settings_repo_is_external: Currently the repository hosted at this site is a read-only copy of an external repository.
+  text_settings_repo_need_help: Please <a href="/projects/soundsoftware-site/wiki/Help">contact us</a> if you need help deciding how best to set this up.<br>We can also import complete revision history from other systems into a new primary repository for you if you wish.
+  text_has_welcome_page_info: <b>Welcome page</b><p>You can replace the standard %{overview_link} page for this project with your own welcome page.<br>This page will be editable using the project Wiki.
+  text_has_welcome_page_wiki_disabled: <b>Note:</b> You must enable the Wiki module in the %{modules_link} tab before you can create or edit this page.
+  text_binary_data: Binary data
+  text_data_too_large: Binary data, or file too large to display
 
   default_role_manager: Manager
   default_role_developer: Developer
@@ -1087,6 +1172,16 @@
   enumeration_doc_categories: Document categories
   enumeration_activities: Activities (time tracking)
   enumeration_system_activity: System Activity
+
+  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}"
   description_filter: Filter
   description_search: Searchfield
   description_choose_project: Projects
@@ -1106,3 +1201,8 @@
   description_date_from: Enter start date
   description_date_to: Enter end date
   text_repository_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.'
+
+  text_who_can_register: '<h3>Who can register here?</h3><p>You can register if you are a researcher in the audio and music field are you are:<ul><li>working in the UK,</li><li>working with UK researchers who are also using this site, <i>or</i></li><li>working on projects having an audience within the UK research community.</li></ul></p><p>Please ensure you provide enough information in the description below to establish which of these is the case.</p><p>(You do not need to register in order to download or use code from this site &mdash; only to host your own code here.)</p>'
+
+  field_no_tags: No tags
+
--- a/config/locales/es.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/es.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -466,6 +466,7 @@
   label_help: Ayuda
   label_history: Histórico
   label_home: Inicio
+  label_home_heading: Inicio
   label_in: en
   label_in_less_than: en menos que
   label_in_more_than: en más que
--- a/config/locales/eu.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/eu.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -460,6 +460,7 @@
   label_login_with_open_id_option: edo OpenID-rekin saioa hasi
   label_password_lost: Pasahitza galduta
   label_home: Hasiera
+  label_home_heading: Hasiera
   label_my_page: Nire orria
   label_my_account: Nire kontua
   label_my_projects: Nire proiektuak
--- a/config/locales/fi.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/fi.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -365,6 +365,7 @@
   label_register: Rekisteröidy
   label_password_lost: Hukattu salasana
   label_home: Koti
+  label_home_heading: Koti
   label_my_page: Omasivu
   label_my_account: Oma tili
   label_my_projects: Omat projektit
--- a/config/locales/fr.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/fr.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -539,6 +539,7 @@
   label_login_with_open_id_option: S'authentifier avec OpenID
   label_password_lost: Mot de passe perdu
   label_home: Accueil
+  label_home_heading: Accueil
   label_my_page: Ma page
   label_my_account: Mon compte
   label_my_projects: Mes projets
--- a/config/locales/gl.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/gl.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -440,6 +440,7 @@
   label_help: Axuda
   label_history: Histórico
   label_home: Inicio
+  label_home_heading: Inicio
   label_in: en
   label_in_less_than: en menos que
   label_in_more_than: en mais que
--- a/config/locales/he.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/he.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -481,6 +481,7 @@
   label_login_with_open_id_option: או התחבר באמצעות OpenID
   label_password_lost: אבדה הסיסמה?
   label_home: דף הבית
+  label_home_heading: דף הבית
   label_my_page: הדף שלי
   label_my_account: החשבון שלי
   label_my_projects: הפרויקטים שלי
--- a/config/locales/hr.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/hr.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -452,6 +452,7 @@
   label_login_with_open_id_option: or login with OpenID
   label_password_lost: Izgubljena zaporka
   label_home: Početna stranica
+  label_home_heading: Početna stranica
   label_my_page: Moja stranica
   label_my_account: Moj profil
   label_my_projects: Moji projekti
--- a/config/locales/hu.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/hu.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -381,6 +381,7 @@
   label_register: Regisztráljon
   label_password_lost: Elfelejtett jelszó
   label_home: Kezdőlap
+  label_home_heading: Kezdőlap
   label_my_page: Saját kezdőlapom
   label_my_account: Fiókom adatai
   label_my_projects: Saját projektem
--- a/config/locales/id.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/id.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -445,6 +445,7 @@
   label_login_with_open_id_option: atau login menggunakan OpenID
   label_password_lost: Lupa password
   label_home: Halaman depan
+  label_home_heading: Halaman depan
   label_my_page: Beranda
   label_my_account: Akun saya
   label_my_projects: Proyek saya
--- a/config/locales/it.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/it.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -325,6 +325,7 @@
   label_register: Registrati
   label_password_lost: Password dimenticata
   label_home: Home
+  label_home_heading: Home
   label_my_page: Pagina personale
   label_my_account: Il mio utente
   label_my_projects: I miei progetti
--- a/config/locales/ja.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/ja.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -524,6 +524,7 @@
   label_login_with_open_id_option: またはOpenIDでログインする
   label_password_lost: パスワードの再発行
   label_home: ホーム
+  label_home_heading: ホーム
   label_my_page: マイページ
   label_my_account: 個人設定
   label_my_projects: マイプロジェクト
--- a/config/locales/ko.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/ko.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -483,6 +483,7 @@
   label_login_with_open_id_option: 또는 OpenID로 로그인
   label_password_lost: 비밀번호 찾기
   label_home: 초기화면
+  label_home_heading: 초기화면
   label_my_page: 내 페이지
   label_my_account: 내 계정
   label_my_projects: 내 프로젝트
--- a/config/locales/lt.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/lt.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -565,6 +565,7 @@
   label_login_with_open_id_option: arba prisijunkite su OpenID
   label_password_lost: Prarastas slaptažodis
   label_home: Pagrindinis
+  label_home_heading: Pagrindinis
   label_my_page: Mano puslapis
   label_my_account: Mano paskyra
   label_my_projects: Mano projektai
--- a/config/locales/lv.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/lv.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -455,6 +455,7 @@
   label_login_with_open_id_option: vai pieslēgties ar OpenID
   label_password_lost: Nozaudēta parole
   label_home: Sākums
+  label_home_heading: Sākums
   label_my_page: Mana lapa
   label_my_account: Mans konts
   label_my_projects: Mani projekti
--- a/config/locales/mk.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/mk.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -473,6 +473,7 @@
   label_login_with_open_id_option: или најави се со OpenID
   label_password_lost: Изгубена лозинка
   label_home: Почетна
+  label_home_heading: Почетна
   label_my_page: Мојата страна
   label_my_account: Мојот профил
   label_my_projects: Мои проекти
--- a/config/locales/mn.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/mn.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -461,6 +461,7 @@
   label_login_with_open_id_option: or login with OpenID
   label_password_lost: Нууц үгээ алдсан
   label_home: Нүүр
+  label_home_heading: Нүүр
   label_my_page: Миний хуудас
   label_my_account: Миний данс
   label_my_projects: Миний төслүүд
--- a/config/locales/nl.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/nl.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -411,6 +411,7 @@
   label_help: Help
   label_history: Geschiedenis
   label_home: Home
+  label_home_heading: Home
   label_in: in
   label_in_less_than: in minder dan
   label_in_more_than: in meer dan
--- a/config/locales/no.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/no.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -352,6 +352,7 @@
   label_register: Registrer
   label_password_lost: Mistet passord
   label_home: Hjem
+  label_home_heading: Hjem
   label_my_page: Min side
   label_my_account: Min konto
   label_my_projects: Mine prosjekter
--- a/config/locales/pl.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/pl.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -443,6 +443,7 @@
   label_help: Pomoc
   label_history: Historia
   label_home: Główna
+  label_home_heading: Główna
   label_in: w
   label_in_less_than: mniejsze niż
   label_in_more_than: większe niż
--- a/config/locales/pt-BR.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/pt-BR.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -391,6 +391,7 @@
   label_register: Cadastre-se
   label_password_lost: Perdi minha senha
   label_home: Página inicial
+  label_home_heading: Página inicial
   label_my_page: Minha página
   label_my_account: Minha conta
   label_my_projects: Meus projetos
--- a/config/locales/pt.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/pt.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -375,6 +375,7 @@
   label_register: Registar
   label_password_lost: Perdi a palavra-chave
   label_home: Página Inicial
+  label_home_heading: Página Inicial
   label_my_page: Página Pessoal
   label_my_account: Minha conta
   label_my_projects: Meus projectos
--- a/config/locales/ro.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/ro.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -423,6 +423,7 @@
   label_login_with_open_id_option: sau autentificare cu OpenID
   label_password_lost: Parolă uitată
   label_home: Acasă
+  label_home_heading: Acasă
   label_my_page: Pagina mea
   label_my_account: Contul meu
   label_my_projects: Proiectele mele
--- a/config/locales/ru.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/ru.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -516,6 +516,7 @@
   label_help: Помощь
   label_history: История
   label_home: Домашняя страница
+  label_home_heading: Домашняя страница
   label_incoming_emails: Приём сообщений
   label_index_by_date: История страниц
   label_index_by_title: Оглавление
--- a/config/locales/sl.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/sl.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -417,6 +417,7 @@
   label_register: Registracija
   label_password_lost: Spremeni geslo
   label_home: Domov
+  label_home_heading: Domov
   label_my_page: Moja stran
   label_my_account: Moj račun
   label_my_projects: Moji projekti
--- a/config/locales/sr-YU.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/sr-YU.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -473,6 +473,7 @@
   label_login_with_open_id_option: ili prijava sa OpenID
   label_password_lost: Izgubljena lozinka
   label_home: Početak
+  label_home_heading: Početak
   label_my_page: Moja stranica
   label_my_account: Moj nalog
   label_my_projects: Moji projekti
--- a/config/locales/sr.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/sr.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -471,6 +471,7 @@
   label_login_with_open_id_option: или пријава са OpenID
   label_password_lost: Изгубљена лозинка
   label_home: Почетак
+  label_home_heading: Почетак
   label_my_page: Моја страница
   label_my_account: Мој налог
   label_my_projects: Моји пројекти
--- a/config/locales/sv.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/sv.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -580,6 +580,7 @@
   label_login_with_open_id_option: eller logga in med OpenID
   label_password_lost: Glömt lösenord
   label_home: Hem
+  label_home_heading: Hem
   label_my_page: Min sida
   label_my_account: Mitt konto
   label_my_projects: Mina projekt
--- a/config/locales/th.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/th.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -357,6 +357,7 @@
   label_register: ลงทะเบียน
   label_password_lost: ลืมรหัสผ่าน
   label_home: หน้าแรก
+  label_home_heading: หน้าแรก
   label_my_page: หน้าของฉัน
   label_my_account: บัญชีของฉัน
   label_my_projects: โครงการของฉัน
--- a/config/locales/tr.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/tr.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -373,6 +373,7 @@
   label_register: Kayıt
   label_password_lost: Parolamı unuttum
   label_home: Anasayfa
+  label_home_heading: Anasayfa
   label_my_page: Kişisel Sayfam
   label_my_account: Hesabım
   label_my_projects: Projelerim
--- a/config/locales/uk.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/uk.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -332,6 +332,7 @@
   label_register: Зареєструватися
   label_password_lost: Забули пароль
   label_home: Домашня сторінка
+  label_home_heading: Домашня сторінка
   label_my_page: Моя сторінка
   label_my_account: Мій обліковий запис
   label_my_projects: Мої проекти
--- a/config/locales/vi.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/vi.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -419,6 +419,7 @@
   label_register: Đăng ký
   label_password_lost: Phục hồi mật mã
   label_home: Trang chính
+  label_home_heading: Trang chính
   label_my_page: Trang riêng
   label_my_account: Cá nhân
   label_my_projects: Dự án của bạn
--- a/config/locales/zh-TW.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/zh-TW.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -629,6 +629,7 @@
   label_login_with_open_id_option: 或使用 OpenID 登入
   label_password_lost: 遺失密碼
   label_home: 網站首頁
+  label_home_heading: 網站首頁
   label_my_page: 帳戶首頁
   label_my_account: 我的帳戶
   label_my_projects: 我的專案
--- a/config/locales/zh.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/locales/zh.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -488,6 +488,7 @@
   label_login_with_open_id_option: 或使用OpenID登录
   label_password_lost: 忘记密码
   label_home: 主页
+  label_home_heading: 主页
   label_my_page: 我的工作台
   label_my_account: 我的帐号
   label_my_projects: 我的项目
--- a/config/routes.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/routes.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -89,6 +89,9 @@
   post 'issues/:object_id/watchers', :to => 'watchers#create', :object_type => 'issue'
   delete 'issues/:object_id/watchers/:user_id' => 'watchers#destroy', :object_type => 'issue'
 
+  match 'projects/:id/settings/:tab', :to => "projects#settings"
+  match 'projects/:id/overview', :to => "projects#overview"
+
   resources :projects do
     member do
       get 'settings(/:tab)', :action => 'settings', :as => 'settings'
@@ -108,6 +111,14 @@
       end
     end
 
+    shallow do
+      resources :members, :controller => 'members', :only => [:index, :show, :new, :create, :update, :destroy] do
+        collection do
+          get 'autocomplete'
+        end
+      end
+    end
+
     resource :enumerations, :controller => 'project_enumerations', :only => [:update, :destroy]
 
     get 'issues/:copy_from/copy', :to => 'issues#new', :as => 'copy_issue'
@@ -189,6 +200,9 @@
   end
   match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete
 
+  # changed this route to ensure compatibility with Rails 3 --lf.20130109
+  match 'explore' => 'projects#explore'
+
   resources :queries, :except => [:show]
 
   resources :news, :only => [:index, :show, :edit, :update, :destroy]
@@ -272,6 +286,7 @@
   get 'attachments/:id/:filename', :to => 'attachments#show', :id => /\d+/, :filename => /.*/, :as => 'named_attachment'
   get 'attachments/download/:id/:filename', :to => 'attachments#download', :id => /\d+/, :filename => /.*/, :as => 'download_named_attachment'
   get 'attachments/download/:id', :to => 'attachments#download', :id => /\d+/
+  get 'attachments/toggle_active/:id', :to => 'attachments#toggle_active', :id => /\d+/
   get 'attachments/thumbnail/:id(/:size)', :to => 'attachments#thumbnail', :id => /\d+/, :size => /\d+/, :as => 'thumbnail'
   resources :attachments, :only => [:show, :destroy]
 
@@ -336,6 +351,8 @@
 
   match 'sys/projects', :to => 'sys#projects', :via => :get
   match 'sys/projects/:id/repository', :to => 'sys#create_project_repository', :via => :post
+  match 'sys/projects/:id/repository_cache.:format', :controller => 'sys', :action => 'clear_repository_cache', :conditions => { :method => :post }
+  match 'sys/projects/:id/embedded.:format', :controller => 'sys', :action => 'set_embedded_active', :conditions => { :method => :post }
   match 'sys/fetch_changesets', :to => 'sys#fetch_changesets', :via => :get
 
   match 'uploads', :to => 'attachments#upload', :via => :post
--- a/config/settings.yml	Tue Sep 09 09:29:00 2014 +0100
+++ b/config/settings.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -25,6 +25,10 @@
   default: Project management
 welcome_text:
   default:
+tipoftheday_text:
+    default:    
+notifications_text:
+    default:
 login_required:
   default: 0
 self_registration:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/console/db/seeds.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+# This file should contain all the record creation needed to seed the database with its default values.
+# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
+#
+# Examples:
+#   
+#   cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
+#   Major.create(:name => 'Daley', :city => cities.first)
+
+puts "this is a test"
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20101123161346_create_ssamr_user_details.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,14 @@
+class CreateSsamrUserDetails < ActiveRecord::Migration
+  def self.up
+    create_table :ssamr_user_details do |t|
+      t.integer :user_id
+      t.text :description 
+      t.text :university
+    end
+  end
+
+  def self.down
+    drop_table :ssamr_user_details
+  end
+  
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20101216140621_create_institutions.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,13 @@
+class CreateInstitutions < ActiveRecord::Migration
+  def self.up
+    create_table :institutions do |t|
+      t.string :name
+
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :institutions
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20101216145813_fix_university_name_in_ssamr_details_table.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,10 @@
+class FixUniversityNameInSsamrDetailsTable < ActiveRecord::Migration
+  def self.up
+    rename_column :ssamr_user_details, :university, :institution_id
+  end
+
+  def self.down
+    # there's no need to rollback the name of this column
+    # because it was not used previously
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110126153504_add_other_institution_column_to_ssamr_user_details.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+class AddOtherInstitutionColumnToSsamrUserDetails < ActiveRecord::Migration
+  def self.up
+    add_column :ssamr_user_details, :other_institution, :string
+  end
+
+  def self.down
+    remove_column :ssamr_user_details, :other_institution
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110127161758_add_institution_type_column_to_ssamr_user_details.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+class AddInstitutionTypeColumnToSsamrUserDetails < ActiveRecord::Migration
+  def self.up
+     add_column :ssamr_user_details, :institution_type, :boolean
+  end
+
+  def self.down
+    remove_column :ssamr_user_details, :institution_type
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110202170156_add_order_column_to_institutions.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+class AddOrderColumnToInstitutions < ActiveRecord::Migration
+  def self.up
+       add_column :institutions, :order, :integer
+  end
+
+  def self.down
+      remove_column :institutions, :order
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110207142856_add_ext_rep_to_repositories.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,11 @@
+class AddExtRepToRepositories < ActiveRecord::Migration
+  def self.up
+    add_column :repositories, :is_external, :bool
+    add_column :repositories, :external_url, :string
+  end
+
+  def self.down
+    remove_column :repositories, :is_external
+    remove_column :repositories, :external_url
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110303152903_add_active_column_to_attachments.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+class AddActiveColumnToAttachments < ActiveRecord::Migration
+  def self.up
+    add_column :attachments, :active, :boolean
+  end
+
+  def self.down
+    remove_column :attachments, :active
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110331152140_add_has_welcome_page_to_projects.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+class AddHasWelcomePageToProjects < ActiveRecord::Migration
+  def self.up
+    add_column :projects, :has_welcome_page, :boolean
+  end
+
+  def self.down
+    remove_column :projects, :has_welcome_page
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/seed_data/institutions.txt	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,141 @@
+University of Aberdeen|1
+University of Abertay Dundee |2
+Aberystwyth University|3
+Anglia Ruskin University|4
+University of the Arts London|5
+Aston University|6
+Bangor University|7
+University of Bath|8
+Bath Spa University|9
+University of Bedfordshire|10
+Birkbeck, University of London|11
+University of Birmingham|12
+Birmingham City University|13
+Birmingham Conservatoire|14
+University Centre at Blackburn College|15
+University of Bolton|16
+Bournemouth University|17
+University of Bradford|18
+University of Brighton|19
+University of Bristol|20
+Brunel University|21
+University of Buckingham|22
+Buckinghamshire New University|23
+University of Cambridge|24
+Institute of Cancer Research, University of London|25
+Canterbury Christ Church University|26
+Cardiff University|27
+University of Central Lancashire|28
+Central School of Speech and Drama, University of London|29
+University of Chester|30
+University of Chichester|31
+City University London|32
+Courtauld Institute of Art|33
+Coventry University|34
+Cranfield University|35
+University for the Creative Arts|36
+University of Cumbria|37
+De Montfort University|38
+University of Derby|39
+University of Dundee|40
+Durham University|41
+University of East Anglia|42
+University of East London|43
+Edge Hill University|44
+University of Edinburgh|45
+Edinburgh Napier University|46
+Institute of Education, University of London|47
+University of Essex|48
+University of Exeter|49
+University of Glamorgan|50
+University of Glasgow|51
+Glasgow Caledonian University|52
+University of Gloucestershire|53
+Goldsmiths, University of London|54
+University of Greenwich|55
+Glynd&#373;r University|56
+Heriot-Watt University|57
+University of Hertfordshire|58
+Heythrop College|59
+University of Huddersfield|60
+University of Hull|61
+Hull York Medical School|62
+Imperial College London|63
+Keele University|64
+University of Kent|65
+King's College London|66
+Kingston University|67
+Lancaster University|68
+University of Leeds|69
+Leeds Metropolitan University|70
+University of Leicester|71
+University of Lincoln|72
+University of Liverpool|73
+Liverpool Hope University|74
+Liverpool John Moores University|75
+London Business School|76
+London College of Music|77
+London Metropolitan University|78
+London School of Economics and Political Science|79
+London School of Hygiene and Tropical Medicine|80
+London South Bank University|81
+Loughborough University|82
+University of Manchester|83
+Manchester Metropolitan University|84
+Middlesex University|85
+Newcastle University|86
+University of Northampton|87
+Northumbria University|88
+University of Nottingham|89
+Nottingham Trent University|90
+The Open University|91
+University of Oxford|92
+Oxford Brookes University|93
+Peninsula College of Medicine and Dentistry|94
+University of Plymouth|95
+University of Portsmouth|96
+Queen's University Belfast|97
+Queen Margaret University|98
+Queen Mary, University of London|99
+University of Reading|100
+The Robert Gordon University, Aberdeen|101
+Roehampton University|102
+Royal Academy of Music|103
+Royal College of Art|104
+Royal Holloway, University of London|105
+Royal Veterinary College|106
+University of St Andrews|107
+St George's, University of London|108
+University of Salford|109
+School of Advanced Study, University of London|110
+School of Oriental and African Studies|111
+School of Pharmacy, University of London|112
+University of Sheffield|113
+Sheffield Hallam University|114
+University of Southampton|115
+Southampton Solent University|116
+Staffordshire University|117
+University of Stirling|118
+University of Strathclyde|119
+University of Sunderland|120
+University of Surrey|121
+University of Sussex|122
+Swansea Metropolitan University|123
+Swansea University|124
+University of Teesside|125
+Thames Valley University|126
+University of Ulster|127
+University College London|128
+University of Wales|129
+University of Wales Institute, Cardiff|130
+University of Wales, Newport|131
+University of Wales, Trinity Saint David|132
+University of Warwick|133
+University of Westminster|134
+University of the West of England|135
+University of the West of Scotland|136
+University of Winchester|137
+University of Wolverhampton|138
+University of Worcester|139
+University of York|140
+York St John University|141
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/seeds.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,27 @@
+# This file should contain all the record creation needed to seed the database with its default values.
+# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
+#
+# Examples:
+#   
+#   cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
+#   Major.create(:name => 'Daley', :city => cities.first)
+
+def truncate_table(table_name)
+  quoted = connection.quote_table_name(table_name)
+  connection.execute("TRUNCATE #{quoted}")
+end
+
+def connection
+  ActiveRecord::Base.connection
+end
+
+truncate_table('institutions')
+
+open("db/seed_data/institutions.txt") do |institutions|
+  institutions.read.each_line do |institution|
+    inst=institution.split('|')
+    
+    
+    Institution.create(:name => inst[0].chomp, :order => inst[1].chomp)
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/mercurial/redminehelper.py	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,91 @@
+# redminehelper: Redmine helper extension for Mercurial
+# it's a draft to show a possible way to explore repository by the Redmine overhaul patch
+# see: http://www.redmine.org/issues/4455
+#
+# Copyright 2010 Alessio Franceschelli (alefranz.net)
+# Copyright 2010 Yuya Nishihara <yuya@tcha.org>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''command to list revision of each file
+'''
+
+import re, time
+from mercurial import cmdutil, commands, node, error
+
+SPECIAL_TAGS = ('tip',)
+
+def rhsummary(ui, repo, **opts):
+    """output the summary of the repository"""
+    # see mercurial/commands.py:tip
+    ui.write(':tip: rev node\n')
+    tipctx = repo[len(repo) - 1]
+    ui.write('%d %s\n' % (tipctx.rev(), tipctx))
+
+    # see mercurial/commands.py:root
+    ui.write(':root: path\n')
+    ui.write(repo.root + '\n')
+
+    # see mercurial/commands.py:tags
+    ui.write(':tags: rev node name\n')
+    for t, n in reversed(repo.tagslist()):
+        if t in SPECIAL_TAGS:
+            continue
+        try:
+            r = repo.changelog.rev(n)
+        except error.LookupError:
+            r = -1
+        ui.write('%d %s %s\n' % (r, node.short(n), t))
+
+    # see mercurial/commands.py:branches
+    def iterbranches():
+        for t, n in repo.branchtags().iteritems():
+            yield t, n, repo.changelog.rev(n)
+
+    ui.write(':branches: rev node name\n')
+    for t, n, r in sorted(iterbranches(), key=lambda e: e[2], reverse=True):
+        if repo.lookup(r) in repo.branchheads(t, closed=False):
+            ui.write('%d %s %s\n' % (r, node.short(n), t))  # only open branch
+
+def rhentries(ui, repo, path='', **opts):
+    """output the entries of the specified directory"""
+    rev = opts.get('rev')
+    pathprefix = (path.rstrip('/') + '/').lstrip('/')
+
+    # TODO: clean up
+    dirs, files = {}, {}
+    mf = repo[rev].manifest()
+    for f in repo[rev]:
+        if not f.startswith(pathprefix):
+            continue
+
+        name = re.sub(r'/.*', '', f[len(pathprefix):])
+        if '/' in f[len(pathprefix):]:
+            dirs[name] = (name,)
+        else:
+            try:
+                fctx = repo.filectx(f, fileid=mf[f])
+                ctx = fctx.changectx()
+                tm, tzoffset = ctx.date()
+                localtime = int(tm) + tzoffset - time.timezone
+                files[name] = (ctx.rev(), node.short(ctx.node()), localtime,
+                               fctx.size(), name)
+            except LookupError:  # TODO: when this occurs?
+                pass
+
+    ui.write(':dirs: name\n')
+    for n, v in sorted(dirs.iteritems(), key=lambda e: e[0]):
+        ui.write(' '.join(v) + '\n')
+
+    ui.write(':files: rev node time size name\n')
+    for n, v in sorted(files.iteritems(), key=lambda e: e[0]):
+        ui.write(' '.join(str(e) for e in v) + '\n')
+
+
+cmdtable = {
+    'rhsummary': (rhsummary, [], 'hg rhsummary'),
+    'rhentries': (rhentries,
+                  [('r', 'rev', '', 'show the specified revision')],
+                  'hg rhentries [path]'),
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/SoundSoftware-unsalted.pm	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,469 @@
+package Apache::Authn::SoundSoftware;
+
+=head1 Apache::Authn::SoundSoftware
+
+SoundSoftware - a mod_perl module for Apache authentication against a
+Redmine database and optional LDAP implementing the access control
+rules required for the SoundSoftware.ac.uk repository site.
+
+=head1 SYNOPSIS
+
+This module is closely based on the Redmine.pm authentication module
+provided with Redmine.  It is intended to be used for authentication
+in front of a repository service such as hgwebdir.
+
+Requirements:
+
+1. Clone/pull from repo for public project: Any user, no
+authentication required
+
+2. Clone/pull from repo for private project: Project members only
+
+3. Push to repo for public project: "Permitted" users only (this
+probably means project members who are also identified in the hgrc web
+section for the repository and so will be approved by hgwebdir?)
+
+4. Push to repo for private project: "Permitted" users only (as above)
+
+5. Push to any repo that is tracking an external repo: Refused always
+
+=head1 INSTALLATION
+
+Debian/ubuntu:
+
+  apt-get install libapache-dbi-perl libapache2-mod-perl2 \
+    libdbd-mysql-perl libauthen-simple-ldap-perl libio-socket-ssl-perl
+
+Note that LDAP support is hardcoded "on" in this script (it is
+optional in the original Redmine.pm).
+
+=head1 CONFIGURATION
+
+   ## This module has to be in your perl path
+   ## eg:  /usr/local/lib/site_perl/Apache/Authn/SoundSoftware.pm
+   PerlLoadModule Apache::Authn::SoundSoftware
+
+   # Example when using hgwebdir
+   ScriptAlias / "/var/hg/hgwebdir.cgi/"
+
+   <Location />
+       AuthName "Mercurial"
+       AuthType Basic
+       Require valid-user
+       PerlAccessHandler Apache::Authn::SoundSoftware::access_handler
+       PerlAuthenHandler Apache::Authn::SoundSoftware::authen_handler
+       SoundSoftwareDSN "DBI:mysql:database=redmine;host=localhost"
+       SoundSoftwareDbUser "redmine"
+       SoundSoftwareDbPass "password"
+       Options +ExecCGI
+       AddHandler cgi-script .cgi
+       ## Optional where clause (fulltext search would be slow and
+       ## database dependant).
+       # SoundSoftwareDbWhereClause "and members.role_id IN (1,2)"
+       ## Optional prefix for local repository URLs
+       # SoundSoftwareRepoPrefix "/var/hg/"
+  </Location>
+
+See the original Redmine.pm for further configuration notes.
+
+=cut
+
+use strict;
+use warnings FATAL => 'all', NONFATAL => 'redefine';
+
+use DBI;
+use Digest::SHA1;
+use Authen::Simple::LDAP;
+use Apache2::Module;
+use Apache2::Access;
+use Apache2::ServerRec qw();
+use Apache2::RequestRec qw();
+use Apache2::RequestUtil qw();
+use Apache2::Const qw(:common :override :cmd_how);
+use APR::Pool ();
+use APR::Table ();
+
+my @directives = (
+  {
+    name => 'SoundSoftwareDSN',
+    req_override => OR_AUTHCFG,
+    args_how => TAKE1,
+    errmsg => 'Dsn in format used by Perl DBI. eg: "DBI:Pg:dbname=databasename;host=my.db.server"',
+  },
+  {
+    name => 'SoundSoftwareDbUser',
+    req_override => OR_AUTHCFG,
+    args_how => TAKE1,
+  },
+  {
+    name => 'SoundSoftwareDbPass',
+    req_override => OR_AUTHCFG,
+    args_how => TAKE1,
+  },
+  {
+    name => 'SoundSoftwareDbWhereClause',
+    req_override => OR_AUTHCFG,
+    args_how => TAKE1,
+  },
+  {
+    name => 'SoundSoftwareRepoPrefix',
+    req_override => OR_AUTHCFG,
+    args_how => TAKE1,
+  },
+);
+
+sub SoundSoftwareDSN { 
+    my ($self, $parms, $arg) = @_;
+    $self->{SoundSoftwareDSN} = $arg;
+    my $query = "SELECT 
+                 hashed_password, 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, $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) {
+		if ($hashed_password eq $pass_digest) {
+		    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;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/SoundSoftware.pm	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,571 @@
+package Apache::Authn::SoundSoftware;
+
+=head1 Apache::Authn::SoundSoftware
+
+SoundSoftware - a mod_perl module for Apache authentication against a
+Redmine database and optional LDAP implementing the access control
+rules required for the SoundSoftware.ac.uk repository site.
+
+=head1 SYNOPSIS
+
+This module is closely based on the Redmine.pm authentication module
+provided with Redmine.  It is intended to be used for authentication
+in front of a repository service such as hgwebdir.
+
+Requirements:
+
+1. Clone/pull from repo for public project: Any user, no
+authentication required
+
+2. Clone/pull from repo for private project: Project members only
+
+3. Push to repo for public project: "Permitted" users only (this
+probably means project members who are also identified in the hgrc web
+section for the repository and so will be approved by hgwebdir?)
+
+4. Push to repo for private project: "Permitted" users only (as above)
+
+5. Push to any repo that is tracking an external repo: Refused always
+
+=head1 INSTALLATION
+
+Debian/ubuntu:
+
+  apt-get install libapache-dbi-perl libapache2-mod-perl2 \
+    libdbd-mysql-perl libauthen-simple-ldap-perl libio-socket-ssl-perl
+
+Note that LDAP support is hardcoded "on" in this script (it is
+optional in the original Redmine.pm).
+
+=head1 CONFIGURATION
+
+   ## This module has to be in your perl path
+   ## eg:  /usr/local/lib/site_perl/Apache/Authn/SoundSoftware.pm
+   PerlLoadModule Apache::Authn::SoundSoftware
+
+   # Example when using hgwebdir
+   ScriptAlias / "/var/hg/hgwebdir.cgi/"
+
+   <Location />
+       AuthName "Mercurial"
+       AuthType Basic
+       Require valid-user
+       PerlAccessHandler Apache::Authn::SoundSoftware::access_handler
+       PerlAuthenHandler Apache::Authn::SoundSoftware::authen_handler
+       SoundSoftwareDSN "DBI:mysql:database=redmine;host=localhost"
+       SoundSoftwareDbUser "redmine"
+       SoundSoftwareDbPass "password"
+       Options +ExecCGI
+       AddHandler cgi-script .cgi
+       ## Optional where clause (fulltext search would be slow and
+       ## database dependant).
+       # SoundSoftwareDbWhereClause "and members.role_id IN (1,2)"
+       ## Optional prefix for local repository URLs
+       # SoundSoftwareRepoPrefix "/var/hg/"
+  </Location>
+
+See the original Redmine.pm for further configuration notes.
+
+=cut
+
+use strict;
+use warnings FATAL => 'all', NONFATAL => 'redefine';
+
+use DBI;
+use Digest::SHA;
+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,
+  },
+  {
+    name => 'SoundSoftwareSslRequired',
+    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 SoundSoftwareSslRequired { set_val('SoundSoftwareSslRequired', @_); }
+
+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);
+
+    # We want to delegate most of the work to the authentication
+    # handler (to ensure that user is asked to login even for 
+    # nonexistent projects -- so they can't tell whether a private
+    # project exists or not without authenticating). So 
+    # 
+    # * if the project is public
+    #   - if the method is read-only
+    #     + set handler to OK, no auth needed
+    #   - if the method is not read-only
+    #     + if the repo is read-only, return forbidden
+    #     + else require auth
+    # * if the project is not public or does not exist
+    #     + require auth
+    #
+    # If we are requiring auth and are not currently https, and
+    # https is required, then we must return a redirect to https
+    # instead of an OK.
+
+    my $status = get_project_status($dbh, $project_id, $r);
+    my $readonly = project_repo_is_readonly($dbh, $project_id, $r);
+
+    $dbh->disconnect();
+    undef $dbh;
+
+    my $auth_ssl_reqd = will_require_ssl_auth($r);
+
+    if ($status == 1) { # public
+
+	print STDERR "SoundSoftware.pm:$$: Project is public\n";
+
+	if (!defined $read_only_methods{$method}) {
+
+	    print STDERR "SoundSoftware.pm:$$: Method is not read-only\n";
+
+	    if ($readonly) {
+		print STDERR "SoundSoftware.pm:$$: Project repo is read-only, refusing access\n";
+		return FORBIDDEN;
+	    } else {
+		print STDERR "SoundSoftware.pm:$$: Project repo is read-write, auth required\n";
+		# fall through, this is the normal case
+	    }
+
+        } elsif ($auth_ssl_reqd and $r->unparsed_uri =~ m/cmd=branchmap/) {
+
+            # A hac^H^H^Hspecial case. We want to ensure we switch to
+            # https (if it will be necessarily for authentication) 
+            # before the first POST request, and this is what I think
+            # will give us suitable warning for Mercurial.
+
+            print STDERR "SoundSoftware.pm:$$: Switching to HTTPS in preparation\n";
+            # fall through, this is the normal case
+
+	} else {
+	    # Public project, read-only method -- this is the only
+	    # case we can decide for certain to accept in this function
+	    print STDERR "SoundSoftware.pm:$$: Method is read-only, no restriction here\n";
+	    $r->set_handlers(PerlAuthenHandler => [\&OK]);
+	    return OK;
+	}
+
+    } else { # status != 1, i.e. nonexistent or private -- equivalent here
+
+	print STDERR "SoundSoftware.pm:$$: Project is private or nonexistent, auth required\n";
+	# fall through
+    }
+
+    if ($auth_ssl_reqd) {
+        my $redir_to = "https://" . $r->hostname() . $r->unparsed_uri();
+        print STDERR "SoundSoftware.pm:$$: Need to switch to HTTPS, redirecting to $redir_to\n";
+        $r->headers_out->add('Location' => $redir_to);
+        return REDIRECT;
+    } else {
+        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 $status = get_project_status($dbh, $project_id, $r);
+    if ($status == 0) {
+	# nonexistent, behave like private project you aren't a member of
+	print STDERR "SoundSoftware.pm:$$: Project doesn't exist, not permitted\n";
+	$dbh->disconnect();
+	undef $dbh;
+	$r->note_auth_failure();
+	return AUTH_REQUIRED;
+    }
+
+    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 will_require_ssl_auth {
+    my $r = shift;
+
+    my $cfg = Apache2::Module::get_config
+        (__PACKAGE__, $r->server, $r->per_dir_config);
+
+    if ($cfg->{SoundSoftwareSslRequired} eq "on") {
+        if ($r->dir_config('HTTPS') eq "on") {
+            # already have ssl
+            return 0;
+        } else {
+            # require ssl for auth, don't have it yet
+            return 1;
+        }
+    } elsif ($cfg->{SoundSoftwareSslRequired} eq "off") {
+        # don't require ssl for auth
+        return 0;
+    } else {
+        print STDERR "WARNING: SoundSoftware.pm:$$: SoundSoftwareSslRequired should be either 'on' or 'off'\n";
+        # this is safer
+        return 1;
+    }
+}
+
+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::SHA::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::SHA::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;
+                last if ($ret);
+	    }
+	} 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;
+    } elsif ($name =~ m/^\s*$/) {
+	# empty or whitespace
+	$name = $project_id;
+    }
+    
+    if ($name =~ m/^\s*$/) {
+        # nothing even in $project_id -- probably a nonexistent project.
+        # use repo name instead (don't want to admit to user that project
+        # doesn't exist)
+        my $location = $r->location;
+        my ($repo) = $r->uri =~ m{$location/*([^/]*)};
+        $name = $repo;
+    }
+
+#    my $realm = '"Mercurial repository for ' . "'$name'" . '"';
+# see #577:
+    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;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/convert-external-repos.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,185 @@
+#!/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 '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
+
+$mirrordir    = '/var/mirror'
+
+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';           puts "Read source for documentation"; exit 
+    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?)
+  puts "Read source for documentation"; exit
+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}"
+  self.format = :xml
+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 ActiveResource::ForbiddenAccess
+  log("Request was denied by your Redmine server. Make sure that 'WS for repository management' is enabled in application settings and that you provided the correct 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)
+  
+  $cache_clearance_file = File.join($mirrordir, project.identifier, 'url_changed')
+  if File.file?($cache_clearance_file)
+    log("\tproject repo url has changed, requesting cache clearance")
+    if project.post(:repository_cache, :key => $api_key)
+      File.delete($cache_clearance_file)
+    end
+  end
+
+end
+  
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/doxysafe.pl	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,220 @@
+#!/usr/bin/perl -w
+
+# Read a Doxyfile and print it out again to stdout, with only
+# whitelisted keys in it and with some keys set to pre-fixed values.
+#
+# Note that OUTPUT_DIRECTORY is not included; it should be added by
+# the caller
+
+use strict;
+
+my $txt = join "", <>;
+$txt =~ s/^\s*#.*$//gm;
+$txt =~ s/\\\n//gs;
+$txt =~ s/\r//g;
+$txt =~ s/\n\s*\n/\n/gs;
+
+my %fixed = (
+    FULL_PATH_NAMES => "NO",
+    SYMBOL_CACHE_SIZE => 2,
+    EXCLUDE_SYMLINKS => "YES",
+    GENERATE_HTML => "YES",
+    PERL_PATH => "/usr/bin/perl",
+    HAVE_DOT => "YES",
+    HTML_OUTPUT => ".",
+    HTML_DYNAMIC_SECTIONS => "NO",
+    SEARCHENGINE => "NO",
+    DOT_FONTNAME => "FreeMono",
+    DOT_FONTSIZE => 10,
+    DOT_FONTPATH => "/usr/share/fonts/truetype/freefont",
+    DOT_IMAGE_FORMAT => "png",
+    DOT_PATH => "/usr/bin/dot",
+    DOT_TRANSPARENT => "YES",
+);
+
+# These are the keys that are safe to take from the output and include
+# in the output; they may still need to be checked for safe values (if
+# file paths).
+my @safe = qw(
+INPUT
+FILE_PATTERNS
+EXAMPLE_PATH
+EXAMPLE_PATTERNS
+IMAGE_PATH
+INCLUDE_PATH           
+INCLUDE_FILE_PATTERNS
+DOXYFILE_ENCODING      
+PROJECT_NAME           
+PROJECT_NUMBER         
+CREATE_SUBDIRS         
+OUTPUT_LANGUAGE        
+BRIEF_MEMBER_DESC      
+REPEAT_BRIEF           
+ABBREVIATE_BRIEF       
+ALWAYS_DETAILED_SEC    
+INLINE_INHERITED_MEMB  
+STRIP_FROM_PATH        
+STRIP_FROM_INC_PATH    
+JAVADOC_AUTOBRIEF      
+QT_AUTOBRIEF           
+MULTILINE_CPP_IS_BRIEF 
+INHERIT_DOCS           
+SEPARATE_MEMBER_PAGES  
+TAB_SIZE               
+ALIASES                
+OPTIMIZE_OUTPUT_FOR_C  
+OPTIMIZE_OUTPUT_JAVA   
+OPTIMIZE_FOR_FORTRAN   
+OPTIMIZE_OUTPUT_VHDL   
+EXTENSION_MAPPING      
+BUILTIN_STL_SUPPORT    
+CPP_CLI_SUPPORT        
+SIP_SUPPORT            
+IDL_PROPERTY_SUPPORT   
+DISTRIBUTE_GROUP_DOC   
+SUBGROUPING            
+TYPEDEF_HIDES_STRUCT   
+EXTRACT_ALL            
+EXTRACT_PRIVATE        
+EXTRACT_STATIC         
+EXTRACT_LOCAL_CLASSES  
+EXTRACT_LOCAL_METHODS  
+EXTRACT_ANON_NSPACES   
+HIDE_UNDOC_MEMBERS     
+HIDE_UNDOC_CLASSES     
+HIDE_FRIEND_COMPOUNDS  
+HIDE_IN_BODY_DOCS      
+INTERNAL_DOCS          
+HIDE_SCOPE_NAMES       
+SHOW_INCLUDE_FILES     
+FORCE_LOCAL_INCLUDES   
+INLINE_INFO            
+SORT_MEMBER_DOCS       
+SORT_BRIEF_DOCS        
+SORT_MEMBERS_CTORS_1ST 
+SORT_GROUP_NAMES       
+SORT_BY_SCOPE_NAME     
+GENERATE_TODOLIST      
+GENERATE_TESTLIST      
+GENERATE_BUGLIST       
+GENERATE_DEPRECATEDLIST
+ENABLED_SECTIONS       
+MAX_INITIALIZER_LINES  
+SHOW_USED_FILES        
+SHOW_DIRECTORIES       
+SHOW_FILES             
+SHOW_NAMESPACES        
+QUIET                  
+WARNINGS               
+WARN_IF_UNDOCUMENTED   
+WARN_IF_DOC_ERROR      
+WARN_NO_PARAMDOC       
+INPUT_ENCODING         
+RECURSIVE              
+EXCLUDE                
+EXCLUDE_SYMLINKS       
+EXCLUDE_PATTERNS       
+EXCLUDE_SYMBOLS        
+EXAMPLE_RECURSIVE      
+SOURCE_BROWSER         
+INLINE_SOURCES         
+STRIP_CODE_COMMENTS    
+REFERENCED_BY_RELATION 
+REFERENCES_RELATION    
+REFERENCES_LINK_SOURCE 
+VERBATIM_HEADERS       
+ALPHABETICAL_INDEX     
+COLS_IN_ALPHA_INDEX    
+IGNORE_PREFIX          
+HTML_TIMESTAMP         
+HTML_ALIGN_MEMBERS     
+ENABLE_PREPROCESSING   
+MACRO_EXPANSION        
+EXPAND_ONLY_PREDEF     
+SEARCH_INCLUDES        
+PREDEFINED             
+EXPAND_AS_DEFINED      
+SKIP_FUNCTION_MACROS   
+ALLEXTERNALS           
+EXTERNAL_GROUPS        
+CLASS_DIAGRAMS         
+HIDE_UNDOC_RELATIONS   
+CLASS_GRAPH            
+COLLABORATION_GRAPH    
+GROUP_GRAPHS           
+UML_LOOK               
+TEMPLATE_RELATIONS     
+INCLUDE_GRAPH          
+INCLUDED_BY_GRAPH      
+CALL_GRAPH             
+CALLER_GRAPH           
+GRAPHICAL_HIERARCHY    
+DIRECTORY_GRAPH        
+DOT_GRAPH_MAX_NODES    
+MAX_DOT_GRAPH_DEPTH    
+DOT_MULTI_TARGETS      
+DOT_CLEANUP            
+);
+
+my %safehash;
+for my $sk (@safe) { $safehash{$sk} = 1; }
+
+my @lines = split "\n", $txt;
+
+my %settings;
+
+sub is_safe {
+    my $key = shift;
+    defined $safehash{$key} and $safehash{$key} == 1;
+}
+
+sub has_file_path {
+    # Returns true if the given key expects a file path as a value.
+    # We only need to test keys that are safe; unsafe keys have been
+    # rejected already.
+    my $key = shift;
+    $key eq "INPUT" or
+	$key =~ /^OUTPUT_/ or
+	$key =~ /_PATH$/ or
+	$key =~ /_PATTERNS$/;
+}
+
+sub is_safe_file_path {
+    my $value = shift;
+    not $value =~ /^\// and not $value =~ /\.\./;
+}
+
+foreach my $line (@lines) {
+
+    chomp $line;
+    my ($key, $value) = split /\s*=\s*/, $line;
+
+    next if !defined $key;
+
+    if ($key =~ /^GENERATE_/ and not $key =~ /LIST$/) {
+	print STDERR "NOTE: Setting $key explicitly to NO\n";
+	$settings{$key} = "NO";
+	next;
+    }
+	
+    if (!is_safe($key)) {
+	print STDERR "NOTE: Skipping non-whitelisted key $key\n";
+	next;
+    }
+
+    if (has_file_path($key) and !is_safe_file_path($value)) {
+	print STDERR "ERROR: Unsafe file path \"$value\" for key $key\n";
+	exit 1;
+    }
+
+    $settings{$key} = $value;
+}
+
+foreach my $key (keys %fixed) {
+    my $value = $fixed{$key};
+    print STDERR "NOTE: Setting $key to fixed value $value\n";
+    $settings{$key} = $value;
+}
+
+print join "\n", map { "$_ = $settings{$_}" } keys %settings;
+print "\n";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/extract-docs.sh	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,150 @@
+#!/bin/bash
+
+# Run this script from anywhere
+
+# Enumerate Hg repos; make sure they're up to date; extract docs for
+# each
+
+hgdir="/var/hg"
+docdir="/var/doc"
+logfile="/var/www/test-cannam/log/extract-docs.log"
+
+redgrp="redmine"
+
+apikey=""
+apischeme="https"
+apihost=""
+apiuser=""
+apipass=""
+
+progdir=$(dirname $0)
+case "$progdir" in
+    /*) ;;
+    *) progdir="$(pwd)/$progdir" ;;
+esac
+
+types="doxygen javadoc matlabdocs" # Do Doxygen first (it can be used for Java too)
+
+for x in $types; do
+    if [ ! -x "$progdir/extract-$x.sh" ]; then
+	echo "Helper script not available: $progdir/extract-$x.sh"
+	exit 1
+    fi
+done
+
+enable_embedded()
+{
+    p="$1"
+    if [ -n "$apikey" ]; then
+	if [ -n "$apiuser" ]; then
+	    sudo -u docgen curl -u "$apiuser":"$apipass" "$apischeme://$apihost/sys/projects/$p/embedded.xml?enable=1&key=$apikey" -d ""
+	else
+	    sudo -u docgen curl "$apischeme://$apihost/sys/projects/$p/embedded.xml?enable=1&key=$apikey" -d ""
+	fi
+    else 
+	echo "Can't enable Embedded, API not configured" 1>&2
+    fi
+}
+
+# We want to ensure the doc extraction is done by the unprivileged
+# user docgen, which is not a member of any interesting group
+# 
+# To this end, we create the tmpdir with user docgen and group
+# www-data, and use the www-data user to pull out an archive of the Hg
+# repo tip into a location beneath that, before using the docgen user
+# to extract docs from that location and write them into the tmpdir
+
+# Same tmpdir for each project: we delete and recreate to avoid
+# cleanup duty from lots of directories being created
+#
+tmpdir=$(mktemp -d "$docdir/tmp_XXXXXX")
+
+fail()
+{
+    message="$1"
+    echo "$message" 1>&2
+    case "$tmpdir" in
+	*/tmp*) rm -rf "$tmpdir";;
+	*);;
+    esac
+    exit 1
+}
+
+case "$tmpdir" in
+    /*) ;;
+    *) fail "Temporary directory creation failed";;
+esac
+
+chown docgen.www-data "$tmpdir" || fail "Temporary directory ownership change failed"
+chmod g+rwx "$tmpdir" || fail "Temporary directory permissions change failed"
+
+for projectdir in "$hgdir"/* ; do
+
+    if [ -d "$projectdir" ] && [ -d "$projectdir/.hg" ]; then
+
+	if ! sudo -u www-data hg -R "$projectdir" -q update; then
+	    echo "Failed to update Hg in $projectdir, skipping" 1>&2
+	    continue
+	fi
+
+	project=$(basename "$projectdir")
+
+	tmptargetdir="$tmpdir/doc"
+	snapshotdir="$tmpdir/hgsnapshot"
+
+	rm -rf "$tmptargetdir" "$snapshotdir"
+
+	mkdir -m 770 "$tmptargetdir" || fail "Temporary target directory creation failed"
+	chown docgen.www-data "$tmptargetdir" || fail "Temporary target directory ownership change failed"
+
+	mkdir -m 770 "$snapshotdir" || fail "Snapshot directory creation failed"
+	chown docgen.www-data "$snapshotdir" || fail "Snapshot directory ownership change failed"
+
+	hgparents=$(sudo -u www-data hg -R "$projectdir" parents)
+	if [ -z "$hgparents" ]; then
+	    echo "Hg repo at $projectdir has no working copy (empty repo?), skipping"
+	    continue
+	else
+	    echo "Found non-empty Hg repo: $projectdir for project $project"
+	fi
+
+	if ! sudo -u www-data hg -R "$projectdir" archive -r tip -t files "$snapshotdir"; then
+	    echo "Failed to pick archive from $projectdir, skipping" 1>&2
+	    continue
+	fi
+
+	targetdir="$docdir/$project"
+
+	echo "Temporary dir is $tmpdir, temporary doc dir is $tmptargetdir, snapshot dir is $snapshotdir, eventual target is $targetdir"
+
+	for x in $types; do
+	    if sudo -u docgen "$progdir/extract-$x.sh" "$project" "$snapshotdir" "$tmptargetdir" >> "$logfile" 2>&1; then
+		break
+	    else
+		echo "Failed to extract via type $x"
+	    fi
+	done
+
+        if [ -f "$tmptargetdir/index.html" ]; then
+	    echo "Processing resulted in an index.html being created, looks good!"
+	    if [ ! -d "$targetdir" ] || [ ! -f "$targetdir/index.html" ]; then
+		echo "This project hasn't had doc extracted before: enabling Embedded"
+		enable_embedded "$project"
+	    fi
+
+	    if [ -d "$targetdir" ]; then
+		mv "$targetdir" "$targetdir"_"$$" && \
+		    mv "$tmptargetdir" "$targetdir" && \
+		    rm -rf "$targetdir"_"$$"
+		chgrp -R "$redgrp" "$targetdir"
+	    else 
+		mv "$tmptargetdir" "$targetdir"
+		chgrp -R "$redgrp" "$targetdir"
+	    fi
+	else
+	    echo "Processing did not result in an index.html being created"
+	fi
+    fi
+done
+
+rm -rf "$tmpdir"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/extract-doxygen.sh	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+docdir="/var/doc"
+
+progdir=$(dirname $0)
+case "$progdir" in
+    /*) ;;
+    *) progdir="$(pwd)/$progdir" ;;
+esac
+
+project="$1"
+projectdir="$2"
+targetdir="$3"
+
+if [ -z "$project" ] || [ -z "$targetdir" ] || [ -z "$projectdir" ]; then
+    echo "Usage: $0 <project> <projectdir> <targetdir>"
+    exit 2
+fi
+
+if [ ! -d "$projectdir" ]; then
+    echo "Project directory $projectdir not found"
+    exit 1
+fi
+
+if [ ! -d "$targetdir" ]; then
+    echo "Target dir $targetdir not found"
+    exit 1
+fi
+
+if [ -f "$targetdir/index.html" ]; then
+    echo "Target dir $targetdir already contains index.html"
+    exit 1
+fi
+
+doxyfile=$(find "$projectdir" -type f -name Doxyfile -print | head -1)
+
+if [ -z "$doxyfile" ]; then
+    echo "No Doxyfile found for project $project"
+    exit 1
+fi
+
+echo "Project $project contains a Doxyfile at $doxyfile"
+
+cd "$projectdir" || exit 1
+
+"$progdir/doxysafe.pl" "$doxyfile" | \
+    sed -e '$a OUTPUT_DIRECTORY='"$targetdir" | \
+    doxygen -
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/extract-javadoc.sh	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+docdir="/var/doc"
+
+project="$1"
+projectdir="$2"
+targetdir="$3"
+
+if [ -z "$project" ] || [ -z "$targetdir" ] || [ -z "$projectdir" ]; then
+    echo "Usage: $0 <project> <projectdir> <targetdir>"
+    exit 2
+fi
+
+if [ ! -d "$projectdir" ]; then
+    echo "Project directory $projectdir not found"
+    exit 1
+fi
+
+if [ ! -d "$targetdir" ]; then
+    echo "Target dir $targetdir not found"
+    exit 1
+fi
+
+if [ -f "$targetdir/index.html" ]; then
+    echo "Target dir $targetdir already contains index.html"
+    exit 1
+fi
+
+# Identify Java files whose packages match the trailing parts of their
+# paths, and list the resulting packages and the path prefixes with
+# the packages removed (so as to find code in subdirs,
+# e.g. src/com/example/...)
+
+# Regexp match is very rough; check what is actually permitted for
+# package declarations
+
+find "$projectdir" -type f -name \*.java \
+    -exec egrep '^ *package +[a-zA-Z][a-zA-Z0-9\._-]*;.*$' \{\} /dev/null \; |
+    sed -e 's/\/[^\/]*: *package */:/' -e 's/;.*$//' |
+    sort | uniq | (
+	current_prefix=
+	current_packages=
+	while IFS=: read filepath package; do 
+	    echo "Looking at $package in $filepath"
+	    packagepath=${package//./\/}
+	    prefix=${filepath%$packagepath}
+	    prefix=${prefix:=$projectdir}
+	    if [ "$prefix" = "$filepath" ]; then
+		echo "Package $package does not match suffix of path $filepath, skipping"
+		continue
+	    fi
+	    if [ "$prefix" != "$current_prefix" ]; then
+		echo "Package $package matches file path and has new prefix $prefix"
+		if [ -n "$current_packages" ]; then
+		    echo "Running Javadoc for packages $current_packages from prefix $current_prefix"
+		    echo "Command is: javadoc -sourcepath "$current_prefix" -d "$targetdir" -subpackages $current_packages"
+		    javadoc -sourcepath "$current_prefix" -d "$targetdir" -subpackages $current_packages
+		fi
+		current_prefix="$prefix"
+		current_packages="$package"
+	    else
+		echo "Package $package matches file path with same prefix as previous file"
+		current_packages="$current_packages $package"
+	    fi
+	done
+	prefix=${prefix:=$projectdir}
+	if [ -n "$current_packages" ]; then
+	    echo "Running Javadoc for packages $current_packages in prefix $current_prefix"
+  	    echo "Command is: javadoc -sourcepath "$current_prefix" -d "$targetdir" -subpackages $current_packages"
+	    javadoc -sourcepath "$current_prefix" -d "$targetdir" -subpackages $current_packages
+	fi
+    )
+
+if [ -f "$targetdir"/overview-tree.html ]; then
+    cp "$targetdir"/overview-tree.html "$targetdir"/index.html
+fi
+
+# for exit code:
+[ -f "$targetdir/index.html" ]
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/extract-matlabdocs.sh	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+docdir="/var/doc"
+
+progdir=$(dirname $0)
+case "$progdir" in
+    /*) ;;
+    *) progdir="$(pwd)/$progdir" ;;
+esac
+
+project="$1"
+projectdir="$2"
+targetdir="$3"
+
+if [ -z "$project" ] || [ -z "$targetdir" ] || [ -z "$projectdir" ]; then
+    echo "Usage: $0 <project> <projectdir> <targetdir>"
+    exit 2
+fi
+
+if [ ! -d "$projectdir" ]; then
+    echo "Project directory $projectdir not found"
+    exit 1
+fi
+
+if [ ! -d "$targetdir" ]; then
+    echo "Target dir $targetdir not found"
+    exit 1
+fi
+
+if [ -f "$targetdir/index.html" ]; then
+    echo "Target dir $targetdir already contains index.html"
+    exit 1
+fi
+
+mfile=$(find "$projectdir" -type f -name \*.m -print0 | xargs -0 grep -l '^% ' | head -1)
+
+if [ -z "$mfile" ]; then
+    echo "No MATLAB files with comments found for project $project"
+    exit 1
+fi
+
+echo "Project $project contains at least one MATLAB file with comments"
+
+cd "$projectdir" || exit 1
+
+perl "$progdir/matlab-docs.pl" -c "$progdir/matlab-docs.conf" -d "$targetdir"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/get-apache-log-stats.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,179 @@
+
+# Read an Apache log file in SoundSoftware site format from stdin and
+# produce some per-project stats.
+#
+# Invoke with e.g.
+#
+# cat /var/log/apache2/code-access.log | \
+#   script/runner -e production extra/soundsoftware/get-apache-log-stats.rb
+
+
+# Use the ApacheLogRegex parser, a neat thing
+# See http://www.simonecarletti.com/blog/2009/02/apache-log-regex-a-lightweight-ruby-apache-log-parser/
+require 'apachelogregex'
+
+# This is the format defined in our httpd.conf
+vhost_combined_format = '%v:%p %h %{X-Forwarded-For}i %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"'
+
+parser = ApacheLogRegex.new(vhost_combined_format)
+
+# project name -> count of hg clones
+clones = Hash.new(0)
+
+# project name -> count of hg pulls
+pulls = Hash.new(0)
+
+# project name -> count of hg pushes
+pushes = Hash.new(0)
+
+# project name -> count of hg archive requests (i.e. Download as Zip)
+zips = Hash.new(0)
+
+# project name -> count of hits to pages under /projects/projectname
+hits = Hash.new(0)
+
+# project name -> Project object
+@projects = Hash.new
+
+parseable = 0
+unparseable = 0
+
+def is_public_project?(project)
+  if !project
+    false
+  elsif project =~ /^\d+$/
+    # ignore numerical project ids, they are only used when editing projects
+    false
+  elsif @projects.key?(project)
+    @projects[project].is_public? 
+  else
+    pobj = Project.find_by_identifier(project)
+    if pobj
+      @projects[project] = pobj
+      pobj.is_public?
+    else
+      print "Project not found: ", project, "\n"
+      false
+    end
+  end
+end
+
+def print_stats(h)
+  h.keys.sort { |a,b| h[b] <=> h[a] }.each do |p|
+    if h[p] > 0
+      print h[p], " ", @projects[p].name, " [", p, "]\n"
+    end
+  end
+end
+
+STDIN.each do |line|
+
+  record = parser.parse(line)
+
+  # most annoyingly, the parser can't handle the comma-separated list
+  # in X-Forwarded-For where it has more than one element. If it has
+  # failed, remove any IP addresses or the word "unknown" with
+  # trailing commas and try again
+  if not record
+    filtered = line.gsub(/(unknown|([0-9]+\.){3}[0-9]+),\s*/, "")
+    record = parser.parse(filtered)
+  end
+
+  # discard, but count, unparseable lines
+  if not record
+    print "Line not parseable: ", line, "\n"
+    unparseable += 1
+    next
+  end
+
+  # discard everything that isn't a 200 OK response
+  next if record["%>s"] != "200"
+
+  # discard anything apparently requested by a crawler
+  next if record["%{User-Agent}i"] =~ /(bot|slurp|crawler|spider|Redmine)\b/i
+
+  # pull out request e.g. GET / HTTP/1.0
+  request = record["%r"]
+
+  # split into method, path, protocol
+  if not request =~ /^[^\s]+ ([^\s]+) [^\s]+$/
+    print "Line not parseable (bad method, path, protocol): ", line, "\n"
+    unparseable += 1
+    next
+  end
+
+  # get the path e.g. /projects/weevilmatic and split on /
+  path = $~[1]
+  components = path.split("/")
+  
+  # should have at least two elements unless path is "/"; first should
+  # be empty (begins with /)
+  if path != "/" and (components.size < 2 or components[0] != "")
+    print "Line not parseable (degenerate path): ", line, "\n"
+    unparseable += 1
+    next
+  end
+
+  if components[1] == "hg"
+    
+    # path is /hg/project?something or /hg/project/something
+
+    project = components[2].split("?")[0]
+    if not is_public_project?(project)
+      next
+    end
+
+    if components[2] =~ /&roots=00*$/
+      clones[project] += 1
+    elsif components[2] =~ /cmd=capabilities/
+      pulls[project] += 1
+    elsif components[2] =~ /cmd=unbundle/
+      pushes[project] += 1
+    elsif components[3] == "archive"
+      zips[project] += 1
+    end
+
+  elsif components[1] == "projects"
+
+    # path is /projects/project or /projects/project/something
+
+    project = components[2]
+    project = project.split("?")[0] if project
+    if not is_public_project?(project)
+      next
+    end
+
+    hits[project] += 1
+
+  end
+
+  parseable += 1
+end
+
+# Each clone is also a pull; deduct it from the pulls hash, because we
+# want that to contain only non-clone pulls
+
+clones.keys.each do |project|
+  pulls[project] -= 1
+end
+
+print parseable, " parseable\n"
+print unparseable, " unparseable\n"
+
+
+print "\nMercurial clones:\n"
+print_stats clones
+
+print "\nMercurial pulls (excluding clones):\n"
+print_stats pulls
+
+print "\nMercurial pushes:\n"
+print_stats pushes
+
+print "\nMercurial archive (zip file) downloads:\n"
+print_stats zips
+
+print "\nProject page hits (excluding crawlers):\n"
+print_stats hits
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/get-statistics.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,21 @@
+
+# Log user and project information
+#
+# Invoke with e.g.
+#
+# ./script/rails runner -e production extra/soundsoftware/get-statistics.rb
+#
+
+projectStats =  {
+        :all => Project.active.all.count,
+        :private => Project.active.find(:all, :conditions => {:is_public => false}).count,
+        :top_level => Project.active.find(:all, :conditions => {:parent_id => nil}).count,
+        :top_level_and_private => Project.active.find(:all, :conditions => {:is_public => false, :parent_id => nil}).count
+      }
+
+userStats = {:all => User.active.all.count}
+
+stats = {:date => Date.today, :projects => projectStats, :users => userStats}.to_json
+
+print "#{stats}\n"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/matlab-docs-credit.html	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1 @@
+<div style="clear: both; float: right"><small><i>Produced by mtree2html by Hartmut Pohlheim</i></small></div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/matlab-docs.conf	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,166 @@
+# configuration file for generation of html-docu from m-files
+#
+# Author:   Hartmut Pohlheim
+# History:  05.11.2000  file created (parameters for mtree2html2001)
+#
+# The following options/variables must be changed/adapted:
+#   dirmfiles
+#   dirhtml
+#   csslink
+#   texttitleframelayout
+#   texttitlefiles
+#
+# The following options/variables should be adapted:
+#   authorfile
+#   filenametopframe
+#   codeheadmeta
+
+#========================================================================
+# Variables (possible keywords: set)
+# to use the built-in settings, comment the line using # in first column
+#========================================================================
+
+#------------------------------------------------------------------------
+# dirmfiles: name of directory containing Matlab m-files
+# dirhtml: name of directory to place the html-files into
+# exthtml: extension used for the html files (.html or .htm)
+#          don't forget the point in front of the extension
+#------------------------------------------------------------------------
+set dirmfiles = .
+set dirhtml = doc-output
+set exthtml = .html
+
+#------------------------------------------------------------------------
+# authorfile:   name of file containing info about author (in html)
+#               if defined, this text is included at the bottom of the 
+#               html files
+#------------------------------------------------------------------------
+set authorfile = matlab-docs-credit.html
+
+#------------------------------------------------------------------------
+# csslink:   text for linking to css file (style sheets)
+#            the text defined here is directly included into the head 
+#            of the html file
+#------------------------------------------------------------------------
+#set csslink = <link rel=stylesheet type="text/css" href="CSSFILENAME.css" />
+
+#------------------------------------------------------------------------
+# links2filescase: this is a bit difficult
+#                  Matlab is case sensitive on UNIX, but case insensitive
+#                  on Windows. Under UNIX Matlab function calls work 
+#                  only, when the case of file name and function call are 
+#                  identical, under Windows you can do what you want.
+#                  This scripts help you, to keep an exact case in your 
+#                  project.
+#          exact - internal links are only generated, when case of file 
+#                  name and in source code are identical
+#            all - case doesn't matter
+#     exactupper - same as exact, additionally links are also vreated to 
+#                  all upper case function names in source code (often 
+#                  used by Mathworks)
+#      exactvery - same as exact, additionally info about not matching
+#                  case is written to screen (stdout), this can be very 
+#                  helpful in cleaning up the case in a project
+#------------------------------------------------------------------------
+set links2filescase = all
+
+#------------------------------------------------------------------------
+# texttitleframelayout:    text of title for frame layout file (whole docu)
+#------------------------------------------------------------------------
+set texttitleframelayout = MATLAB Function Documentation
+
+#------------------------------------------------------------------------
+# texttitle/headerindexalldirs: text of title and header for directory index
+#------------------------------------------------------------------------
+set texttitleindexalldirs = Index of Directories
+set textheaderindexalldirs = Index of Directories
+
+#------------------------------------------------------------------------
+# texttitle/headerindex:    text of title and header for index file
+#------------------------------------------------------------------------
+set texttitleindex = A-Z Index of Functions
+set textheaderindex = A-Z Index of Functions
+
+#------------------------------------------------------------------------
+# texttitle/headerfiles:    text of title and header for files
+#                           name of file will be added at the end
+#------------------------------------------------------------------------
+set texttitlefiles = Function
+set textheaderfiles = Documentation of
+
+#------------------------------------------------------------------------
+# frames: whether to use frames in layout (yes or no)
+#------------------------------------------------------------------------
+set frames = no
+
+#------------------------------------------------------------------------
+# filenametopframe: name of file including frame layout (highest level file)
+# [default: index]
+#------------------------------------------------------------------------
+set filenametopframe = index
+
+#------------------------------------------------------------------------
+# textjumpindexglobal: text displayed for jump to index of all files
+#                      (global)
+# textjumpindexlocal:  text displayed for jump to index of files in actual
+#                      directory (local)
+#------------------------------------------------------------------------
+set textjumpindexglobal = <b>Index of</b> all files:
+set textjumpindexlocal = this subdirectory only:
+
+#------------------------------------------------------------------------
+# includesource: include source of m-files in documentation [YES|no]
+#------------------------------------------------------------------------
+set includesource = yes
+
+#------------------------------------------------------------------------
+# usecontentsm: use contents.m files as well for structured
+#               (hopefully) index [YES|no]
+#------------------------------------------------------------------------
+set usecontentsm = no
+
+#------------------------------------------------------------------------
+# includesource: write/update contents.m files [yes|NO]
+#------------------------------------------------------------------------
+set writecontentsm = no
+
+#------------------------------------------------------------------------
+# processtree:  parse whole directory tree recursively [YES|no]
+#------------------------------------------------------------------------
+set processtree = yes
+
+#------------------------------------------------------------------------
+# producetree:  produce tree for html-files in same structure than
+#		          tree of m-files [yes|NO]
+#               if no, all files are saved in the same directory, often 
+#               easier for outside linking to files
+#------------------------------------------------------------------------
+set producetree = yes
+
+#------------------------------------------------------------------------
+# codebodyindex/files: HTML-code for adding to BODY tag
+#                      can be used for defining colors and
+#                      backgroundimages of the files
+#                      No longer recommended, use the css file
+#------------------------------------------------------------------------
+set codebodyindex =
+set codebodyfiles =
+
+#------------------------------------------------------------------------
+# codeheadmeta: HTML-code added in HEAD area, use for supplying META info
+#------------------------------------------------------------------------
+set codeheadmeta = 
+
+#------------------------------------------------------------------------
+# codehr: HTML-code used to define a <HR>, do what you want
+#------------------------------------------------------------------------
+set codehr = <hr>
+
+#------------------------------------------------------------------------
+# codeheader: HTML-code added to <H*> tags, use for centering header text
+#             or changing the colour/size/font of the header text
+#------------------------------------------------------------------------
+set codeheader = 
+
+
+# End of parameter file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/matlab-docs.pl	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1585 @@
+@rem = '--*-Perl-*--';
+@rem = '
+@echo off
+perl -w -S %0.bat %1 %2 %3 %4 %5 %6 %7 %8 %9
+goto endofperl
+@rem ';
+# perl -w -S %0.bat "$@"
+#!/usr/bin/perl
+#
+# mtree2html_2000 - produce html files from Matlab m-files.
+#                   use configuration file for flexibility
+#                   can process tree of directories
+#
+# Copyright (C) 1996-2000 Hartmut Pohlheim.  All rights reserved.
+# includes small parts of m2html from Jeffrey C. Kantor 1995
+#
+# Author:  Hartmut Pohlheim
+# History: 06.03.1996  file created
+#          07.03.1996  first working version
+#          08.03.1996  modularized, help text only once included
+#          11.03.1996  clean up, some functions rwritten
+#          18.04.1996  silent output with writing in one line only
+#                      version 0.20 fixed
+#          14.05.1996  start of adding tree structure, could create tree
+#          15.05.1996  creating of index files for every directory
+#          17.05.1996  first working version except compact A-Z index
+#          20.05.1996  cleanup of actual version, more variables and 
+#                      configurable settings
+#          21.05.1996  reading, update and creation of contents.m added 
+#          22.05.1996  creation of short index started
+#          28.05.1996  jump letters for short index,
+#                      3 different directory indexes (short/long/contents)
+#          29.05.1996  major cleanup, short and long index created from one function
+#                      links for HTML and Indexes from 1 function,
+#                      version 0.9
+#          30.05.1996  contents.m changed to Contents.m (because unix likes it)
+#                      function definition can be in first line of m file before comments
+#                      version 0.91 fixed
+#          03.06.1996  contents file can be written as wanted, the links will be correct
+#                      cross references in help block of m-file will be found and
+#                      converted, even if the name of the function is written upper case
+#                      version 0.92 fixed
+#          05.06.1996  construction of dependency matrix changed, is able now to process
+#                      even the whole matlab tree (previous version needed to much memory)
+#                      removed warning for contents files in different directories
+#                      version 0.94 fixed
+#          06.06.1996  new link name matrices for ConstructHTMLFile created,
+#                      everything is done in ConstructDependencyMatrix,
+#                      both dependencies (calls and called) and matrix 
+#                      with all mentioned names in this m-file, thus, much
+#                      less scanning in html construction
+#                      script is now (nearly) linear scalable, thus, matlab-toolbox
+#                      tree takes less than 1 hour on a Pentium120, with source
+#                      version 0.96 fixed
+#          10.06.1996  order of creation changed, first all indexes (includes 
+#                      update/creation of contents.m) and then ConstructDepency
+#                      thus, AutoAdd section will be linked as well
+#                      excludenames extended, some more common word function names added
+#                      version 0.97 fixed
+#          17.02.1998  writecontentsm as command line parameter added
+#                      error of file not found will even appear when silent
+#                      version 1.02
+#          21.05.2000  mark comments in source code specially (no fully correct, 
+#                      can't handle % in strings)
+#                      version 1.11
+#          05.11.2000  link also to upper and mixed case m-files
+#                      searching for .m files now really works (doesn't find grep.com any longer)
+#                      file renamed to mtree2html2001
+#                      generated html code now all lower case 
+#                      inclusion of meta-description and meta-keywords in html files
+#                      HTML4 compliance done (should be strict HTML4.0, quite near XHTML)
+#                      version 1.23
+#
+#	   29.03.2011  (Chris Cannam) add frames option.
+
+$VERSION  = '1.23';
+($PROGRAM = $0) =~ s@.*/@@; $PROGRAM = "\U$PROGRAM\E";
+$debug = 1;
+
+#------------------------------------------------------------------------
+# Define platform specific things
+#------------------------------------------------------------------------
+# suffix for files to search is defined twice
+# the first ($suffix) is for string creation and contains the . as well
+# the second ($suffixforsearch) is for regular expression, handling of . is quite special
+$suffix = ".m";
+$suffixforsearch = "m";
+# the directory separator
+$dirsep = "/";
+# what is the current directory
+$diract = ".";
+
+#------------------------------------------------------------------------
+#  Define all variables and their standard settings
+#  documentation of variables is contained in accompanying rc file
+#------------------------------------------------------------------------
+%var =
+(
+   'authorfile',                '',
+   'codebodyfiles',             '',
+   'codebodyindex',             '',
+   'codeheadmeta',              '<meta name="author of conversion perl script" content="Hartmut Pohlheim" />',
+   'codehr',                    '<hr size="3" noshade="noshade" />',
+   'codeheader',                '',
+   'configfile',                'matlab-docs.conf',
+   'csslink',                   '',
+   'dirmfiles',                 $diract,
+   'dirhtml',                   $diract,
+   'exthtml',                   '.html',
+   'frames',                    'yes',
+   'filenametopframe',          'index',
+   'filenameindexlongglobal',   'indexlg',
+   'filenameindexlonglocal',    'indexll',
+   'filenameindexshortglobal',  'indexsg',
+   'filenameindexshortlocal',   'indexsl',
+   'filenameextensionframe',    'f',
+   'filenameextensionindex',    'i',
+   'filenameextensionjump',     'j',
+   'filenamedirshort',          'dirtops',
+   'filenamedirlong',           'dirtopl',
+   'filenamedircontents',       'dirtopc',
+   'includesource',             'yes',
+   'links2filescase',           'all',
+   'processtree',               'yes',
+   'producetree',               'yes',
+   'textjumpindexlocal',        'Local Index',
+   'textjumpindexglobal',       'Global Index',
+   'texttitleframelayout',      'Documentation of Matlab Files',
+   'texttitleindexalldirs',     'Index of Directories',
+   'textheaderindexalldirs',    'Index of Directories',
+   'texttitleindex',            '',
+   'textheaderindex',           '',
+   'texttitlefiles',            'Documentation of ',
+   'textheaderfiles',           'Documentation of ',
+   'usecontentsm',              'yes',
+   'writecontentsm',            'no'
+);
+
+
+# define all m-file names, that should be excluded from linking
+# however, files will still be converted
+@excludenames = ( 'all','ans','any','are',
+                  'cs',
+                  'demo','dos',
+                  'echo','edit','else','elseif','end','exist',
+                  'flag','for','function',
+                  'global',
+                  'help',
+                  'i','if','inf','info',
+                  'j',
+                  'more',
+                  'null',
+                  'return',
+                  'script','strings',
+                  'what','which','while','who','whos','why',
+                );
+
+# Text for inclusion in created HTML/Frame files: Doctype and Charset
+$TextDocTypeHTML  = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">';
+$TextDocTypeFrame = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">'; 
+$TextMetaCharset = '<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />';
+
+#------------------------------------------------------------------------
+# Read the command line arguments
+#------------------------------------------------------------------------
+if (@ARGV == 0) {
+   &DisplayHelp()  if &CheckFileName($var{'configfile'}, 'configuration file');
+}
+
+# Print provided command line arguments on screen
+foreach (@ARGV) { print "   $_\n      "; }
+
+# Get the options
+use Getopt::Long;
+@options = ('help|h', 'todo|t', 'version|v',
+            'authorfile|a=s', 'configfile|c=s', 'dirhtml|html|d=s',
+            'dirmfiles|mfiles|m=s', 'includesource|i=s',
+            'processtree|r=s', 'producetree|p=s',
+            'silent|quiet|q', 'writecontentsm|w=s');
+&GetOptions(@options) || die "use -h switch to display help statement\n";
+
+
+# Display help or todo list, when requested
+&DisplayHelp()                         if $opt_help;
+&DisplayTodo()                         if $opt_todo;
+die "$PROGRAM v$VERSION\n"             if $opt_version;
+
+$exit_status = 0;
+
+#------------------------------------------------------------------------
+# Read the config file
+#------------------------------------------------------------------------
+$var{'configfile'} = $opt_configfile         if $opt_configfile;
+&GetConfigFile($var{'configfile'});
+
+
+#------------------------------------------------------------------------
+# Process/Check the command line otions
+#------------------------------------------------------------------------
+$var{'dirhtml'}   = $opt_dirhtml              if $opt_dirhtml;
+if (!(substr($var{'dirhtml'}, -1, 1) eq $dirsep)) { $var{'dirhtml'} = $var{'dirhtml'}.$dirsep; }
+$var{'dirmfiles'} = $opt_dirmfiles            if $opt_dirmfiles;
+if (!(substr($var{'dirmfiles'}, -1, 1) eq $dirsep)) { $var{'dirmfiles'} = $var{'dirmfiles'}.$dirsep; }
+
+$var{'authorfile'} = $opt_author              if $opt_author;
+$var{'includesource'} = $opt_includesource    if $opt_includesource;
+if ($var{'includesource'} ne 'no') { $var{'includesource'} = 'yes'; }
+$var{'processtree'} = $opt_processtree        if $opt_processtree;
+if ($var{'processtree'} ne 'no') { $var{'processtree'} = 'yes'; }
+$var{'producetree'} = $opt_producetree        if $opt_producetree;
+if ($var{'producetree'} ne 'no') { $var{'producetree'} = 'yes'; }
+if ($var{'processtree'} eq 'no') { $var{'producetree'} = 'no'; }
+if ($var{'frames'} ne 'no') { $var{'frames'} = 'yes'; }
+# if (($var{'processtree'} eq 'yes') && ($var{'producetree'} eq 'no')) { $var{'usecontentsm'} = 'no'; }
+
+$var{'writecontentsm'} = $opt_writecontentsm  if $opt_writecontentsm;
+
+#------------------------------------------------------------------------
+# Do the real stuff
+#------------------------------------------------------------------------
+
+# Print variables on screen, when not silent
+&ListVariables                          if !$opt_silent;
+
+# Check the author file
+if ($var{'authorfile'} ne '') {
+    if (!($var{'authorfile'} =~ m,^/,)) {
+	# relative path: treat as relative to config file
+	my $cfd = $var{'configfile'};
+	$cfd =~ s,/[^/]*$,/,;
+	$cfd =~ s,^[^/]*$,.,;
+	$var{'authorfile'} = "$cfd/" . $var{'authorfile'};
+    }
+    if (&CheckFileName($var{'authorfile'}, 'author file')) {
+	$var{'authorfile'} = '';
+	if (!$opt_silent) { print "   Proceeding without author information!\n"; }
+    }
+}
+
+# Call the function doing all the real work
+&ConstructNameMatrix;
+
+&ConstructDependencyMatrix;
+
+&ConstructAllIndexFiles;
+
+&ConstructHTMLFiles;
+
+exit $exit_status;
+
+#------------------------------------------------------------------------
+# Construct list of all mfile names and initialize various data arrays.
+#------------------------------------------------------------------------
+sub ConstructNameMatrix
+{
+   local(*MFILE);
+   local($file, $dirname);
+   local(@newdirectories);
+   local(%localnames);
+   
+   $RecDeep = 0;
+   &ParseTreeReadFiles($var{'dirmfiles'}, $RecDeep);
+
+   foreach $dirname (@directories) { 
+      if ($dirnumbermfiles{$dirname} > 0) {
+         push(@newdirectories, $dirname);
+         if (! defined($contentsname{$dirname})) {
+            $contentsname{$dirname} = 'Contents';
+            if (($var{'writecontentsm'} eq 'no') && ($var{'usecontentsm'} eq 'yes')) {
+               print "\r ParseTree - for directory  $dirname  no contents file found!\n";
+               print   "             create one or enable writing of contents file (writecontentsm = yes)!\n";
+            }
+         }
+      }
+   }
+   @alldirectories = @directories;
+   @directories = @newdirectories;
+
+   foreach $dirname (@directories) { 
+      if ($debug > 0) { print "Dir: $dirname \t\t $dirnumbermfiles{$dirname} \t$contentsname{$dirname}\n"; }
+   }
+   
+   @names = sort(keys %mfile);
+
+   # check, if name of directory is identical to name of file
+   @dirsinglenames = values(%dirnamesingle);
+   grep($localnames{$_}++, @dirsinglenames);
+   @dirandfilename = grep($localnames{$_}, @names);
+   if (@dirandfilename) { 
+      print "\r   Name clash between directory and file name: @dirandfilename\n";
+      print   "      These files will be excluded from linking!\n";
+      push(@excludenames, @dirandfilename);
+   }
+   
+   # construct names matrix for help text linking
+   #    exclude some common words (and at the same time m-functions) from linking in help text
+   grep($localnames{$_}++, @excludenames);
+   @linknames = grep(!$localnames{$_}, @names);
+
+   if ($debug > 2) { print "linknames (names of found m-files):\n    @linknames\n"; }
+   
+}
+
+#------------------------------------------------------------------------
+# Parse tree and collect all Files
+#------------------------------------------------------------------------
+sub ParseTreeReadFiles
+{
+   local($dirname, $localRecDeep) = @_;
+   local($file, $name, $filewosuffix);
+   local($dirhtmlname, $dirmode);
+   local($relpath, $relpathtoindex, $replacevardir);
+   local(*CHECKDIR, *AKTDIR);
+   local(@ALLEFILES);
+   
+   opendir(AKTDIR, $dirname) || die "ParseTree - Can't open directory $dirname: $!";
+   if ($debug > 1) { print "\nDirectory: $dirname\n"; }
+
+   # create relative path
+   $_ = $dirname; $replacevardir = $var{'dirmfiles'};
+   s/$replacevardir//; $relpath = $_;
+   s/[^\/]+/../g; $relpathtoindex = $_;
+
+   # producetree no
+   if ($var{'producetree'} eq 'no') { $relpath = ''; $relpathtoindex = ''; }
+
+   # names of directories (top-level and below top-level m-file-directory)
+   push(@directories, $dirname);
+   $dirnumbermfiles{$dirname} = 0;    # set number of m-files for this dir to zero
+   # relative path from top-level directory, depends on directory name
+   $dirnamerelpath{$dirname} = $relpath;
+   # relative path from actual directory to top-level directory, depends on directory name
+   $dirnamerelpathtoindex{$dirname} = $relpathtoindex;
+   # recursion level for directory, depends on directory name
+   $dirnamerecdeep{$dirname} = $localRecDeep;
+   
+   # only the name of the directory, without path
+   $rindexprint = rindex($dirname, $dirsep, length($dirname)-2);
+   $rindsub = substr($dirname, $rindexprint+1, length($dirname)-$rindexprint-2);
+   $dirnamesingle{$dirname} = $rindsub;
+
+   # create name of html-directories 
+   $_ = $dirname;
+   s/$var{'dirmfiles'}/$var{'dirhtml'}/;
+   $dirhtmlname = $_;
+   if ($var{'producetree'} eq 'no') { $dirhtmlname = $var{'dirhtml'}; }
+   # try to open html directory, if error, then create directory,
+   # use same mode as for corresponding m-file directory
+   opendir(CHECKDIR,"$dirhtmlname") || do {
+      $dirmode = (stat($dirname))[2]; # print "$dirmode\n";
+      mkdir("$dirhtmlname", $dirmode) || die ("Cannot create directory $dirhtmlname: $! !");
+   };
+   closedir(CHECKDIR);
+
+
+   # read everything from this directory and process them
+   @ALLEFILES = readdir(AKTDIR);
+
+   foreach $file (@ALLEFILES) {
+      # exclude . and .. directories
+      next if $file eq '.';  next if $file eq '..';
+
+      # test for existense of entry (redundant, used for debugging)
+      if (-e $dirname.$file) {
+         # if it's a directory, call this function recursively
+         if (-d $dirname.$file) {
+            if ($var{'processtree'} eq 'yes') {
+               &ParseTreeReadFiles($dirname.$file.$dirsep, $localRecDeep+1);
+            }
+         }
+         # if it's a file - test for m-file, save name and create some arrays
+         elsif (-f $dirname.$file) {
+            if ($file =~ /\.$suffixforsearch$/i) {
+               # Remove the file suffix to establish the matlab identifiers
+               $filewosuffix = $file;
+               $filewosuffix =~ s/\.$suffixforsearch$//i;
+               # $filename = $name;
+
+               # Contents file in unix must start with a capital letter (Contents.m)
+               # ensure, that m-file name is lower case, except the contents file
+               if (! ($filewosuffix =~ /^contents$/i)) {
+		   # if ($var{'links2filescase'}  eq 'low') { $filewosuffix = "\L$filewosuffix\E"; }
+                  $filewosuffixlow = "\L$filewosuffix\E";
+               }
+               else { $contentsname{$dirname} = $filewosuffix; }
+
+               # internal handle name is always lower case
+               $name     = $filewosuffixlow;
+               # file name is not lower case
+               $filename = $filewosuffix;
+
+               # if don't use C|contents.m, then forget all C|contents.m
+               if ($var{'usecontentsm'} eq 'no') { if ($name =~ /contents/i) { next; } }
+
+               # if m-file with this name already exists, use directory and name for name
+               # only the first occurence of name will be used for links
+               if (defined $mfile{$name}) { 
+                  if (! ($name =~ /^contents$/i) ) {
+                     print "\r ParseTree - Name conflict:  $name in $dirname already exists: $mfile{$name} !\n";
+                     print   "             $mfile{$name}  will be used for links!\n";
+                  }
+                  $name = $dirname.$name;
+               }
+               # mfile name with path
+               $mfile{$name} = $dirname.$file;
+               # mfile name (without path)
+               $mfilename{$name} = $filename;
+               # mfile directory
+               $mfiledir{$name} = $dirname;
+               
+               # html file name and full path, special extension of Contents files
+               if ($name =~ /contents/i) { $extrahtmlfilename = $dirnamesingle{$dirname}; }
+               else { $extrahtmlfilename = ''; }
+               $hfile{$name} = $dirhtmlname.$mfilename{$name}.$extrahtmlfilename.$var{'exthtml'};
+
+               # save relative html path
+               # if ($var{'producetree'} eq 'yes') {
+               $hfilerelpath{$name} = $relpath;
+               # } else { # if no tree to produce, relative path is empty
+               #    $hfilerelpath{$name} = '';
+               # }
+
+               # create relative path from html file to directory with global index file
+               $hfileindexpath{$name} = $relpathtoindex;
+
+               # Function declaration, if one exists, set default to script
+               $synopsis{$name} = "";
+               $mtype{$name} = "script";
+
+               # First comment line
+               $apropos{$name} = "";
+
+               # count number of m-files in directories
+               $dirnumbermfiles{$dirname}++;
+
+               if ($debug > 1) {
+                  if ($opt_silent) { print "\r"; }
+                  print "   ParseTree: $name \t\t $mfile{$name} \t\t $hfile{$name}\t\t";
+                  if (!$opt_silent) { print "\n"; }
+               }
+            }
+         }
+         else {
+            print "Unknown type of file in $dirname: $file\n";
+         }
+      }
+      else { print "Error: Not existing file in $dirname: $file\n"; }
+   }
+
+   closedir(AKTDIR)
+   
+}
+
+#------------------------------------------------------------------------
+# Construct Dependency matrix
+#    $dep{$x,$y} > 0 if $x includes a reference to $y.
+#------------------------------------------------------------------------
+sub ConstructDependencyMatrix
+{
+   &ConstructDependencyMatrixReadFiles('all');
+   &ConstructDependencyMatrixReally;
+}
+
+
+#------------------------------------------------------------------------
+# Construct Dependency matrix
+#    $dep{$x,$y} > 0 if $x includes a reference to $y.
+#------------------------------------------------------------------------
+sub ConstructDependencyMatrixReadFiles
+{
+   local($whatstring) = @_;
+   local(*MFILE);
+   local($name, $inames);
+   local(%symbolsdep, %symbolsall);
+
+   # Initialize as all zeros.
+   # foreach $name (@names) { grep($dep{$name,$_}=0,@names); if ($debug > 0) { print "\r   DepMatrix anlegen: $name\t$#names\t"; } }
+
+   # Compute the dependency matrix
+   $inames = -1;
+   foreach $name (@names) {
+      # Read each file and tabulate the distinct alphanumeric identifiers in
+      # an array of symbols. Also scan for:
+      #   synopsis: The function declaration line
+      #   apropos:  The first line of the help text
+
+      # look for whatstring, if all: process every file, if contents: process only contents files 
+      if ($whatstring eq 'contents') { if (! ($name =~ /contents$/i) ) { next; } }
+      elsif ($whatstring eq 'all') { }    # do nothing
+      else { print "\r   ConstructDependency: Unknown parameter whatstring: $whatstring \n"; }
+
+      undef %symbolsall; undef %symbolsdep;
+      open(MFILE,"<$mfile{$name}") || die("Can't open $mfile{$name}: $!\n");
+      while (<MFILE>) {
+         chop;
+
+         # Split on nonalphanumerics, then look for all words, used for links later
+         # this one for all references
+         @wordsall = grep(/[a-zA-Z]\w*/, split('\W',$_));
+         # set all words to lower case for link checking
+         undef @wordsall2;
+         # do case conversion not, case checking is done later
+         foreach (@wordsall) { push(@wordsall2, "\L$_\E"); }
+         # @wordsall2 = @wordsall;
+         grep($symbolsall{$_}++, @wordsall2);
+
+         # Store first comment line, skip all others.
+         if (/^\s*%/) {
+            if (!$apropos{$name}) {
+               s/^\s*%\s*//;   # remove % and leading white spaces on line
+               $_ = &SubstituteHTMLEntities($_);
+               $apropos{$name} = $_;
+            }
+            next;
+         }
+
+         # If it's the function declaration line, then store it and skip
+         # but only, when first function definition (multiple function lines when private subfunctions in file
+         if ($synopsis{$name} eq '') {
+            if (/^\s*function/) {
+               s/^\s*function\s*//;
+               $synopsis{$name} = $_;
+               $mtype{$name} = "function";
+               next;
+            }
+         }
+
+         # Split off any trailing comments
+         if ($_ ne '') {
+            # this one for references in program code only
+            # when syntax parsing, here is a working place
+            ($statement) = split('%',$_,1);
+            @wordsdep = grep(/[a-zA-Z]\w*/,split('\W',$statement));
+            # do case conversion not, case checking is done later
+            undef @wordsdep2;
+            foreach (@wordsdep) { push(@wordsdep2, "\L$_\E"); }
+            grep($symbolsdep{$_}++, @wordsdep2);
+         }
+      }
+      close MFILE;
+
+      # compute intersection between %symbolsall and @linknames
+      delete($symbolsall{$name});
+      # foreach $localsumall ($symbolsall) {
+      #    $localsumall = "\L$localsumall\E";
+      # }
+      @{'all'.$name} = grep($symbolsall{$_}, @linknames);
+
+      # compute intersection between %symbolsdep and @linknames
+      delete($symbolsdep{$name});
+      @{'depcalls'.$name} = grep($symbolsdep{$_}, @linknames);
+
+      $inames++; print "\r   DepCallsMatrix: $inames/$#names\t $name\t";
+      if ($debug > 2) { print "\n      depnames: @{'depcalls'.$name}\n      all: @{'all'.$name}\n"; } 
+   }
+}
+
+
+#------------------------------------------------------------------------
+# Construct Dependency matrix
+#    $dep{$x,$y} > 0 if $x includes a reference to $y.
+#------------------------------------------------------------------------
+sub ConstructDependencyMatrixReally
+{
+   local($inames, $name);
+
+   $inames = -1;
+   foreach $name (@names) { undef %{'depint'.$name}; }
+   foreach $name (@names) {
+      grep(${'depint'.$_}{$name}++, @{'depcalls'.$name});
+      $inames++; print "\r   DepCalledMatrix1: $inames/$#names\t $name\t";
+   }
+   $inames = -1;
+   foreach $name (@names) {
+      # compute intersection between %depint.name{$_} and @linknames
+      if (defined (%{'depint'.$name})) { @{'depcalled'.$name} = grep(${'depint'.$name}{$_}, @linknames); }
+      $inames++; print "\r   DepCalledMatrix2: $inames/$#names\t $name\t";
+      if ($debug > 2) { print "\n      depcalled: @{'depcalled'.$name}\n"; }
+   }
+
+}
+
+
+#========================================================================
+# Construct all index files
+#========================================================================
+sub ConstructAllIndexFiles
+{
+   local(@localnames);
+   local($ActDir);
+   local($name);
+   
+   # define variables and names for frame target
+   $GlobalNameFrameMainLeft = 'Cont_Main';
+   $GlobalNameFrameMainRight = 'Cont_Lower';
+   $GlobalNameFrameAZIndexsmall = 'IndexAZindex';
+   $GlobalNameFrameAZIndexjump = 'IndexAZjump';
+
+   $indexcreated = 0;
+
+   &ConstructHighestIndexFile;
+   $indexcreated++;
+
+   # if ($var{'producetree'} eq 'yes') {
+      # moved next 2 lines out of if for producetree no
+      # &ConstructHighestIndexFile;
+      # $indexcreated++;
+   
+      foreach $ActDir (@directories) {
+         undef @localnames;
+         foreach $name (@names) {
+            local($pathsubstr) = substr($mfile{$name}, 0, rindex($mfile{$name}, "/")+1);
+            if ($ActDir eq $pathsubstr) {
+               if ($debug > 1) { print "IndexFile: $pathsubstr    ActDir: $ActDir   Hfilerelpath: $hfilerelpath{$name}\n"; }
+               push(@localnames, $name);
+            }
+         }
+         if ($debug > 2) { print "localnames: @localnames\n"; }
+         # create contents file and short|long index of files in local directory
+         &ConstructContentsmFile($ActDir, @localnames);
+         &ConstructAZIndexFile($ActDir, 'short', 'local', @localnames);
+         &ConstructAZIndexFile($ActDir, 'long', 'local', @localnames);
+         $indexcreated+=2;
+      }   
+   # } else {
+   #    &ConstructContentsmFile($var{'dirmfiles'}, @names);
+   # }
+
+   # create short|long index of files in all directory
+   &ConstructAZIndexFile($var{'dirmfiles'}, 'short', 'global', @names);
+   &ConstructAZIndexFile($var{'dirmfiles'}, 'long', 'global', @names);
+   $indexcreated+=2;
+
+   # if contents.m were created or updated, the dependency matrices should 
+   # be updated as well
+   if ($var{'writecontentsm'} eq 'yes') { &ConstructDependencyMatrixReadFiles('contents');; }
+}
+
+
+#========================================================================
+# Construct the highest level index file
+#========================================================================
+sub ConstructHighestIndexFile
+{
+   local(*IFILE);
+   local($indexfile, $filename);
+
+   # Build the frame layout file, this files includes the layout of the frames
+   # Build the frame layout file name (highest one)
+   $indexfile = $var{'dirhtml'}.$var{'filenametopframe'}.$var{'exthtml'};
+
+   if ($var{'frames'} eq 'yes') {
+
+       open(IFILE,">$indexfile") || die("Cannot open frame layout file $indexfile\n");
+
+       # Write the header of frame file
+       print IFILE "$TextDocTypeFrame\n<html>\n<head>\n$var{'codeheadmeta'}\n$TextMetaCharset\n";
+       print IFILE "   <title>$var{'texttitleframelayout'}</title>\n";
+       print IFILE "</head>\n";
+
+       # definition of 2 frames, left the tree of directories,
+       # right the index of that directory or the docu of a file
+       print IFILE "<frameset  cols=\"25%,75%\">\n";
+       print IFILE "   <frame src=\"$var{'filenamedirshort'}$var{'exthtml'}\" name=\"$GlobalNameFrameMainLeft\" />\n";
+       print IFILE "   <frame src=\"$var{'filenameindexshortglobal'}$var{'filenameextensionframe'}$var{'exthtml'}\" name=\"$GlobalNameFrameMainRight\" />\n";   print IFILE "</frameset>\n";
+
+       print IFILE "</html>\n";
+
+       close(IFILE);
+
+       if ($opt_silent) { print "\r"; }
+       print "   Frame layout file created: $indexfile\t";
+       if (!$opt_silent) { print "\n"; }
+   }
+
+   for($irun=0; $irun <= 2; $irun++) {
+      # Build the top directory index file, these files include the directory tree
+      # Build the directory tree index file name
+      
+      # Create no directory file for contents, when no contents to use
+      if (($irun == 2) && ($var{'usecontentsm'} eq 'no')) { next; }
+
+      # Assign the correct index file name
+      if ($irun == 0) { $filename = $var{'filenamedirshort'}; }
+      elsif ($irun == 1) { $filename = $var{'filenamedirlong'}; }
+      elsif ($irun == 2) { $filename = $var{'filenamedircontents'}; }
+      
+      $indexfile = $var{'dirhtml'}.$filename.$var{'exthtml'};
+
+      open(IFILE,">$indexfile") || die("Cannot open directory tree index file $indexfile\n");
+      # Write header of HTML file
+      print IFILE "$TextDocTypeHTML\n<html>\n<head>\n$var{'codeheadmeta'}\n$TextMetaCharset\n$var{'csslink'}\n";
+
+      if ($var{'texttitleindexalldirs'} eq '') {
+         print IFILE "<title>Index of Directories of $var{'dirmfiles'}</title>\n";
+      } else {
+         print IFILE "<title>$var{'texttitleindexalldirs'}</title>\n";
+      }
+      
+      if ($var{'frames'} eq 'yes') {
+	  print IFILE "<base target=\"$GlobalNameFrameMainRight\" />\n";
+      }
+
+      print IFILE "</head>\n";
+      print IFILE "<body $var{'codebodyindex'}>\n";
+      print IFILE "<div id=\"matlabdoc\">\n";
+      if ($var{'textheaderindexalldirs'} eq '') {
+         print IFILE "<h1 $var{'codeheader'}>Index of Directories of <em>$var{'dirmfiles'}</em></h1>\n";
+      } else {
+         print IFILE "<h1 $var{'codeheader'}>$var{'textheaderindexalldirs'}</h1>\n";
+      }
+      print IFILE "<p>\n";
+
+      if ($var{'frames'} eq 'yes') {
+	  if ($irun == 0) { print IFILE "<strong>short</strong>\n"; }
+	  else { print IFILE "<a href=\"$var{'filenamedirshort'}$var{'exthtml'}\" target=\"$GlobalNameFrameMainLeft\">short</a>\n"; }
+	  if ($irun == 1) { print IFILE " | <strong>long</strong>\n"; }
+	  else { print IFILE " | <a href=\"$var{'filenamedirlong'}$var{'exthtml'}\" target=\"$GlobalNameFrameMainLeft\">long</a>\n"; }
+	  if ($var{'usecontentsm'} eq 'yes') {
+	      if ($irun == 2) { print IFILE " | <strong>contents</strong>\n"; }
+	      else { print IFILE " | <a href=\"$var{'filenamedircontents'}$var{'exthtml'}\" target=\"$GlobalNameFrameMainLeft\">contents</a>\n"; }
+	  }
+      } else {
+	  if ($irun == 0) { print IFILE "<strong>short</strong>\n"; }
+	  else { print IFILE "<a href=\"$var{'filenamedirshort'}$var{'exthtml'}\">short</a>\n"; }
+	  if ($irun == 1) { print IFILE " | <strong>long</strong>\n"; }
+	  else { print IFILE " | <a href=\"$var{'filenamedirlong'}$var{'exthtml'}\">long</a>\n"; }
+	  if ($var{'usecontentsm'} eq 'yes') {
+	      if ($irun == 2) { print IFILE " | <strong>contents</strong>\n"; }
+	      else { print IFILE " | <a href=\"$var{'filenamedircontents'}$var{'exthtml'}\">contents</a>\n"; }
+	  }
+      }
+   
+      print IFILE "</p><br />\n\n";
+      print IFILE "<ul>\n";
+
+      # go through all directories and create a list entry for each one,
+      # depending on recursion level create sublists
+      $prevrecdeeplevel = 0;
+      foreach $name (@alldirectories) {
+         $actrecdeeplevel = $dirnamerecdeep{$name};
+         for( ; $prevrecdeeplevel < $actrecdeeplevel; $prevrecdeeplevel++ ) { print IFILE "<ul>\n"; }
+         for( ; $prevrecdeeplevel > $actrecdeeplevel; $prevrecdeeplevel-- ) { print IFILE "</ul>\n"; }
+         if ($irun == 0) { $indexfilenameused = $var{'filenameindexshortlocal'}.$var{'filenameextensionframe'}; }
+         elsif ($irun == 1) { $indexfilenameused = $var{'filenameindexlonglocal'}.$var{'filenameextensionframe'}; }
+         elsif ($irun == 2) { $indexfilenameused = $contentsname{$name}; }
+         else { die "ConstructHighestIndexFile: Unknown value of irun"; }
+         if ($dirnumbermfiles{$name} > 0) {
+            # producetree no
+            # if ($var{'producetree'} eq 'no') { $dirnamehere = ''; }
+            # else { $dirnamehere = '$dirnamerelpath{$name}'; }
+            # print IFILE "<LI><A HREF=\"$dirnamehere$indexfilenameused_$dirnamesingle{$name}$var{'exthtml'}\">$dirnamesingle{$name}</A>\n";
+            print IFILE "<li><a href=\"$dirnamerelpath{$name}$indexfilenameused$dirnamesingle{$name}$var{'exthtml'}\">$dirnamesingle{$name}</a></li>\n";
+         } else { 
+            # print directories with no m-files inside not
+            # print IFILE "<li>$dirnamesingle{$name}</li>\n";
+         }
+      }
+      $actrecdeeplevel = 0;
+      for( ; $prevrecdeeplevel > $actrecdeeplevel; $prevrecdeeplevel-- ) { print IFILE "</ul>\n"; }
+      print IFILE "</ul>\n<br />$var{'codehr'}\n";
+
+      # Include info about author from authorfile
+      &WriteFile2Handle($var{'authorfile'}, IFILE);
+
+      print IFILE "<!--navigate-->\n";
+      print IFILE "<!--copyright-->\n";
+      print IFILE "</div>\n</body>\n</html>\n";
+
+      close(IFILE);
+
+      if ($opt_silent) { print "\r"; }
+      print "   Directory - Indexfile created: $indexfile\t";
+      if (!$opt_silent) { print "\n"; }
+   }
+}
+
+
+#========================================================================
+# Construct the A-Z index file (global/local and/or short/long)
+#========================================================================
+sub ConstructAZIndexFile
+{
+   local($LocalActDir, $LocalShortLong, $LocalGlobalLocal, @localnames) = @_;
+   local(*IFILE);
+   local($name, $indexfilename, $dirpath);
+   local($firstletter, $firstone);
+   
+   if ($debug > 2) { print "localnames in AZ small: @localnames\n"; print "     ActDir in A-Z: $LocalActDir\n"; }
+   
+   # extract filename of index file from parameters of function
+   if ($LocalShortLong eq 'short') {
+      if ($LocalGlobalLocal eq 'global') { $indexfilename = $var{'filenameindexshortglobal'}; }
+      elsif ($LocalGlobalLocal eq 'local') { $indexfilename = $var{'filenameindexshortlocal'}; }
+      else { die "wrong parameter for LocalGlobalLocal in ConstructAZIndexFile: $LocalGlobalLocal."; }
+   } elsif ($LocalShortLong eq 'long') {
+      if ($LocalGlobalLocal eq 'global') { $indexfilename = $var{'filenameindexlongglobal'}; }
+      elsif ($LocalGlobalLocal eq 'local') { $indexfilename = $var{'filenameindexlonglocal'}; }
+      else { die "wrong parameter for LocalGlobalLocal in ConstructAZIndexFile: $LocalGlobalLocal."; }
+   } else { die "wrong parameter for LocalShortLong in ConstructAZIndexFile: $LocalShortLong."; }
+   
+   # producetree no
+   # if ($var{'producetree'} eq 'no') { $dirnamehere = ''; }
+   # else { $dirnamehere = '$dirnamerelpath{$LocalActDir}'; }
+   # Build the index file name
+   # handle the global index file case separately (no extra directory name in file)
+   #    the local index file name must be extended by the name of the directory
+   if ($LocalGlobalLocal eq 'global') { $extradirfilename = ''; }
+   else { $extradirfilename = $dirnamesingle{$LocalActDir}; }
+   $indexfile = $var{'dirhtml'}.$dirnamerelpath{$LocalActDir}.$indexfilename.$var{'filenameextensionindex'}.$extradirfilename.$var{'exthtml'};
+
+   if ($LocalShortLong eq 'short' and $extradirfilename eq '' and $var{'frames'} ne 'yes') {
+       # With no frames and no subdir path, this must go in the
+       # top-level index file instead
+       $indexfile = $var{'dirhtml'}.$var{'filenametopframe'}.$var{'exthtml'};
+   }
+
+   if ($debug > 2) { print "   indexfilename (a-z small): $indexfile\n"; }
+
+   open(IFILE,">$indexfile") || die("Cannot open index file $indexfile: $!\n");
+
+   # Write the header of HTML file
+   print IFILE "$TextDocTypeHTML\n<html>\n<head>\n$var{'codeheadmeta'}\n$TextMetaCharset\n$var{'csslink'}\n";
+   
+   my $dirToPrint = $LocalActDir;
+   $dirToPrint =~ s,^./,,;
+
+   if ($var{'texttitleindex'} eq '') {
+      print IFILE "<title>Index of Matlab Files in Directory $dirToPrint</title>\n";
+   } else {
+      if ($LocalGlobalLocal eq 'global') { print IFILE "<title>$var{'texttitleindex'}</title>\n"; }
+      else { print IFILE "<title>$var{'texttitleindex'} in Directory $dirToPrint</title>\n"; }
+   }
+
+   if ($var{'frames'} eq 'yes') {
+       print IFILE "<base target=\"$GlobalNameFrameMainRight\" />\n";
+   }
+   print IFILE "</head>\n";
+
+   print IFILE "<body $var{'codebodyindex'}>\n";
+   print IFILE "<div id=\"matlabdoc\">\n";
+   if ($var{'textheaderindex'} eq '') {
+      print IFILE "<h1 $var{'codeheader'}>Index of Matlab Files in Directory $dirToPrint</h1>\n";
+   } else {
+      if ($LocalGlobalLocal eq 'global') { print IFILE "<h1 $var{'codeheader'}>$var{'textheaderindex'}</h1>\n"; }
+      else { print IFILE "<h1 $var{'codeheader'}>$var{'textheaderindex'} in Directory $dirToPrint</h1>\n"; }
+   }
+
+   # include links to indexes
+   &ConstructLinks2Index(IFILE, $dirnamerelpathtoindex{$LocalActDir}, $LocalActDir, $LocalGlobalLocal);
+
+   # Collect the starting letters of m files in this directory or all m-files
+   for('a'..'z') { undef @{$_}; }
+   foreach $name (@localnames) {
+      if (! ($mfilename{$name} =~ /contents/i)) {
+         $firstletter = substr($mfilename{$name}, 0, 1);
+         # convert first letter always to lower case
+         # needed for reference to lower and upper case m-files
+         $firstletter = "\L$firstletter\E";
+         push(@{$firstletter}, $name);
+      }
+   }
+   
+   if ($LocalShortLong eq 'short') {
+      # begin create short index
+      print IFILE "<table width=\"100%\">\n";
+
+      for('a'..'z') {
+         # print "   $_: @{$_}\n";
+         $numberofletter = $#{$_}+1;
+	 $cols = 3;
+         if ($numberofletter > 0) {
+            print IFILE "\n<tr><td><br/><strong><a name=\"\U$_\E$_\"></a><span class=\"an\">\U$_\E</span></strong></td></tr>\n";
+            for ($count = 0; $count < $numberofletter; $count++) {
+		if (($count % $cols) == 0) {
+		    if ($count > 0) {
+			print IFILE "</tr><tr>\n";
+		    }
+		    print IFILE "<tr><td></td>";
+		}
+		$name = @{$_}[$count];
+		if ($LocalGlobalLocal eq 'global') { $dirpath = $hfilerelpath{$name}; } else { $dirpath = ""; }
+		print IFILE "<td><a href=\"$dirpath$mfilename{$name}$var{'exthtml'}\">$mfilename{$name}</a></td>";
+	    }
+
+	    print IFILE "</tr>\n";
+		    
+            # $numberhalf = ($numberofletter + 1 - (($numberofletter+1) % 2))/2;
+            # if ($debug > 2) { print "   $_: @{$_} \t $numberhalf \t $numberofletter\n"; }
+            # for($count = 0; $count < $numberhalf; $count++) {
+            #    $name = @{$_}[$count];
+            #    if ($LocalGlobalLocal eq 'global') { $dirpath = $hfilerelpath{$name}; } else { $dirpath = ""; }
+            #    print IFILE "<tr><td width=\"50%\"><a href=\"$dirpath$mfilename{$name}$var{'exthtml'}\">$mfilename{$name}</a></td>";
+            #    if (($count + $numberhalf) < $numberofletter) {
+            #       $name = @{$_}[$count + $numberhalf];
+            #       if ($LocalGlobalLocal eq 'global') { $dirpath = $hfilerelpath{$name}; } else { $dirpath = ""; }
+            #       print IFILE "<td width=\"50%\"><a href=\"$dirpath$mfilename{$name}$var{'exthtml'}\">$mfilename{$name}</a></td></tr>\n";
+            #    } else {
+            #       print IFILE "<td width=\"50%\"></td></tr>\n";
+            #    }
+            # }
+         }
+      }
+      print IFILE "</table>\n<br />$var{'codehr'}\n";
+
+   } elsif ($LocalShortLong eq 'long') {
+      # begin create long index
+      print IFILE "<table width=\"100%\">\n";
+      print IFILE "<tr><th>Name</th><th>Synopsis</th></tr>\n";
+
+      for('a'..'z') {
+         # print "   $_: @{$_}\n";
+         $numberofletter = $#{$_}+1;
+         if ($numberofletter > 0) {
+            $firstone = 1;
+            foreach $name (@{$_}) {
+               if ($debug > 1) { print "   AZinforeach1: $name \t\t $hfilerelpath{$name} \t\t $dirnamerelpath{$LocalActDir}\n"; }
+               if ($LocalGlobalLocal eq 'global') { $dirpath = $hfilerelpath{$name}; } else { $dirpath = ""; }
+               if (! ($mfilename{$name} =~ /contents/i)) {
+                  if ($firstone == 1) { print IFILE "\n<tr><td colspan=\"2\"><br /><strong><a name=\"\U$_\E$_\"></a><span class=\"an\">\U$_\E</span></strong></td></tr>\n"; $firstone = 0; } 
+                  print IFILE "<tr><td valign=\"top\"><a href=\"$dirpath$mfilename{$name}$var{'exthtml'}\">$mfilename{$name}</a></td><td>$apropos{$name}</td></tr>\n";
+               }
+            }
+         }
+      }
+      print IFILE "</table>\n<br />$var{'codehr'}\n";
+   } else { die "wrong parameter for LocalShortLong in ConstructAZIndexFile: $LocalShortLong."; }
+
+   # Include info about author from authorfile
+   &WriteFile2Handle($var{'authorfile'}, IFILE);
+
+   print IFILE "<!--navigate-->\n";
+   print IFILE "<!--copyright-->\n";
+   print IFILE "</div>\n</body>\n</html>\n";
+
+   close(IFILE);
+
+   if ($opt_silent) { print "\r"; }
+   print "   Indexfile small (A-Z) created: $indexfile\t";
+   if (!$opt_silent) { print "\n"; }
+
+
+   # Build the A-Z jump index file name
+   # handle the global index file case separately (no extra directory name in file)
+   if ($LocalGlobalLocal eq 'global') { $extradirfilename = ''; }
+   else { $extradirfilename = $dirnamesingle{$LocalActDir}; }
+
+   if ($var{'frames'} eq 'yes') {
+
+       $indexfile = $var{'dirhtml'}.$dirnamerelpath{$LocalActDir}.$indexfilename.$var{'filenameextensionjump'}.$extradirfilename.$var{'exthtml'};
+       if ($debug > 2) { print "   indexfilename (a-z jump): $indexfile\n"; }
+       open(IFILE,">$indexfile") || die("Cannot open jump index file $indexfile: $!\n");
+
+       # Write the header of HTML file
+       print IFILE "$TextDocTypeHTML\n<html>\n<head>\n$var{'codeheadmeta'}\n$TextMetaCharset\n$var{'csslink'}\n";
+       
+       if ($var{'texttitleindex'} eq '') {
+	   print IFILE "<title>A-Z jump index in directory $dirToPrint</title>\n";
+       } else {
+	   if ($LocalGlobalLocal eq 'global') { print IFILE "<title>$var{'texttitleindex'}</title>\n"; }
+	   else { print IFILE "<title>$var{'texttitleindex'} in Directory $dirToPrint</title>\n"; }
+       }
+
+       if ($var{'frames'} eq 'yes') {
+	   print IFILE "<base target=\"$GlobalNameFrameAZIndexsmall\" />\n";
+       }
+       print IFILE "</head>\n";
+       print IFILE "<body $var{'codebodyindex'}>\n";
+       print IFILE "<div id=\"matlabdoc\">\n";
+
+       # Write the A-Z jump line, generate link for letters with files starting with this letter
+       # and only letters for no files starting with this letter
+       # use previously generated arrays with names of files sorted by starting letter
+       for('a'..'z') {
+	   $numberofletter = $#{$_}+1;
+	   if ($numberofletter > 0) {
+	       print IFILE "<strong><a href=\"$indexfilename$var{'filenameextensionindex'}$extradirfilename$var{'exthtml'}#\U$_\E$_\">\U$_\E</a> </strong>\n";
+	   } else {
+	       print IFILE "\U$_\E \n";
+	   }
+       }
+
+       print IFILE "</div></body>\n</html>\n";
+
+       close(IFILE);
+
+       if ($opt_silent) { print "\r"; }
+       print "   Indexfile small (A-Z jump) created: $indexfile\t";
+       if (!$opt_silent) { print "\n"; }
+   }
+
+
+   # Build the frame layout file, this file includes the layout of the frames
+   # Build the frame layout file name (for small/compact A-Z index)
+   # handle the global index file case separately (no extra directory name in file)
+   if ($LocalGlobalLocal eq 'global') { $extradirfilename = ''; }
+   else { $extradirfilename = $dirnamesingle{$LocalActDir}; }
+
+   if ($var{'frames'} eq 'yes') {
+
+       $indexfile = $var{'dirhtml'}.$dirnamerelpath{$LocalActDir}.$indexfilename.$var{'filenameextensionframe'}.$extradirfilename.$var{'exthtml'};
+       if ($debug > 2) { print "   indexfilename (a-z frame): $indexfile\n"; }
+
+       open(IFILE,">$indexfile") || die("Cannot open jump index frame file $indexfile: $!\n");
+
+       # Write the header of Frame file
+       print IFILE "$TextDocTypeHTML\n<html>\n<head>\n$var{'codeheadmeta'}\n$TextMetaCharset\n$var{'csslink'}\n";
+
+       if ($var{'texttitleindex'} eq '') {
+	   print IFILE "<title>Index of Matlab Files in Directory $dirToPrint</title>\n";
+       } else {
+	   if ($LocalGlobalLocal eq 'global') { print IFILE "<title>$var{'texttitleindex'}</title>\n"; }
+	   else { print IFILE "<title>$var{'texttitleindex'} in Directory $dirToPrint</title>\n"; }
+       }
+       print IFILE "</head>\n";
+
+       # definition of 2 frames, top the A-Z index, below the jump letter line
+       print IFILE "<frameset  rows=\"90%,10%\">\n";
+       print IFILE "   <frame src=\"$indexfilename$var{'filenameextensionindex'}$extradirfilename$var{'exthtml'}\" name=\"$GlobalNameFrameAZIndexsmall\" />\n";
+       print IFILE "   <frame src=\"$indexfilename$var{'filenameextensionjump'}$extradirfilename$var{'exthtml'}\" name=\"$GlobalNameFrameAZIndexjump\" />\n";
+       print IFILE "</frameset>\n";
+
+       print IFILE "</html>\n";
+
+       close(IFILE);
+
+       if ($opt_silent) { print "\r"; }
+       print "   Frame layout file created: $indexfile\t";
+       if (!$opt_silent) { print "\n"; }
+   }
+}
+   
+
+#========================================================================
+# Construct the links to all indexes
+#========================================================================
+sub ConstructLinks2Index
+{
+   local(*WRITEFILE, $LocalPath2Index, $PathContents, $LocalGlobalLocal) = @_;
+   
+   # include links to short/long - local/global index and C|contents.m
+   print WRITEFILE "\n<p>";
+   print WRITEFILE "$var{'textjumpindexglobal'} ";
+
+   if ($var{'frames'} eq 'yes') {
+       print WRITEFILE "<a href=\"$LocalPath2Index$var{'filenameindexshortglobal'}$var{'filenameextensionframe'}$var{'exthtml'}\">short</a> | ";
+       print WRITEFILE "<a href=\"$LocalPath2Index$var{'filenameindexlongglobal'}$var{'filenameextensionframe'}$var{'exthtml'}\">long</a>\n";
+   } else {
+       print WRITEFILE "<a href=\"$LocalPath2Index$var{'filenametopframe'}$var{'exthtml'}\">short</a> | ";
+       print WRITEFILE "<a href=\"$LocalPath2Index$var{'filenameindexlongglobal'}$var{'filenameextensionindex'}$var{'exthtml'}\">long</a>\n";
+   }
+
+   if ($LocalGlobalLocal eq 'local') {
+      if ($var{'usecontentsm'} eq 'yes') {
+         print WRITEFILE " | <a href=\"$contentsname{$PathContents}$dirnamesingle{$PathContents}$var{'exthtml'}\">Local contents</a>\n";
+      }
+      if ($var{'frames'} eq 'yes') {
+         print WRITEFILE " | $var{'textjumpindexlocal'} ";
+         print WRITEFILE "<a href=\"$var{'filenameindexshortlocal'}$var{'filenameextensionframe'}$dirnamesingle{$PathContents}$var{'exthtml'}\">short</a> | ";
+         print WRITEFILE "<a href=\"$var{'filenameindexlonglocal'}$var{'filenameextensionframe'}$dirnamesingle{$PathContents}$var{'exthtml'}\">long</a>\n";
+      } else {
+         print WRITEFILE " | $var{'textjumpindexlocal'} ";
+         print WRITEFILE "<a href=\"$var{'filenameindexshortlocal'}$var{'filenameextensionindex'}$dirnamesingle{$PathContents}$var{'exthtml'}\">short</a> | ";
+         print WRITEFILE "<a href=\"$var{'filenameindexlonglocal'}$var{'filenameextensionindex'}$dirnamesingle{$PathContents}$var{'exthtml'}\">long</a>\n";
+      }
+   }
+   print WRITEFILE "</p>\n\n";
+   print WRITEFILE "$var{'codehr'}\n";
+}
+
+
+#========================================================================
+# Construct the contents.m files or update
+#========================================================================
+sub ConstructContentsmFile
+{
+   local($LocalActDir, @localnames) = @_;
+   local(*CFILE, $name,$newline);
+   local($contentsfile, $isincontentsonly);
+   local(@lines, @autoaddlines, @emptylines);
+   local($autoadd) = 'AutoAdd';
+   local($autoaddsection) = 0;
+   local($emptylineflag) = 0;
+   local(%nameincontents);
+   
+   # Build the contents file name
+   $contentsfile = $LocalActDir.$contentsname{$LocalActDir}.$suffix;
+   
+   if (-e $contentsfile) {
+      open(CFILE,"<$contentsfile") || die("Cannot open contents file $contentsfile: $!\n");
+      while (<CFILE>) {
+         # Search for the specified string pattern
+         @words = split;
+         if ((@words >= 3) && ($words[2] eq '-')) {
+            $isincontentsonly = 0;
+            foreach $name (@localnames) {
+               if ($name eq $words[1]) {    # old
+               # if ($mfilename{$name} eq $words[1]) {
+                  $isincontentsonly = 1;
+                  $nameincontents{$name} = 1;
+                  $newline = sprintf("%% %-13s - %s\n", $mfilename{$name}, $apropos{$name});
+                  push(@lines, $newline);
+               }
+            }
+            # issue a warning, if file is in contents, but not as file in the directory
+            if ($isincontentsonly == 0) {
+               print "\rConstructContents: Obsolete entry  $words[1]  in  $contentsfile ! Entry not used.\n";
+            }
+         } else {
+            # look for the AutoAdd section, should be the second word
+            if ((@words >= 2) && ($words[1] eq $autoadd)) { $autoaddsection = 1; }
+            # push the red line in an array
+            push(@lines, $_); 
+         }
+      }   
+      close(CFILE);
+   } else {
+      $newline = "% MATLAB Files in directory  $LocalActDir\n%\n";
+      push(@lines, $newline);
+      
+   }
+   
+   # collect the file names, that were not included in original C|contents.m
+   foreach $name (@localnames) {
+      if (! defined $nameincontents{$name}) {
+         if (! ($mfilename{$name} =~ /contents/i)) {
+            $newline = sprintf("%% %-13s - %s\n", $mfilename{$name}, $apropos{$name});
+            push(@autoaddlines, $newline);
+         }   
+      }
+   }
+
+   # write/update C|contents.m only if variable is set
+   if ($var{'writecontentsm'} eq 'yes') {
+      unlink($contentsfile);
+      open(CFILE,">$contentsfile") || die("Cannot open contents file $contentsfile: $!\n");
+      # write old C|contents.m or header of new file, as long as comment lines
+      foreach $line (@lines) {
+         if ($emptylineflag == 0) {
+            if ($line =~ /^\s*%/) { print CFILE $line; }
+            else { $emptylineflag = 1; push(@emptylines, $line); }
+         } else { push(@emptylines, $line); }
+      }
+      # add header of AutoAdd section
+      if (($autoaddsection == 0) && (@autoaddlines > 0)) { print CFILE "%\n% $autoadd\n"; }
+      # add autoadd section lines (previously undocumented files
+      foreach $line (@autoaddlines) { print CFILE $line; }
+      # add tail of original C|contents.m (everything behind first non-comment line)
+      foreach $line (@emptylines)   { print CFILE $line; }
+      print CFILE "\n";
+      close CFILE;
+      if ($opt_silent) { print "\r"; }
+      print "   Contents file created/updated: $contentsfile\t";
+      if (!$opt_silent) { print "\n"; }
+   }
+}
+
+
+#========================================================================
+# Replace found special characters with their HTMl Entities
+#========================================================================
+sub SubstituteHTMLEntities {
+   local($_) = @_;
+   
+   # Replace & <-> &amp;  < <-> &lt;  > <-> &gt;  " <-> &quot;
+   s/&/&amp;/g; s/\</&lt;/g; s/\>/&gt;/g; s/\"/&quot;/g;
+   return $_;
+}
+
+#========================================================================
+# Replace found m-filenamestring with full link.
+#========================================================================
+sub SubstituteName2Link {
+   local($_, $funname) = @_;
+   local($refstr1, $refstr2, $reffound);
+   
+   # Look for something matching in the line
+   if ( /(\W+)($funname)(\W+)/i ) {
+      $reffound = $2;
+      $refstr1 = "<a class=\"mfun\" href=\"$hfileindexpath{$name}$hfilerelpath{$funname}$mfilename{$funname}$var{'exthtml'}\">";
+      $refstr2 = "<\/a>";
+      # Do links only for exact case match
+      if ( ($var{'links2filescase'}  eq 'exact') || ($var{'links2filescase'}  eq 'exactvery') ) {
+         if ( /(\W+)($funname)(\W+)/g ) {
+            s/(\W+)($funname)(\W+)/$1$refstr1$funname$refstr2$3/g;
+         }
+         else {
+            # Print info for not matching case in references, good for check up of files
+            if ( ($var{'links2filescase'}  eq 'exactvery') ) {
+               print "Diff in case found: $funname  (case of file name)   <->  $reffound  (case in source code)\n";  
+               print "     (source line)  $_ \n";
+            }
+         }
+      }
+      # Do links for exact match and additionally for all upper case (often used in original matlab help text)
+      elsif ( ($var{'links2filescase'}  eq 'exactupper') ) {
+         s/(\W+)($funname)(\W+)/$1$refstr1$2$refstr2$3/g;
+         $funname2 = "\U$funname\E";
+         s/(\W+)($funname2)(\W+)/$1$refstr1$2$refstr2$3/g;
+      }
+      # Do links for all case mixes, this calls for trouble under LINUX/UNIX
+      else {  #elsif ( ($var{'links2filescase'}  eq 'all') )
+         s/(\W+)($funname)(\W+)/$1$refstr1$2$refstr2$3/ig;
+      }
+   }
+   
+   return $_;
+}
+
+#========================================================================
+# Construct the html files for each matlab file.
+#    Need to reread each matlab file to find the help text.
+#    Note that we can't do this in a single loop because sometimes
+#    the help text maybe before the function declaration.
+#========================================================================
+sub ConstructHTMLFiles
+{
+   local(*MFILE);
+   local(*HFILE);
+
+   local($filescreated) = 0;
+   local($functionline);
+   
+   foreach $name (@names) {
+      # Create cross reference information already here, used for keywords as well
+      # Construct list of referenced functions
+      @xref = @{'depcalls'.$name};    # the functions, that this m-file calls
+      @yref = @{'depcalled'.$name};   # the functions, that this m-file is called from
+      # print "   depcalls: @{'depcalls'.$name}\n   depcalled: @{'depcalled'.$name}\n";
+      # foreach $cname (@names) { next if $cname eq $name; push(@yref,$cname) if grep(/$name/,@{'depcalls'.$cname}); }
+
+
+      # Open m-file and html-file
+      open(MFILE,"<$mfile{$name}");
+      open(HFILE,">$hfile{$name}");
+
+      # Write the header of HTML file
+      print HFILE "$TextDocTypeHTML\n<html>\n<head>\n$var{'codeheadmeta'}\n$TextMetaCharset\n$var{'csslink'}\n";
+
+      # Write meta tags: use apropos (one line function description) for description
+      # and cross reference function names for keywords (any better ideas?)
+      print HFILE "<meta name=\"description\" content=\" $apropos{$name} \" />\n";
+      print HFILE "<meta name=\"keywords\" content=\" @xref @yref \" />\n";
+
+      # Write Title and start body of html-file
+      print HFILE "<title>$var{'texttitlefiles'} $mfilename{$name}</title>\n</head>\n";
+      print HFILE "<body $var{'codebodyfiles'}>\n";
+      print HFILE "<div id=\"matlabdoc\">\n";
+      print HFILE "<h1 $var{'codeheader'}>$var{'textheaderfiles'} $mfilename{$name}</h1>\n";
+
+# http://test.soundsoftware.ac.uk/cannam/projects/smallbox/repository/annotate/DL/RLS-DLA/SolveFISTA.m
+#      print HFILE "<a href=\"" . $hfileindexpath{$name} . "../../projects/smallbox/repository/annotate/" . $mfiledir{$name}  . $mfilename{$name} . ".m\">View in repository</a>\n";
+
+      print HFILE "$var{'codehr'}\n";
+
+      # include links to short/long - local/global index and C|contents.m
+      &ConstructLinks2Index(HFILE, $hfileindexpath{$name}, $mfiledir{$name}, 'local');
+
+      # If this is a function, then write out the first line as a synopsis
+      if ($mtype{$name} eq "function") {
+         print HFILE "<h2 $var{'codeheader'}>Function Synopsis</h2>\n";
+         print HFILE "<pre>$synopsis{$name}</pre>\n$var{'codehr'}\n";
+      }
+
+      # Look for the matlab help text block
+      $functionline = "\n";
+      do {
+         $_ = <MFILE>;
+         # remember functionline, if before help text block
+         if (/^\s*function/) { $functionline = $_; }
+      } until (/^\s*%/ || eof);
+      if (! (eof(MFILE))) {
+         print HFILE "<h2 $var{'codeheader'}>Help text</h2>\n";
+         print HFILE "<pre>\n";
+         while (/^\s*%/) {
+            # First remove leading % and white space, then Substitute special characlers
+            s/^\s*%//;
+            $_ = &SubstituteHTMLEntities($_);
+
+            # check/create cross references
+            foreach $funname (@{'all'.$name}) {
+               if ($funname =~ /simulink/) { print "\n Simulink - Filename: $name;  scanname: $funname\n"; }
+               next if $funname eq $name;
+               $_ = &SubstituteName2Link($_, $funname);
+            }
+            print HFILE $_;
+            if (! eof) { $_ = <MFILE>; }
+         }
+         print HFILE "</pre>\n$var{'codehr'}\n";
+      }
+
+      # Write the cross reference information
+      if (@xref || @yref) {
+         print HFILE "<h2 $var{'codeheader'}>Cross-Reference Information</H2>\n";
+         print HFILE "<table border=\"0\" width=\"100%\">\n<tr align=\"left\">\n<th width=\"50%\">";
+         if (@xref) {
+            print HFILE "This $mtype{$name} calls";
+         }
+         print HFILE "</th>\n<th width=\"50%\">";
+         if (@yref) {
+            print HFILE "This $mtype{$name} is called by";
+         }
+         print HFILE "</th>\n</tr>\n<tr valign=\"top\"><td>";
+         if (@xref) {
+            print HFILE "\n<ul>\n";
+            foreach $cname (sort @xref) {
+               print HFILE "<li><a class=\"mfun\" href=\"$hfileindexpath{$name}$hfilerelpath{$cname}$mfilename{$cname}$var{'exthtml'}\">$mfilename{$cname}</a></li>\n";
+            }
+            print HFILE "</ul>\n";
+         }
+         print HFILE "</td><td>";
+         if (@yref) {
+            print HFILE "\n<ul>\n";
+            foreach $cname (sort @yref) {
+               print HFILE "<li><a class=\"mfun\" href=\"$hfileindexpath{$name}$hfilerelpath{$cname}$mfilename{$cname}$var{'exthtml'}\">$mfilename{$cname}</a></li>\n";
+            }
+            print HFILE "</ul>\n";
+         }
+         print HFILE "</td>\n</tr>\n</table>\n";
+         print HFILE "$var{'codehr'}\n";
+      }
+
+      # Include source text if requested
+      if (($var{'includesource'} eq 'yes') && (! ($mfilename{$name} =~ /^contents$/i))) {
+         print HFILE "<h2 $var{'codeheader'}>Listing of $mtype{$name} $mfilename{$name}</h2>\n";
+         seek(MFILE,0,0);
+         print HFILE "<pre>\n";
+         $IsStillHelp = 2;
+         print HFILE $functionline;    # functionline from scanning of help
+         while (<MFILE>) {
+            if ($IsStillHelp == 2) {
+               next     if (/^\s*$/);
+               next     if (/^\s*function/);
+               if (/^\s*%/) { $IsStillHelp = 1; next; }
+            } elsif ($IsStillHelp == 1) {
+               next     if (/^\s*%/);
+               $IsStillHelp = 0;
+            }
+            
+            # Substritute special characters
+            $_ = &SubstituteHTMLEntities($_);
+            
+            # check for comment in line and format with css em
+            s/^(.*)%(.*?)([\s\r\n]+)$/$1<em class=\"mcom\">%$2<\/em>$3/;
+
+            # check/create cross references
+            foreach $funname (@{'all'.$name}) {
+               next if $funname eq $name;
+               $_ = &SubstituteName2Link($_, $funname);
+            }
+            print HFILE $_;
+         }
+         print HFILE "</pre>\n$var{'codehr'}\n";
+      }
+
+      # Include info about author from authorfile
+      &WriteFile2Handle($var{'authorfile'}, HFILE)   ;
+
+      print HFILE "<!--navigate-->\n";
+      print HFILE "<!--copyright-->\n";
+      print HFILE "</div>\n</body>\n</html>\n";
+      close(MFILE);
+      close(HFILE);
+
+      # Print name of finished file
+      if ($opt_silent) { print "\r"; }
+      print "   HTML-File created: $hfile{$name}\t";
+      if (!$opt_silent) { print "\n"; }
+      $filescreated++;
+   }
+
+   print "\n$PROGRAM: $indexcreated index and $filescreated files created.\n"; 
+}
+
+#========================================================================
+# Function:	CheckFileName
+# Purpose:	.
+#========================================================================
+sub CheckFileName {
+   local($filename, $description) = @_;
+   local(*CHECKFILE);
+   
+   open(CHECKFILE,"<$filename") || do {
+      if ($description eq '') {$description = 'file';}
+      # if (!$opt_silent) { print "Cannot open $description $filename: $!\n"; }
+      print "Cannot open $description $filename: $!\n";
+      return 1;
+   };
+   close(CHECKFILE);
+   return 0;
+
+}
+
+#========================================================================
+# Function:	CheckDirName
+# Purpose:	.
+#========================================================================
+sub CheckDirName {
+   local($dirname, $description) = @_;
+   local(*CHECKDIR);
+   
+   opendir(CHECKDIR,"$dirname") || die ("Cannot open $description directory $dirname: $!\n");
+   closedir(CHECKDIR);
+}
+
+#========================================================================
+# Function:	WriteFile2Handle
+# Purpose:	.
+#========================================================================
+sub WriteFile2Handle {
+   local($filename, *WRITEFILE) = @_;
+   local(*READFILE);
+   
+   if ($filename ne '') {
+      open(READFILE,"<$filename");  
+      @filecontents = <READFILE>;
+      close(READFILE);
+      print WRITEFILE "@filecontents\n";
+      # if (!$opt_silent) {print "      Contents of $filename added\n"};
+   }
+}
+
+
+#========================================================================
+# Function:	GetConfigFile
+# Purpose:	Read user's configuration file, if such exists.
+#========================================================================
+sub GetConfigFile
+{
+   local($filename) = @_;
+   local(*CONFIG);
+   local($value);
+
+   if (&CheckFileName($filename, 'configuration file')) {
+      # if (!$opt_silent) { print "   Proceeding using built-in defaults for configuration.\n"; }
+      print "   Proceeding using built-in defaults for configuration.\n";
+      return 0;
+   };
+
+   open(CONFIG,"< $filename");
+   while (<CONFIG>) {
+      s/#.*$//;
+      next if /^\s*$/o;
+
+      # match keyword: process one or more arguments
+      # keyword set
+      if (/^\s*set\s+(\S+)\s*=\s*(.*)/) {
+         # setting a configuration variable
+         if (defined $var{$1}) {
+            $var{$1} = $2;
+            if ($debug > 3) { print "$1:   $var{$1}\n"; }
+         }
+         else {
+            print "$PROGRAM: unknown variable `$1' in configuration file\n"
+         }
+      } else {
+         chop($_);
+         print "$PROGRAM: unknown keyword in configuration file in line: `$_'\n"
+      }
+   }
+   close CONFIG;
+   1;
+}
+
+
+#------------------------------------------------------------------------
+# DisplayHelp - display help text using -h or -help command-line switch
+#------------------------------------------------------------------------
+sub DisplayHelp
+{
+   $help=<<EofHelp;
+   $PROGRAM v$VERSION - generate html documentation from Matlab m-files
+
+   Usage: $PROGRAM [-h] [-c config_file] [-m|dirmfiles matlab_dir] [-d|dirhtml html_dir]
+                   [-i yes|no] [-r yes|no] [-p yes|no] [-quiet|q] [-a authorfile]
+
+   $PROGRAM is a perl script that reads each matlab .m file in a directory
+   to produce a corresponding .html file of help documentation and cross
+   reference information. An index file is written with links to all of 
+   the html files produced. The options are:
+
+      -quiet         or -q : be silent, no status information during generation
+      -help          or -h : display this help message
+      -todo          or -t : print the todo list for $PROGRAM
+      -version       or -v : display version
+
+      -configfile    or -c : name of configuration file (default to $var{'configfile'}).
+      -dirmfiles     or -m : top level directory containing matlab files to generate html for;
+                             default to actual directory.
+      -dirhtml       or -d : top level directory for generated html files;
+                             default to actual directory.
+
+      -includesource or -i : Include matlab source in the html documentation [yes|no]
+                             default to yes.
+      -processtree   or -r : create docu for m-file directory and all subdirectories [yes|no];
+                             default to yes.
+      -producetree   or -p : create multi-level docu identical to directory structure
+                             of m-files [yes|no]; default to yes.
+      -writecontentsm or -w: update or write contents.m files into the matlab source
+                             directories [yes|no]; default to no.
+
+      -authorfile    or -a : name of file including author information, last element in html;
+                             default to empty.
+      
+   The command line setting overwrite all other settings (built-in and configuration file).
+   The configuration file settings overwrite the built-in settings (and not the command
+   line settings).
+
+   Typical usages are:
+     $PROGRAM   
+        (use default parameters from perl script, if configuration 
+         file is found -> generation of docu, else display of help)
+
+     $PROGRAM -dirmfiles matlab -dirhtml html
+        (generate html documentation for all m-files in directory matlab,
+         place html files in directory html, use built-in defaults for
+         all other parameters, this way all m-files in the directory
+         matlab and below are converted and the generated html-files are
+         placed in the directory html and below producing the same 
+         directory structure than below matlab)
+
+     $PROGRAM -quiet
+        (use built-in parameters from perl script, if configuration 
+         file is found use these settings as well, do generation,
+         no display except critical errors, status of conversion and result)
+         
+     $PROGRAM -m toolbox -dirhtml doc/html -r yes -p no
+        (convert all m-files in directory toolbox and below and place
+         the generated html files in directory doc/html, read all m-files
+         recursively, however, the generated html files are placed in one
+         directory)
+
+     $PROGRAM -m toolbox -dirhtml doc/html -i no -r no
+        (convert all m-files in directory toolbox and place
+         the generated html files in directory doc/html, do not read m-files
+         recursively, do not include source code in documentation)
+
+EofHelp
+
+   die "$help";
+}
+
+#------------------------------------------------------------------------
+# DisplayTodo - display ToDo list using -t or -todo command-line switch
+#------------------------------------------------------------------------
+sub DisplayTodo
+{
+   $todo=<<EofToDo;
+      $PROGRAM v$VERSION - ToDo list
+
+       o	use more than one high level directory
+       
+       o	what should/could be done here???
+
+EofToDo
+
+   die "$todo";
+}
+
+
+#------------------------------------------------------------------------
+# ListVariables - list all defined variables and their values
+#------------------------------------------------------------------------
+sub ListVariables
+{
+   local($value);
+   
+   if ($debug > 0) {
+      print "List of all variables and their values\n";
+      foreach (sort keys %var)
+      {
+         if ($var{$_} eq '') {
+            $value = "empty";
+         } else {
+            $value = $var{$_};
+         }
+         print "   $_\n      $value\n";
+      }
+      print "\n\n";
+   }
+}
+
+
+__END__
+:endofperl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/reposman-soundsoftware.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,369 @@
+#!/usr/bin/env ruby
+
+# == Synopsis
+#
+# reposman: manages your repositories with Redmine
+#
+# == Usage
+#
+#    reposman [OPTIONS...] -s [DIR] -r [HOST]
+#
+#  Examples:
+#    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, --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 (you can use the
+#                             --key-file option as an alternative)
+#
+# == Options
+#
+#   -o, --owner=OWNER         owner of the repository. using the rails login
+#                             allow user to browse the repository within
+#                             Redmine even for private project. If you want to
+#                             share repositories through Redmine.pm, you need
+#                             to use the apache owner.
+#   -g, --group=GROUP         group of the repository. (default: root)
+#   --scm=SCM                 the kind of SCM repository you want to create (and
+#                             register) in Redmine (default: Subversion).
+#                             reposman is able to create Git and Subversion
+#                             repositories. For all other kind, you must specify
+#                             a --command option
+#   -u, --url=URL             the base url Redmine will use to access your
+#                             repositories. This option is used to automatically
+#                             register the repositories in Redmine. The project
+#                             identifier will be appended to this url. Examples:
+#                             -u https://example.net/svn
+#                             -u file:///var/svn/
+#                             if this option isn't set, reposman will register
+#                             the repositories with local file paths in Redmine
+#   -c, --command=COMMAND     use this command instead of "svnadmin create" to
+#                             create a repository. This option can be used to
+#                             create repositories other than subversion and git
+#                             kind.
+#                             This command override the default creation for git
+#                             and subversion.
+#   --http-user=USER          User for HTTP Basic authentication with Redmine WS
+#   --http-pass=PASSWORD      Password for Basic authentication with Redmine WS
+#       --key-file=PATH       path to a file that contains the Redmine API key
+#                             (use this option instead of --key if you don't 
+#                             the key to appear in the command line)
+#   -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
+#
+# == References
+#
+# You can find more information on the redmine's wiki : http://www.redmine.org/wiki/redmine/HowTos
+
+
+require 'getoptlong'
+#require 'rdoc/usage'
+require 'find'
+require 'etc'
+
+Version = "1.3"
+SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem )
+
+opts = GetoptLong.new(
+                      ['--scm-dir',      '-s', GetoptLong::REQUIRED_ARGUMENT],
+                      ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
+                      ['--key',          '-k', GetoptLong::REQUIRED_ARGUMENT],
+                      ['--key-file',           GetoptLong::REQUIRED_ARGUMENT],
+                      ['--owner',        '-o', GetoptLong::REQUIRED_ARGUMENT],
+                      ['--group',        '-g', GetoptLong::REQUIRED_ARGUMENT],
+                      ['--url',          '-u', GetoptLong::REQUIRED_ARGUMENT],
+                      ['--command' ,     '-c', GetoptLong::REQUIRED_ARGUMENT],
+                      ['--scm',                GetoptLong::REQUIRED_ARGUMENT],
+                      ['--http-user',          GetoptLong::REQUIRED_ARGUMENT],
+                      ['--http-pass',          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    = ''
+$svn_owner    = 'root'
+$svn_group    = 'root'
+$use_groupid  = true
+$svn_url      = false
+$test         = false
+$scm          = 'Subversion'
+
+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
+
+def usage
+  puts "See source code for supported options"
+  exit
+end
+
+module SCM
+
+  module Subversion
+    def self.create(path)
+      system_or_raise "svnadmin create #{path}"
+    end
+  end
+
+  module Git
+    def self.create(path)
+      Dir.mkdir path
+      Dir.chdir(path) do
+        system_or_raise "git --bare init --shared"
+        system_or_raise "git update-server-info"
+      end
+    end
+  end
+
+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 '--key-file'
+      begin
+        $api_key = File.read(arg).strip
+      rescue Exception => e
+        $stderr.puts "Unable to read the key from #{arg}: #{e.message}"
+        exit 1
+      end
+    when '--owner';          $svn_owner    = arg.dup; $use_groupid = false;
+    when '--group';          $svn_group    = arg.dup; $use_groupid = false;
+    when '--url';            $svn_url      = arg.dup
+    when '--scm';            $scm          = arg.dup.capitalize; log("Invalid SCM: #{$scm}", :exit => true) unless SUPPORTED_SCM.include?($scm)
+    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';           usage
+    when '--quiet';          $quiet = true
+    end
+  end
+rescue
+  exit 1
+end
+
+if $test
+  log("running in test mode")
+end
+
+# Make sure command is overridden if SCM vendor is not handled internally (for the moment Subversion and Git)
+if $command.nil?
+  begin
+    scm_module = SCM.const_get($scm)
+  rescue
+    log("Please use --command option to specify how to create a #{$scm} repository.", :exit => true)
+  end
+end
+
+$svn_url += "/" if $svn_url and not $svn_url.match(/\/$/)
+
+if ($redmine_host.empty? or $repos_base.empty?)
+  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 repository manager/#{Version}"
+  self.format = :xml
+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 ActiveResource::ForbiddenAccess
+  log("Request was denied by your Redmine server. Make sure that 'WS for repository management' is enabled in application settings and that you provided the correct 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("found #{projects.size} projects at " + Time.now.inspect);
+
+def set_owner_and_rights(project, repos_path, &block)
+  if mswin?
+    yield if block_given?
+  else
+    uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project.identifier).gid : Etc.getgrnam($svn_group).gid)
+    right = project.is_public ? 02775 : 02770
+    yield if block_given?
+    Find.find(repos_path) do |f|
+      File.chmod right, f
+      File.chown uid, gid, f
+    end
+  end
+end
+
+def other_read_right?(file)
+  (File.stat(file).mode & 0007).zero? ? false : true
+end
+
+def owner_name(file)
+  mswin? ?
+    $svn_owner :
+    Etc.getpwuid( File.stat(file).uid ).name
+end
+
+def mswin?
+  (RUBY_PLATFORM =~ /(:?mswin|mingw)/) || (RUBY_PLATFORM == 'java' && (ENV['OS'] || ENV['os']) =~ /windows/i)
+end
+
+projects.each do |project|
+  log("inspecting 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
+
+  repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
+
+  create_repos = false
+  # Logic required for SoundSoftware.ac.uk repositories:
+  #
+  # * If the project has a repository path declared already,
+  #   - if it's a local path,
+  #     - if it does not exist
+  #       - if it has the right root
+  #         - create it
+  #   - else
+  #     - leave alone (remote repository)
+  # * else
+  #   - create repository with same name as project
+  #   - set to project
+
+  if project.respond_to?(:repository)
+
+    repos_url = project.repository.url;
+    log("\texisting url for project #{project.identifier} is #{repos_url}", :level => 2);
+
+    if repos_url.match(/^file:\//) || repos_url.match(/^\//)
+
+      repos_url = repos_url.gsub(/^file:\/*/, "/");
+      log("\tthis is a local file path, at #{repos_url}", :level => 2);
+
+      if repos_url.slice(0, $repos_base.length) != $repos_base
+        # leave repos_path set to our original suggestion
+        log("\tpreparing to replace incorrect repo location #{repos_url} for #{project.name} with #{repos_path}");
+        create_repos = true
+      else
+        if !File.directory?(repos_url)
+          log("\tpreparing to create repo for #{project.name} at #{repos_url}");
+          repos_path = repos_url
+          create_repos = true
+        else
+          log("\tit exists and is in the right place", :level => 2);
+        end
+      end
+    else
+      log("\tthis is a remote path, leaving alone", :level => 2);
+    end
+  else
+    log("\tpreparing to set repo location and create for #{project.name} at #{repos_url}")
+#    if File.directory?(repos_path)
+#      log("\trepository path #{repos_path} already exists, not creating")
+#    else 
+      create_repos = true
+#    end
+  end
+
+  if create_repos
+
+    registration_url = repos_path
+    if $svn_url
+      registration_url = "#{$svn_url}#{project.identifier}"
+    end
+
+    if $test
+      log("\tproposal: create repository #{repos_path}")
+      log("\tproposal: register repository #{repos_path} in Redmine with vendor #{$scm}, url #{registration_url}")
+      next
+    end
+
+# No -- we need "other" users to be able to read it.  Access control
+# is not handled through Unix user id anyway
+#    project.is_public ? File.umask(0002) : File.umask(0007)
+    File.umask(0002)
+
+    log("\taction: create repository #{repos_path}")
+
+    begin
+      if !File.directory?(repos_path)
+        set_owner_and_rights(project, repos_path) do
+          if scm_module.nil?
+            log("\trunning command: #{$command} #{repos_path}")
+            system_or_raise "#{$command} #{repos_path}"
+          else
+            scm_module.create(repos_path)
+          end
+        end
+      end
+    rescue => e
+      log("\tunable to create #{repos_path} : #{e}\n")
+      next
+    end
+
+    begin
+      log("\taction: register repository #{repos_path} in Redmine with vendor #{$scm}, url #{registration_url}");
+      project.post(:repository, :vendor => $scm, :repository => {:url => "#{registration_url}"}, :key => $api_key)
+    rescue => e
+      log("\trepository #{repos_path} not registered in Redmine: #{e.message}");
+    end
+    log("\trepository #{repos_path} created");
+  end
+end
+
+log("project review completed at " + Time.now.inspect);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/update-external-repo.sh	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,132 @@
+#!/bin/sh
+
+mirrordir="/var/mirror"
+hg="/usr/bin/hg"
+
+project="$1"
+local_repo="$2"
+remote_repo="$3"
+
+if [ -z "$project" ] || [ -z "$local_repo" ] || [ -z "$remote_repo" ]; then
+    echo "Usage: $0 <project> <local-repo-path> <remote-repo-url>"
+    exit 2
+fi
+
+  # We need to handle different source repository types separately.
+  # 
+  # The convert extension cannot convert directly from a remote git
+  # repo; we'd have to mirror to a local repo first.  Incremental
+  # conversions do work though.  The hg-git plugin will convert
+  # directly from remote repositories, but not via all schemes
+  # (e.g. https is not currently supported).  It's probably easier to
+  # use git itself to clone locally and then convert or hg-git from
+  # there.
+  # 
+  # We can of course convert directly from remote Subversion repos,
+  # but we need to keep track of that -- you can ask to convert into a
+  # repo that has already been used (for Mercurial) and it'll do so
+  # happily; we don't want that.
+  #
+  # Converting from a remote Hg repo should be fine!
+  #
+  # One other thing -- we can't actually tell the difference between
+  # the various SCM types based on URL alone.  We have to try them
+  # (ideally in an order determined by a guess based on the URL) and
+  # see what happens.
+
+project_mirror="$mirrordir/$project"
+mkdir -p "$project_mirror"
+project_repo_mirror="$project_mirror/repo"
+
+  # Some test URLs:
+  # 
+  # http://aimc.googlecode.com/svn/trunk/
+  # http://aimc.googlecode.com/svn/
+  # http://vagar.org/git/flam
+  # https://github.com/wslihgt/IMMF0salience.git
+  # http://hg.breakfastquay.com/dssi-vst/
+  # git://github.com/schacon/hg-git.git
+  # http://svn.drobilla.net/lad (externals!)
+
+# If we are importing from another distributed system, then our aim is
+# to create either a Hg repo or a git repo at $project_mirror, which
+# we can then pull from directly to the Hg repo at $local_repo (using
+# hg-git, in the case of a git repo).
+
+# Importing from SVN, we should use hg convert directly to the target
+# hg repo (or should we?) but keep a record of the last changeset ID
+# we brought in, and test each time whether it matches the last
+# changeset ID actually in the repo
+
+success=""
+
+# If we have a record of the last successfully updated remote repo
+# URL, check it against our current remote URL: if it has changed, we
+# will need to start again with a new clone rather than pulling
+# updates into the existing local mirror
+
+successfile="$project_mirror/last_successful_url"
+if [ -f "$successfile" ]; then
+    last=$(cat "$successfile")
+    if [ x"$last" = x"$remote_repo" ]; then
+	echo "$$: Remote URL is unchanged from last successful update"
+    else
+	echo "$$: Remote URL has changed since last successful update:"
+	echo "$$: Last URL was $last, current is $remote_repo"
+	suffix="$$.$(date +%s)"
+	echo "$$: Moving existing repos to $suffix suffix and starting afresh"
+	mv "$project_repo_mirror" "$project_repo_mirror"."$suffix"
+	mv "$local_repo" "$local_repo"."$suffix"
+	mv "$successfile" "$successfile"."$suffix"
+	touch "$project_mirror/url_changed"
+    fi
+fi
+
+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
+	if [ -z "$success" ]; then
+	    ( cd "$project_repo_mirror" && "$hg" pull "$remote_repo" ) && success=true
+	fi
+    elif [ -d "$project_repo_mirror/.git" ]; then
+	( cd "$project_repo_mirror" && git pull "$remote_repo" master ) && 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 "$local_repo" ]; then
+	"$hg" init "$local_repo"
+    fi
+    if [ -d "$project_repo_mirror/.git" ]; then
+	( cd "$local_repo" && "$hg" --config extensions.hggit= pull "$project_repo_mirror" ) && echo "$remote_repo" > "$successfile"
+    else 
+	( cd "$local_repo" && "$hg" pull "$project_repo_mirror" ) && echo "$remote_repo" > "$successfile"
+    fi
+fi
--- a/files/delete.me	Tue Sep 09 09:29:00 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-default directory for uploaded files
\ No newline at end of file
--- a/lib/redmine.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/lib/redmine.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -80,10 +80,10 @@
 
 # Permissions
 Redmine::AccessControl.map do |map|
-  map.permission :view_project, {:projects => [:show], :activities => [:index]}, :public => true, :read => true
+  map.permission :view_project, {:projects => [:show], :activities => [:index], :members => [:index]}, :public => true, :read => true
   map.permission :search_project, {:search => :index}, :public => true, :read => true
   map.permission :add_project, {:projects => [:new, :create]}, :require => :loggedin
-  map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member
+  map.permission :edit_project, {:projects => [:settings, :edit, :update, :overview]}, :require => :member
   map.permission :close_project, {:projects => [:close, :reopen]}, :require => :member, :read => true
   map.permission :select_project_modules, {:projects => :modules}, :require => :member
   map.permission :view_members, {:members => [:index, :show]}, :public => true, :read => true
@@ -193,7 +193,7 @@
 Redmine::MenuManager.map :top_menu do |menu|
   menu.push :home, :home_path
   menu.push :my_page, { :controller => 'my', :action => 'page' }, :if => Proc.new { User.current.logged? }
-  menu.push :projects, { :controller => 'projects', :action => 'index' }, :caption => :label_project_plural
+  menu.push :projects, { :controller => 'projects', :action => 'explore' }, :caption => :label_project_plural
   menu.push :administration, { :controller => 'admin', :action => 'index' }, :if => Proc.new { User.current.admin? }, :last => true
   menu.push :help, Redmine::Info.help_url, :last => true
 end
@@ -230,6 +230,8 @@
 
 Redmine::MenuManager.map :project_menu do |menu|
   menu.push :overview, { :controller => 'projects', :action => 'show' }
+  menu.push :members, { :controller => 'members', :action => 'index' }, :param => :project_id
+  menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
   menu.push :activity, { :controller => 'activities', :action => 'index' }
   menu.push :roadmap, { :controller => 'versions', :action => 'index' }, :param => :project_id,
               :if => Proc.new { |p| p.shared_versions.any? }
@@ -238,7 +240,6 @@
               :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) }
   menu.push :gantt, { :controller => 'gantts', :action => 'show' }, :param => :project_id, :caption => :label_gantt
   menu.push :calendar, { :controller => 'calendars', :action => 'show' }, :param => :project_id, :caption => :label_calendar
-  menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
   menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
   menu.push :wiki, { :controller => 'wiki', :action => 'show', :id => nil }, :param => :project_id,
               :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
--- a/lib/redmine/info.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/lib/redmine/info.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -3,7 +3,7 @@
     class << self
       def app_name; 'Redmine' end
       def url; 'http://www.redmine.org/' end
-      def help_url; 'http://www.redmine.org/guide' end
+      def help_url; '/projects/soundsoftware-site/wiki/Help' end
       def versioned_name; "#{app_name} #{Redmine::VERSION}" end
 
       def environment
--- a/lib/redmine/scm/adapters/abstract_adapter.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/lib/redmine/scm/adapters/abstract_adapter.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -287,7 +287,12 @@
 
         def scm_iconv(to, from, str)
           return nil if str.nil?
-          return str if to == from
+          # bug 446: non-utf8 paths in repositories blow up repo viewer and reposman
+          # -- Remove this short-circuit: we want the conversion to
+          #    happen always, so we can trap the error here if the
+          #    source text happens not to be in the advertised
+          #    encoding (instead of having the database blow up later)
+#          return str if to == from
           if str.respond_to?(:force_encoding)
             str.force_encoding(from)
             begin
--- a/lib/redmine/scm/adapters/bazaar_adapter.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/lib/redmine/scm/adapters/bazaar_adapter.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -15,7 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/abstract_adapter'
+require_dependency 'redmine/scm/adapters/abstract_adapter'
 
 module Redmine
   module Scm
--- a/lib/redmine/scm/adapters/cvs_adapter.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/lib/redmine/scm/adapters/cvs_adapter.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -15,7 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/abstract_adapter'
+require_dependency 'redmine/scm/adapters/abstract_adapter'
 
 module Redmine
   module Scm
--- a/lib/redmine/scm/adapters/darcs_adapter.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/lib/redmine/scm/adapters/darcs_adapter.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -15,7 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/abstract_adapter'
+require_dependency 'redmine/scm/adapters/abstract_adapter'
 require 'rexml/document'
 
 module Redmine
--- a/lib/redmine/scm/adapters/filesystem_adapter.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/lib/redmine/scm/adapters/filesystem_adapter.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -18,7 +18,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/abstract_adapter'
+require_dependency 'redmine/scm/adapters/abstract_adapter'
 require 'find'
 
 module Redmine
--- a/lib/redmine/scm/adapters/git_adapter.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/lib/redmine/scm/adapters/git_adapter.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -15,7 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/abstract_adapter'
+require_dependency 'redmine/scm/adapters/abstract_adapter'
 
 module Redmine
   module Scm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/redmine/scm/adapters/mercurial/hg-template-0.9.5.tmpl	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,12 @@
+changeset = 'This template must be used with --debug option\n'
+changeset_quiet =  'This template must be used with --debug option\n'
+changeset_verbose = 'This template must be used with --debug option\n'
+changeset_debug = '<logentry revision="{rev}" node="{node|short}">\n<author>{author|escape}</author>\n<date>{date|isodate}</date>\n<paths>\n{files}{file_adds}{file_dels}{file_copies}</paths>\n<msg>{desc|escape}</msg>\n{tags}</logentry>\n\n'
+
+file = '<path action="M">{file|urlescape}</path>\n'
+file_add = '<path action="A">{file_add|urlescape}</path>\n'
+file_del = '<path action="D">{file_del|urlescape}</path>\n'
+file_copy = '<path-copied copyfrom-path="{source|urlescape}">{name|urlescape}</path-copied>\n'
+tag = '<tag>{tag|escape}</tag>\n'
+header='<?xml version="1.0" encoding="UTF-8" ?>\n<log>\n\n'
+footer='</log>'
--- a/lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl	Tue Sep 09 09:29:00 2014 +0100
+++ b/lib/redmine/scm/adapters/mercurial/hg-template-1.0.tmpl	Tue Sep 09 10:02:18 2014 +0100
@@ -9,4 +9,4 @@
 file_copy = '<path-copied copyfrom-path="{source|urlescape}">{name|urlescape}</path-copied>\n'
 parent = '<parent>{node}</parent>\n'
 header='<?xml version="1.0" encoding="UTF-8" ?>\n<log>\n\n'
-# footer="</log>"
+footer='</log>'
--- a/lib/redmine/scm/adapters/mercurial_adapter.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/lib/redmine/scm/adapters/mercurial_adapter.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -15,7 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/abstract_adapter'
+require_dependency 'redmine/scm/adapters/abstract_adapter'
 require 'cgi'
 
 module Redmine
@@ -198,8 +198,7 @@
               output.force_encoding('UTF-8')
             end
             begin
-              # Mercurial < 1.5 does not support footer template for '</log>'
-              parse_xml("#{output}</log>")['log']
+              parse_xml("#{output}")['log']
             rescue
             end
           end
--- a/lib/redmine/scm/adapters/subversion_adapter.rb	Tue Sep 09 09:29:00 2014 +0100
+++ b/lib/redmine/scm/adapters/subversion_adapter.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -15,7 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-require 'redmine/scm/adapters/abstract_adapter'
+require_dependency 'redmine/scm/adapters/abstract_adapter'
 require 'uri'
 
 module Redmine
--- a/log/delete.me	Tue Sep 09 09:29:00 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-default directory for uploaded files
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/engines/generators/plugin_migration/templates/plugin_migration.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,13 @@
+class <%= class_name %> < ActiveRecord::Migration
+  def self.up
+  <%- plugins.each do |plugin| -%>
+    Engines.plugins["<%= plugin.name %>"].migrate(<%= new_versions[plugin.name] %>)
+  <%- end -%>
+  end
+
+  def self.down
+  <%- plugins.each do |plugin| -%>
+    Engines.plugins["<%= plugin.name %>"].migrate(<%= current_versions[plugin.name] %>)
+  <%- end -%>
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/Gemfile	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,3 @@
+gem 'bibtex-ruby'
+gem 'nokogiri'
+gem 'citeproc-ruby'
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/README.rdoc	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,3 @@
+= bibliography
+
+Description goes here
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/controllers/authors_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,13 @@
+class AuthorsController < ApplicationController
+  helper :publications
+  include PublicationsHelper
+  
+  def index
+    @authors = Author.find(:all)
+  end
+
+  def show
+    @author = Author.find(params[:id])
+  end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/controllers/authorships_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,15 @@
+class AuthorshipsController < ApplicationController
+
+    def sort
+        @authorships = Authorship.find(params['authorship'])
+
+        @authorships.each do |authorship|
+
+            # note: auth_order is usually called position (default column name in the acts_as_list plugin )
+            authorship.auth_order = params['authorship'].index(authorship.id.to_s) + 1
+            authorship.save
+        end
+
+        render :nothing => true, :status => 200
+    end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/controllers/bibtex_entries_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,3 @@
+class BibtexEntriesController < ApplicationController
+
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/controllers/publications_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,280 @@
+# -*- coding: utf-8 -*-
+# vendor/plugins/redmine_bibliography/app/controllers/publications_controller.rb
+
+class PublicationsController < ApplicationController
+  unloadable
+
+  model_object Publication
+  before_filter :find_model_object, :only => [ :show, :add_project ]
+  before_filter :find_project_by_project_id, :authorize, :only => [ :edit, :new, :update, :create ]
+
+  def new
+    find_project_by_project_id
+    @publication = Publication.new
+
+    # we'll always want a new publication to have its bibtex entry
+    @publication.build_bibtex_entry
+
+  end
+
+  def create
+    @project = Project.find(params[:project_id])
+
+    @publication = Publication.new(params[:publication])
+    @publication.projects << @project unless @project.nil?
+
+    if @publication.save
+      @publication.notify_authors_publication_added(@project)
+
+      flash[:notice] = "Successfully created publication."
+      redirect_to :action => :show, :id => @publication, :project_id => @project
+    else
+      render :action => 'new', :project_id => @project
+    end
+  end
+
+  def index
+    if !params[:project_id].nil?
+      find_project_by_project_id
+      @project = Project.find(params[:project_id])
+      @publications = Publication.find :all, :joins => :projects, :conditions => ["project_id = ?", @project.id]
+    else
+      @publications = Publication.find :all
+    end
+  end
+
+  def new_from_bibfile
+    @publication.current_step = session[:publication_step]
+
+    # contents of the paste text area
+    bibtex_entry = params[:bibtex_entry]
+
+    # method for creating "pasted" bibtex entries
+    if bibtex_entry
+      parse_bibtex_list bibtex_entry
+    end
+  end
+
+  def show_bibtex_fields
+    @fields = []
+
+    unless params[:value].empty?
+      @fields = BibtexEntryType.fields(params[:value])
+    end
+
+    respond_to do |format|
+      format.js {
+        render :show_bibtex_fields
+      }
+    end
+  end
+
+  def add_author
+    if (request.xhr?)
+      render :text => User.find(params[:user_id]).name
+    else
+      # No?  Then render an action.
+      #render :action => 'view_attribute', :attr => @name
+      logger.error { "Error while adding Author to publication." }
+    end
+  end
+
+  def edit
+    find_project_by_project_id unless params[:project_id].nil?
+
+    @publication = Publication.find(params[:id])
+    @selected_bibtex_entry_type_id = @publication.bibtex_entry.entry_type
+    @bibtype_fields = BibtexEntryType.fields(@selected_bibtex_entry_type_id)
+  end
+
+  def update
+    @publication = Publication.find(params[:id])
+
+    if @publication.update_attributes(params[:publication])
+      flash[:notice] = "Successfully updated Publication."
+
+      # expires the previosly cached entries
+      Rails.cache.delete "publication-#{@publication.id}-ieee"
+      Rails.cache.delete "publication-#{@publication.id}-bibtex"
+
+      if !params[:project_id].nil?
+        redirect_to :action => :show, :id => @publication, :project_id => params[:project_id]
+      else
+        redirect_to :action => :show, :id => @publication
+      end
+    else
+      render :action => 'edit'
+    end
+  end
+
+
+  def show
+    find_project_by_project_id unless params[:project_id].nil?
+
+    if @publication.nil?
+      @publications = Publication.all
+      render "index", :alert => 'The publication was not found!'
+    else
+      @authors = @publication.authors
+      @bibtext_entry = @publication.bibtex_entry
+    end
+  end
+
+  # parse string with bibtex authors
+  def parse_authors(authors_entry)
+    # in bibtex the authors are always seperated by "and"
+    return authors_entry.split(" and ")
+  end
+
+  # parses a list of bibtex
+  def parse_bibtex_list(bibtex_list)
+    bibliography = BibTeX.parse bibtex_list
+
+    no_entries = bibliography.data.length
+
+    # parses the bibtex entries
+    bibliography.data.map do |d|
+
+      if d.class == BibTeX::Entry
+        create_bibtex_entry d
+      end
+    end
+  end
+
+  def create_bibtex_entry(d)
+    @publication = Publication.new
+    @bentry = BibtexEntry.new
+    authors = []
+    institution = ""
+    email = ""
+
+    d.fields.keys.map do |field|
+      case field.to_s
+      when "author"
+        authors = parse_authors d[field]
+      when "title"
+        @publication.title = d[field]
+      when "institution"
+        institution = d[field]
+      when "email"
+        email = d[field]
+      else
+        @bentry[field] = d[field]
+      end
+    end
+
+    @publication.bibtex_entry = @bentry
+    @publication.save
+
+    # need to save all authors
+    #   and establish the author-publication association
+    #   via the authorships table
+    authors.each_with_index.map do |authorname, idx|
+      author = Author.new(:name => authorname)
+      if author.save!
+        # todo: catch the errors...
+        puts "SAVED"
+      else
+        puts "NOT SAVED"
+      end
+
+      author.authorships.create!(
+      :publication => @publication,
+      :institution => institution,
+      :email => email,
+      :order => idx)
+    end
+  end
+
+  def autocomplete_for_project
+    @publication = Publication.find(params[:id])
+
+    @projects = Project.active.name_or_homepage_like(params[:q]).find(:all, :limit => 100) - @publication.projects
+    logger.debug "Query for \"#{params[:q]}\" returned \"#{@projects.size}\" results"
+    render :layout => false
+  end
+
+  def autocomplete_for_author
+    @results = []
+
+    object_id = params[:object_id]
+    @object_name = "publications[authorships_attributes][#{object_id}][search_results]"
+
+    # todo: make sure query works with both pgres and mysql ~lf.20131010
+    authors_list = Author.joins(:authorships).where("LOWER(authorships.name_on_paper) LIKE LOWER(?)", "%#{params[:term]}%").uniq
+
+    # name_like scope, defined in lib/user_author patch
+    users_list = User.active.name_like(params[:term]).find(:all, :limit => 100)
+
+    logger.debug "Query for \"#{params[:term]}\" returned \"#{authors_list.size}\" authors and \"#{users_list.size}\" users"
+
+    # will check if any of the members of the users list
+    #  doesn't belong to the authors list
+
+    @results = authors_list
+
+    users_list.each do |user|
+      @results << user unless authors_list.include?(user.author)
+    end
+
+    logger.debug { "Autocomplete_for_author results --> #{@results}" }
+
+    render :layout => false
+  end
+
+  def sort_author_order
+    params[:authorships].each_with_index do |id, index|
+      Authorship.update_all(['auth_order=?', index+1], ['id=?', id])
+    end
+    render :nothing => true
+  end
+
+  def add_project
+    @projects = Project.find(params[:publication][:project_ids])
+    @publication.projects << @projects
+    @project = Project.find(params[:project_id])
+
+    # TODO luisf should also respond to HTML???
+    respond_to do |format|
+      format.html { redirect_to :back }
+      format.js {
+        render(:update) {|page|
+          page[:add_project_form].reset
+          page.replace_html :list_projects, :partial => 'list_projects'
+        }
+      }
+    end
+  end
+
+  def remove_project
+    @project = Project.find(params[:project_id])
+    proj = Project.find(params[:remove_project_id])
+
+    if @publication.projects.length > 1
+      if @publication.projects.exists? proj
+        @publication.projects.delete proj if request.post?
+      end
+    else
+      logger.error { "Cannot remove project from publication list" }
+    end
+
+    logger.error { "CURRENT project name#{proj.name} and wanna delete #{@project.name}" }
+
+    render(:update) {|page|
+      page.replace_html "list_projects", :partial => 'list_projects', :id  => @publication
+    }
+  end
+
+  def destroy
+    find_project_by_project_id unless params[:project_id].nil?
+    @publication = Publication.find(params[:id])
+
+    @publication.destroy
+
+    flash[:notice] = "Successfully deleted Publication."
+    redirect_to :controller => :publications, :action => 'index', :project_id => @project
+  end
+
+  private
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/helpers/authors_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,28 @@
+module AuthorsHelper
+  unloadable
+  
+  def render_author_publications(author)
+    s = ""
+    pubs = []
+
+    author.publications.each do |pub|
+     pubs << link_to(pub.title, pub)
+    end
+
+    if pubs.size < 3
+      s << '<nobr>' << pubs.join(', ') << '</nobr>'
+    else
+      s << pubs.join(', ')
+    end
+    s    
+  end
+  
+  
+  # Generates a link to an author
+  #   todo: test options
+  def link_to_author(author, options={}, html_options = nil)
+    url = {:controller => 'authors', :action => 'show', :id => author}.merge(options)
+    link_to(h(author.name), url, html_options)
+  end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/helpers/authorships_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+module AuthorshipsHelper
+
+  # Generates a link to either author or user, depending on which is
+  # available
+  def link_to_authorship(authorship)
+    s = ''
+    if authorship.author.nil?
+      # legacy reasons…
+      s << h(authorship.name_on_paper)
+    else
+      if authorship.author.user.nil?
+        s << link_to(authorship.name_on_paper, :controller => 'authors', :action => 'show', :id => authorship.author)
+      else
+        s << link_to(authorship.name_on_paper, :controller => 'users', :action => 'show', :id => authorship.author.user)
+      end
+    end
+    s.html_safe
+  end
+
+end
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/helpers/bibtex_entries_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,3 @@
+module BibtexEntriesHelper
+
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/helpers/publications_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+require 'bibtex'
+
+module PublicationsHelper
+  include AuthorshipsHelper
+
+  def link_to_publication(publication, options={}, html_options = nil)
+    url = {:controller => 'publications', :action => 'show', :id => publication}.merge(options)
+    link_to(h(publication.title), url, html_options)
+  end
+
+  def projects_check_box_tags(name, projects)
+    s = ''
+    projects.sort.each do |project|
+      if User.current.allowed_to?(:edit_publication, project)
+        s << "<label>#{ check_box_tag name, project.id, false } #{link_to_project project}</label>\n"
+        s << '<br />'
+      end
+    end
+
+    s.html_safe
+  end
+
+  def link_to_remove_fields(name, f)
+    f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)", :class => 'icon icon-del')
+  end
+
+  def link_to_add_author_fields(name, f, association, action)
+    new_object = f.object.class.reflect_on_association(association).klass.new
+    fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
+      # renders _authorship_fields.html.erb
+      render(association.to_s.singularize + "_fields", :f => builder)
+    end
+
+    link_to_function(name, "add_author_fields(this, '#{association}', '#{escape_javascript(fields)}', '#{action}')", { :class => 'icon icon-add', :id => "add_another_author" })
+  end
+
+  def sanitized_object_name(object_name)
+    object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/,"_").sub(/_$/,"")
+  end
+
+  def sanitized_method_name(method_name)
+    method_name.sub(/\?$/, "")
+  end
+
+  def form_tag_name(object_name, method_name)
+      str = "#{object_name.to_s}[#{sanitized_method_name(method_name.to_s)}]"
+      str.to_sym
+  end
+
+  def form_tag_id(object_name, method_name)
+    str = "#{sanitized_object_name(object_name.to_s)}_#{sanitized_method_name(method_name.to_s)}"
+    str.to_sym
+  end
+
+  def form_object_id(object_name)
+    str = object_name.split("\[").last().gsub("\]","")
+    str.to_sym
+  end
+
+  def render_authorships_list(publication)
+    s = '<p>'
+
+    publication.authorships.each do |authorship|
+      s << link_to_authorship(authorship)
+      s << "<br /><em>#{authorship.institution}</em></p>"
+    end
+
+    s.html_safe
+  end
+
+  def render_projects_list(publication, show_delete_icon)
+    s= ""
+
+    publication.projects.visible.each do |proj|
+      s << link_to_project(proj, {}, :class => 'publication_project')
+
+      if show_delete_icon
+        if User.current.allowed_to?(:edit_publication, @project)
+          if @project == proj
+            # todo: move this message to yml file
+            confirm_msg = 'Are you sure you want to remove the current project from this publication\'s projects list?'
+          else
+            confirm_msg = false
+          end
+
+          s << link_to(l(:button_delete), { :url => { :controller => 'publications', :action => 'remove_project', :id => publication, :remove_project_id => proj,  :project_id => @project }, :method => :post, :confirm => confirm_msg }, :class => 'icon icon-del', :remote => :true)
+        end
+      end
+
+      s << "<br />"
+    end
+
+    s.html_safe
+  end
+
+  def print_ieee_format(publication)
+    Rails.cache.fetch("publication-#{publication.id}-ieee") do
+      entry = publication.print_entry(:ieee)
+      if entry
+        entry.html_safe
+      end
+    end
+  end
+
+  def print_bibtex_format(publication)
+    Rails.cache.fetch("publication-#{publication.id}-bibtex") do
+      publication.print_entry(:bibtex)
+    end
+  end
+
+  def show_bibtex_fields(bibtex_entry)
+    s = ""
+    bibtex_entry.attributes.keys.sort.each do |key|
+      value = bibtex_entry.attributes[key].to_s
+      next if key == 'id' or key == 'publication_id' or value == ""
+      s << "<h4>" + l("field_#{key}") + "</h4>"
+      s << "<p>"
+      if key == "entry_type"
+        s << bibtex_entry.entry_type_label
+      else
+        s << value
+      end
+      s << "</p>"
+    end
+    s
+  end
+end
+
+
+def render_authorship_link(link_class, link_id)
+
+  # Renders a link for an author used when adding authors for a publication
+  # link_class can be either User or Author
+  # link_id will be the id of the Author/User we wish to link
+
+  s= ""
+
+  if link_class == "Author"
+    url = {:controller => 'authors', :action => 'show', :id => link_id}
+    s << link_to(h(Author.find(link_id).name), url)
+  else
+    url = {:controller => 'users', :action => 'show', :id => link_id}
+    s << link_to(h(User.find(link_id).name), url)
+  end
+
+  s.html_safe
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/models/author.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,46 @@
+class Author < ActiveRecord::Base
+  unloadable
+
+  has_many :authorships, :dependent => :destroy
+  has_many :publications, :through => :authorships
+
+  belongs_to :user
+
+  def <=>(author)
+    name.downcase <=> author.name.downcase
+  end
+
+  # todo: review usage of scope --lf.20130108
+  scope :like, lambda {|q|
+    s = "%#{q.to_s.strip.downcase}%"
+    {:conditions => ["LOWER(name) LIKE :s", {:s => s}],
+     :order => 'name'
+    }
+  }
+
+  def institution
+    if self.authorships.first.nil?
+      ""
+    else
+      self.authorships.first.institution
+    end
+  end
+
+  def mail
+    if self.authorships.first.nil?
+      ""
+    else
+      self.authorships.first.mail
+    end
+  end
+
+  # todo: need to fix the name getter
+  def name
+    if self.authorships.first.nil?
+      ""
+    else
+      self.authorships.first.name
+    end
+  end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/models/authorship.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,138 @@
+class Authorship < ActiveRecord::Base
+  unloadable
+
+  belongs_to :author
+  belongs_to :publication
+
+  accepts_nested_attributes_for :author
+  accepts_nested_attributes_for :publication
+
+  validates_presence_of :name_on_paper
+
+  attr_writer :search_author_id , :search_author_class
+  attr_writer :search_author_tie
+
+  ### attr_accessor :search_results, :identify_author
+  ## attr_writer :search_author_class
+
+  before_save :set_author
+  before_update :delete_publication_cache
+
+  # tod: review scope of ordering
+  acts_as_list :column => 'auth_order'
+
+  # todo: review usage of scope --lf.20130108
+  scope :like_unique, lambda {|q|
+    s = "%#{q.to_s.strip.downcase}%"
+    {:conditions => ["LOWER(name_on_paper) LIKE :s OR LOWER(email) LIKE :s", {:s => s}],
+     :order => 'name_on_paper',
+     :group => "name_on_paper, institution, email"
+    }
+  }
+
+  # todo: review usage of scope --lf.20130108
+  scope :like, lambda {|q|
+    s = "%#{q.to_s.strip.downcase}%"
+    {:conditions => ["LOWER(name_on_paper) LIKE :s OR LOWER(email) LIKE :s", {:s => s}],
+     :order => 'name_on_paper'
+    }
+  }
+
+  def search_author_class
+    # Authorship must always have an Author
+    # unless it hasn't been saved yet
+    # using default setter (attr_writer)
+
+    if self.author.nil?
+      aclass = ""
+    else
+      aclass = "Author"
+    end
+
+    @search_author_class || aclass
+  end
+
+  def search_author_id
+    if self.author.nil?
+      authid = ""
+    else
+      authid = author_id
+    end
+
+    @search_author_id || authid
+  end
+
+  def search_author_tie
+    if self.author.nil?
+      auth_tie = false
+    else
+      auth_tie = true
+    end
+
+    @search_author_tie || auth_tie
+  end
+
+  def name
+    return self.name_on_paper
+  end
+
+  def <=>(authorship)
+    name.downcase <=> authorship.name.downcase
+  end
+
+  def mail
+    return self.email
+  end
+
+  protected
+
+  def delete_publication_cache
+    publication = Publication.find(self.publication_id)
+    Rails.cache.delete "publication-#{publication.id}-ieee"
+    Rails.cache.delete "publication-#{publication.id}-bibtex"
+  end
+
+  private
+
+  def set_author
+    # do we want to associate the authorship
+    #  with an existing author/user?
+    if @search_author_tie
+      # if an author, simply associates with it
+      # if an user, checks if it has already an author associated with it
+      #   if so, associates with that author
+      #   otherwise, creates a new author
+
+      case @search_author_class
+      when ""
+        author = Author.new
+        author.save
+
+      when "User"
+        user = User.find(@search_author_id)
+
+        if user.author.nil?
+          # User w/o author:
+          # create new author and update user
+          author = Author.new
+          author.save
+          user.author = author
+          user.save
+        else
+          author = user.author
+        end
+
+      when "Author"
+        author = Author.find(@search_author_id)
+      end
+
+    # if we don't want to associate with an existing author/user
+    else
+      # todo: should we delete any previously existing relationship?
+      author = Author.new
+      author.save
+    end
+
+    self.author = author
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/models/bibtex_entry.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,16 @@
+class BibtexEntry < ActiveRecord::Base
+  unloadable
+
+  belongs_to :publication
+  validates_presence_of :entry_type
+  
+  def entry_type_name
+    entry_type = self.entry_type
+    BibtexEntryType.find(entry_type).name
+  end
+  
+  def entry_type_label
+    entry_type = self.entry_type
+    BibtexEntryType.find(entry_type).label
+  end  
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/models/bibtex_entry_type.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,30 @@
+class BibtexEntryType < ActiveRecord::Base
+  unloadable
+
+  @@fields = Hash['article', ['journal', 'year', 'volume', 'number', 'pages', 'month', 'note' ],
+                  'book' , [ 'editor', 'publisher', 'volume', 'series', 'address', 'edition', 'month', 'year', 'note' ],
+                  'booklet' , [ 'howpublished', 'address', 'year', 'month', 'note', 'key' ],
+                  'conference', [ 'booktitle', 'year', 'editor', 'pages', 'organization', 'publisher', 'address', 'month', 'note' ],
+                  'inbook', [ 'editor', 'publisher', 'chapter', 'pages', 'volume', 'series', 'address', 'edition', 'year', 'note' ],
+                  'incollection', [ 'editor', 'publisher', 'chapter', 'pages', 'volume', 'series', 'address', 'edition', 'year', 'note' ],
+                  'inproceedings', [ 'booktitle', 'year', 'editor', 'pages', 'organization', 'publisher', 'address', 'month', 'note' ],
+                  'manual', [ 'organization', 'address', 'edition', 'month', 'year', 'note' ],
+                  'masterthesis', [ 'school', 'year', 'address', 'month', 'note' ],
+                  'misc', [ 'howpublished', 'month', 'year', 'note' ],
+                  'phdthesis', [ 'school', 'year', 'address', 'month', 'note' ],
+                  'proceedings', [ 'booktitle', 'year', 'editor', 'pages', 'organization', 'publisher', 'address', 'month', 'note' ],
+                  'techreport', [ 'year', 'type', 'number', 'address', 'month', 'note' ],
+                  'unpublished', [ 'note', 'month', 'year' ]]
+
+  def redundant?
+    name == 'conference'  # conference is a duplicate of inproceedings
+  end
+
+  def label
+    l("field_bibtex_#{name}")
+  end
+
+  def self.fields (type)
+    @@fields[ self.find(type).name ]
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/models/publication.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,110 @@
+# vendor/plugins/redmine_bibliography/app/models/publication.rb
+
+class Publication < ActiveRecord::Base
+  unloadable
+
+  has_many :authorships, :dependent => :destroy, :order => "auth_order ASC"
+  has_many :authors, :through => :authorships, :uniq => true
+
+  has_one :bibtex_entry, :dependent => :destroy
+
+  validates_presence_of :title
+  validates_length_of :authorships, :minimum => 1, :message => l("error_no_authors")
+  validates_associated :bibtex_entry, :authorships
+
+  accepts_nested_attributes_for :authorships
+  accepts_nested_attributes_for :authors, :allow_destroy => true
+  accepts_nested_attributes_for :bibtex_entry, :allow_destroy => true
+
+  has_and_belongs_to_many :projects, :uniq => true
+
+  before_save :set_initial_author_order
+
+  scope :visible, lambda {|*args| { :include => :projects,
+                                    :conditions => Project.allowed_to_condition(args.shift || User.current, :view_publication, *args) } }
+
+  acts_as_activity_provider :type => 'publication',
+                            :timestamp => "#{Publication.table_name}.created_at",
+                            :find_options => {
+                              :include => :projects,
+                              :conditions => "#{Project.table_name}.id = projects_publications.project_id"
+                            }
+
+  acts_as_event :title => Proc.new {|o| o.title },
+                :datetime => :created_at,
+                :type => 'publications',
+                :author => nil,
+                #todo - need too move the cache from the helper to the model
+                :description => Proc.new {|o| o.print_entry(:ieee)},
+                :url => Proc.new {|o| {:controller => 'publications', :action => 'show', :id => o.id }}
+
+
+  # Ensure error message uses proper text instead of
+  # bibtex_entry.entry_type (#268).  There has to be a better way to
+  # do this!
+  def self.human_attribute_name(k, *args)
+    if k == 'bibtex_entry.entry_type'
+      l(:field_entry_type)
+    else
+      super
+    end
+  end
+
+  def notify_authors_publication_added(project)
+    self.authors.each do |author|
+      Rails.logger.debug { "Sending mail to \"#{self.title}\" publication authors." }
+      Mailer.publication_added(author.user, self, project).deliver unless author.user.nil?
+    end
+  end
+
+  def notify_authors_publication_updated(project)
+    self.authors.each do |author|
+      Rails.logger.debug { "Sending mail to \"#{self.title}\" publication authors." }
+      Mailer.publication_updated(author.user, self, project).deliver unless author.user.nil?
+    end
+  end
+
+
+  def set_initial_author_order
+    authorships = self.authorships
+
+    logger.debug { "Publication \"#{self.title}\" has #{authorships.size} authors." }
+
+    authorships.each_with_index do |authorship, index|
+      if authorship.auth_order.nil?
+         authorship.auth_order = index
+      end
+    end
+  end
+
+  def print_bibtex_author_names
+    # this authors are correctly sorted because the authorships model
+    # already outputs the author names ASC by auth_order
+    self.authorships.map{|a| a.name_on_paper}.join(' and ')
+  end
+
+  def print_entry(style)
+    bib = BibTeX::Entry.new
+
+    bib.author = self.print_bibtex_author_names
+    bib.title = self.title
+
+    self.bibtex_entry.attributes.keys.sort.each do |key|
+      value = self.bibtex_entry.attributes[key].to_s
+      next if key == 'id' or key == 'publication_id' or value == ""
+
+      if key == "entry_type"
+        bib.type = BibtexEntryType.find(self.bibtex_entry.entry_type).name
+      else
+        bib[key.to_sym] = value
+      end
+    end
+
+    if style == :ieee
+      CiteProc.process(bib.to_citeproc, :style => :ieee, :format => :html)
+    else
+      bibtex = bib.to_s :include => :meta_content
+      bibtex.strip!
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/activities/_recent.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,92 @@
+<% events = @events_by_day %>
+<% max = 5 %>
+<% if (events.nil?)
+     activity = Redmine::Activity::Fetcher.new(User.current, :project => @project)
+
+     if @project
+        # Don't show news (duplicated with News box) or wiki edits (too
+	# tedious) in project front page
+        activity.scope = [ "changesets", "files", "issues", "documents" ]
+     end
+
+     events = activity.events(Date.today - 28, Date.today + 1)
+
+     if defined? user
+        events = events.select { |e|
+
+          if e.class != Publication
+            user.member_of? e.project
+          else
+            e.projects.map {|p| user.member_of? p }.any?
+          end
+        }
+
+     end
+
+     events = events.first(max)
+
+   end
+%>
+
+<div id="activity">
+
+<% if @project.nil? %>
+   <%= content_tag('h3', l(:label_activity_my_recent)) %>
+   <div class="activity box">
+<% end %>
+
+<% if events.empty? %>
+
+   <% if @project.nil? %>
+     <div class="tip"><%= l(:label_activity_my_recent_none) %></div>
+   <% end %>
+
+<% else %>
+
+   <% if !@project.nil? %>
+     <div class="activity box">
+     <%= content_tag('h3', l(:label_activity_recent)) %>
+   <% end %>
+
+   <dl>
+   <% events.sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| -%>
+    <%- if e.class != Publication -%>
+      <dt class="<%= User.current.logged? && e.respond_to?(:event_author) && User.current == e.event_author ? 'me' : nil %>">
+     	<%= avatar(e.event_author, :size => "24") if e.respond_to?(:event_author) %>
+        <span class="time"><%= format_time(e.event_datetime) %></span>
+        <%= content_tag('span', link_to_project(e.project), :class => 'project') if @project.nil? || @project != e.project %>
+        <% if e.respond_to?(:event_author) %>
+          <span class="author"><%= e.event_author %></span>
+        <% end %>
+      </dt>
+      <dd><%= link_to format_activity_title(e.event_title), e.event_url %>
+        <span class="description"><%= format_activity_description(e.event_description) %></span>
+      </dd>
+     <% else -%>
+      <dt class="<%= User.current.logged? && e.respond_to?(:event_author) && User.current == e.event_author ? 'me' : nil %>">
+         <span class="time"><%= format_time(e.event_datetime) %></span>
+          <%= link_to format_activity_title(e.event_title), e.event_url %>
+      was added to the following
+      <% if e.projects.count > 1 %>
+        projects:
+      <%- else -%>
+        project:
+      <%- end -%>
+      <%= content_tag('span', e.projects.join(', ')) -%>         <% if e.respond_to?(:event_author) %>
+             <span class="author"><%= e.event_author %></span>
+         <% end %>
+        </dt>
+           <dd><%= link_to format_activity_title(e.event_title), e.event_url %>
+           <span class="description"><%= format_activity_description(e.event_description) %></span>
+           </dd>
+     <% end -%>
+   <% end -%>
+   </dl>
+
+   </div>
+
+<% end %>
+
+<% if events.empty? and @project.nil? %></div><% end %>
+
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/activities/index.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,99 @@
+<h2><%=
+  if @author.nil?
+    if @institution_name.blank?
+      l(:label_activity)
+    else
+      l(:label_institution_activity, h(@institution_name))
+    end
+  else
+    l(:label_user_activity, link_to_user(@author)).html_safe
+  end
+  %></h2>
+<p class="subtitle"><%= l(:label_date_from_to, :start => format_date(@date_to - @days), :end => format_date(@date_to-1)) %></p>
+
+<div id="activity">
+<% @events_by_day.keys.sort.reverse.each do |day| %>
+<h3><%= format_activity_day(day) %></h3>
+<dl>
+<% sort_activity_events(@events_by_day[day]).each do |e, in_group| -%>
+  <%- if e.class != Publication -%>
+    <dt class="<%= e.event_type %> <%= "grouped" if in_group %> <%= User.current.logged? && e.respond_to?(:event_author) && User.current == e.event_author ? 'me' : nil %>">
+      <%= avatar(e.event_author, :size => "24") if e.respond_to?(:event_author) %>
+      <span class="time"><%= format_time(e.event_datetime, false) %></span>
+      <%= content_tag('span', h(e.project), :class => 'project') if @project.nil? || @project != e.project %>
+      <%= link_to format_activity_title(e.event_title), e.event_url %>
+    </dt>
+    <dd class="<%= "grouped" if in_group %>">
+      <span class="description"><%= format_activity_description(e.event_description) %></span>
+      <span class="author"><%= link_to_user(e.event_author) if e.respond_to?(:event_author) %></span>
+    </dd>
+  <%- else -%>
+    <dt class="<%= e.event_type %>  <%= User.current.logged? && e.respond_to?(:event_author) && User.current == e.event_author ? 'me' : nil %>">
+      <%= avatar(e.event_author, :size => "24") if e.respond_to?(:event_author) %>
+      <span class="time"><%= format_time(e.event_datetime, false) %></span>
+      <%= link_to format_activity_title(e.event_title), e.event_url %>
+      was added to the following
+      <% if e.projects.count > 1 %>
+        projects:
+      <%- else -%>
+        project:
+      <%- end -%>
+      <%= content_tag('span', e.projects.join(', ')) -%>
+    </dt>
+    <dd>
+      <span class="description"><%= e.event_description -%></span>
+      <span class="author"><%= link_to_user(e.event_author) if e.respond_to?(:event_author) %></span>
+    </dd>
+  <% end -%>
+<%- end -%>
+</dl>
+<% end -%>
+</div>
+
+<%= content_tag('p', l(:label_no_data), :class => 'nodata') if @events_by_day.empty? %>
+
+<div style="float:left;">
+<%= link_to_content_update("\xc2\xab " + l(:label_previous),
+                   params.merge(:from => @date_to - @days - 1),
+                   :title => l(:label_date_from_to, :start => format_date(@date_to - 2*@days), :end => format_date(@date_to - @days - 1))) %>
+</div>
+<div style="float:right;">
+<%= link_to_content_update(l(:label_next) + " \xc2\xbb",
+                   params.merge(:from => @date_to + @days - 1),
+                   :title => l(:label_date_from_to, :start => format_date(@date_to), :end => format_date(@date_to + @days - 1))) unless @date_to >= Date.today %>
+</div>
+&nbsp;
+<% other_formats_links do |f| %>
+  <%= f.link_to 'Atom', :url => params.merge(:from => nil, :key => User.current.rss_key) %>
+<% end %>
+
+<% content_for :header_tags do %>
+<%= auto_discovery_link_tag(:atom, params.merge(:format => 'atom', :from => nil, :key => User.current.rss_key)) %>
+<% end %>
+
+<% content_for :sidebar do %>
+<% form_tag({}, :method => :get) do %>
+<h3><%= l(:label_activity) %></h3>
+
+<ul>
+<% @activity.event_types.each do |t| %>
+  <li>
+    <%= check_box_tag "show_#{t}", 1, @activity.scope.include?(t) %>
+    <label for="show_<%=t%>">
+      <%= link_to(l("label_#{t.singularize}_plural"),
+                  {"show_#{t}" => 1, :user_id => params[:user_id], :from => params[:from]})%>
+    </label>
+  </li>
+<% end %>
+</ul>
+
+<% if @project && @project.descendants.active.any? %>
+    <%= hidden_field_tag 'with_subprojects', 0 %>
+    <p><label><%= check_box_tag 'with_subprojects', 1, @with_subprojects %> <%=l(:label_subproject_plural)%></label></p>
+<% end %>
+<%= hidden_field_tag('user_id', params[:user_id]) unless params[:user_id].blank? %>
+<p><%= submit_tag l(:button_apply), :class => 'button-small', :name => nil %></p>
+<% end %>
+<% end %>
+
+<% html_title(l(:label_activity), @author) -%>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/authors/index.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,26 @@
+<h2><%=l(:label_authors_index)%></h2>
+
+<table class="list authors">		
+	<thead><tr>
+	<th><%=l(:field_author_name)%></th>
+	<th><%=l(:field_author_username)%></th>
+	<th><%=l(:field_author_publications)%></th>
+	</tr></thead>
+	<tbody>
+	  	
+  <% @authors.each do |author|%>    
+	<tr id="author-<%= author.id %>" class="<%= cycle('odd', 'even') %>">
+		<td class="title">
+			<%= link_to_author author %>
+		</td>
+    <td class="username ">
+      <%= link_to author.user unless author.user.nil? %>
+    </td>
+    <td class="project">             
+      <%= render_author_publications(author) %>	
+    </td>
+	</tr>
+	<% end %>
+	</tbody>
+</table>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/authors/show.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,28 @@
+<h2><%=l(:label_authors_show)%></h2>
+
+<div class="autoscroll">
+  <table class="list authors">		
+  	<thead>
+  	  <tr>
+  	    <th><%=l(:field_authorship_publication_title)%></th>
+  	    <th><%=l(:field_authorship_name)%></th>
+  	    <th><%=l(:field_authorship_email)%></th>
+	 	    <th><%=l(:field_authorship_institution)%></th>
+  	  </tr>
+  	</thead>
+  
+     <% @author.authorships.each do |authorship| %>
+       <tr id="authorship-<%= authorship.id %>" class="<%= cycle('odd', 'even') %>">
+         <td class="title"><%= link_to_publication(authorship.publication) %></td>
+         <td class="name"><%= h authorship.name_on_paper %></td>
+         <td class="email"><%= h authorship.email %></td>
+         <td class="institution"><%= h authorship.institution %></td>
+       </tr>
+    <% end %>
+  	</tbody>
+  </table>
+</div>
+
+<% content_for :sidebar do %>
+<% end %>
+  
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/authorships/update.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1 @@
+<h2>Authorships#update</h2>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/mailer/publication_added.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1 @@
+<%= l(:mail_body_publication_added, :publication => @publication.title, :project => @project.name) %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/mailer/publication_added.text.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,2 @@
+<%= l(:mail_body_publication_added, :publication => @publication.title, :project => @project.name) %>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/my/blocks/_publications_box.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,30 @@
+<% get_my_publications %>
+
+<h3><%=l(:label_my_publications_box) %> <%= "(" + @my_publications.count.to_s + ")" %> </h3>
+
+<table class="list publications">		
+	<thead><tr>
+	<th><%=l(:field_publication_title)%></th>
+	<th><%=l(:field_publication_authors)%></th>
+	<th><%=l(:field_publication_projects)%></th>
+	</tr></thead>
+	<tbody>
+	  	
+  <% @my_publications.each do |publication|%>    
+	<tr id="publication-<%= publication.id %>" class="<%= cycle('odd', 'even') %>">
+		<td class="title">
+			<%= link_to publication.title, publication %>
+		</td>
+    <td class="authors">
+      <%= render_publications_authors(publication) %>
+    </td>
+    <td class="project">             
+      <%= render_publications_projects(publication) %>	
+    </td>
+	</tr>
+	<% end %>
+	</tbody>
+</table>
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/projects/_bibliography_box.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,35 @@
+<% content_for :header_tags do %>
+    <%= stylesheet_link_tag 'bibliography', :plugin => 'redmine_bibliography' %>
+    <%= javascript_include_tag 'bibtex', :plugin => 'redmine_bibliography' -%>
+<% end %>
+
+<% if @project.publications.any? %>
+<%= stylesheet_link_tag 'bibliography', :plugin => 'redmine_bibliography' %>
+  <div id="bibliography">
+    <div class="box">
+    <h3><%=l(:label_related_publication_plural)%></h3>
+
+     <dl>
+     <% @project.publications.each do |publication| %>
+       <dt>
+          <%= print_ieee_format(publication) %>
+       </dt>
+       <dd>
+         <%= link_to(l("more_details_link"), {:controller => :publications, :action => :show, :id => publication.id, :project_id => @project.id}) -%>
+
+         <%= link_to l(:bibtex_link).html_safe, "javascript:void(0)", :class => "bibtex-link"-%>
+
+        <%- unless publication.external_url.blank? -%>
+            <%= link_to l(:external_url_link), publication.external_url, {:target => "_blank"} -%>
+        <%- end -%>
+
+
+       </dd>
+       <dd class="bibtex-textarea collapsed" style="display: none;">
+         <textarea readonly> <%= print_bibtex_format(publication) -%> </textarea>
+       </dd>
+   <% end -%>
+     </dl>
+  </div>
+</div>
+<% end -%>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/projects/show.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,140 @@
+<div class="contextual">
+	<% if User.current.allowed_to?(:add_subprojects, @project) %>
+		<%= link_to l(:label_subproject_new), new_project_path(:parent_id => @project), :class => 'icon icon-add' %>
+	<% end %>
+	
+  <% if User.current.allowed_to?(:close_project, @project) %>
+    <% if @project.active? %>
+      <%= link_to l(:button_close), close_project_path(@project), :data => {:confirm => l(:text_are_you_sure)}, :method => :post, :class => 'icon icon-lock' %>
+    <% else %>
+      <%= link_to l(:button_reopen), reopen_project_path(@project), :data => {:confirm => l(:text_are_you_sure)}, :method => :post, :class => 'icon icon-unlock' %>
+    <% end %>
+  <% end %>
+
+	<% if @project.module_enabled? :redmine_bibliography %>
+	  <% if User.current.allowed_to?(:add_publication, @project) %>	
+		  <%= link_to l(:label_add_publication_to_project), {:controller => 'publications', :action => 'new', :project_id => @project}, :class => 'icon icon-add' %>		
+	  <% end %>
+	<% end %>
+</div>
+
+<% if @project.has_welcome_page %>
+<% page = @project.wiki.find_page("Overview") %>
+<% end %>
+
+<% if page %>
+
+<% if @project.module_enabled? :wiki %>
+<% if User.current.allowed_to?(:edit_wiki_pages, @project) %>
+<div class="contextual">
+<%= link_to(l(:button_welcome_page_edit_this), {:controller => 'wiki', :action => 'edit', :project_id => @project, :id => Wiki.titleize("Overview")}, :class => 'icon icon-edit') %>
+</div>
+<% end %>
+<% end %>
+
+<div class="contextual" style="clear: right">
+<ul>
+<% unless @project.homepage.blank? %><li><%=l(:field_homepage)%>: <%= link_to h(@project.homepage), @project.homepage %></li><% end %>
+<% if @subprojects.any? %>
+	<li><%=l(:label_subproject_plural)%>:
+	    <%= @subprojects.collect{|p| link_to p, project_path(p)}.join(", ").html_safe %></li>
+<% end %>
+</ul>
+</div>
+
+<%= render(:partial => "wiki/content", :locals => {:content => page.content_for_version()}) %>
+
+<% else %>
+
+<h2><%=l(:label_overview)%></h2> 
+	
+<% unless @project.active? %>
+  <p class="warning"><span class="icon icon-lock"><%= l(:text_project_closed) %></span></p>
+<% end %>
+
+<div class="splitcontentleft">
+  <% if @project.description.present? %>
+	<div class="wiki">
+		<%= textilizable @project.description %>
+	</div>	
+  <% end %>
+  <div class="overviewfields">
+	<% unless @project.homepage.blank? %><h4><%=l(:field_homepage)%>:</h4><ul><li><%= link_to h(@project.homepage), @project.homepage %></li></ul><% end %>
+  <% if @subprojects.any? %>
+ 	<h4><%=l(:label_subproject_plural)%>:</h4>
+          <ul>
+	    <li><%= @subprojects.collect{|p| link_to(h(p), :action => 'show', :id => p)}.join("</li><li>").html_safe %></li>
+          </ul>
+  <% end %>
+	<% @project.visible_custom_field_values.each do |custom_value| %>
+	<% if !custom_value.value.blank? %>
+	   <h4><%= custom_value.custom_field.name%>:</h4><ul><li><%=h show_value(custom_value) %></li></ul>
+	<% end %>
+	<% end %>
+  </div>
+  <% if User.current.allowed_to?(:view_issues, @project) and @open_issues_by_tracker.values.any? %>
+  <div class="issues box">    
+    <h3><%=l(:label_issue_tracking)%></h3>
+    <ul>
+    <% for tracker in @trackers %>    
+      <li><%= link_to h(tracker.name), project_issues_path(@project, :set_filter => 1, :tracker_id => tracker.id) %>:
+	<%= l(:label_x_open_issues_abbr_on_total, :count => @open_issues_by_tracker[tracker].to_i,
+	:total => @total_issues_by_tracker[tracker].to_i) %>
+      </li>
+    <% end %>
+    </ul>
+    <p>
+    	<%= link_to l(:label_issue_view_all), project_issues_path(@project, :set_filter => 1) %>
+	    <% if User.current.allowed_to?(:view_calendar, @project, :global => true) %>
+	      | <%= link_to l(:label_calendar), project_calendar_path(@project) %>
+	    <% end %>
+	    <% if User.current.allowed_to?(:view_gantt, @project, :global => true) %>
+	      | <%= link_to l(:label_gantt), project_gantt_path(@project) %>
+	    <% end %>
+    </p>
+  </div>
+  <% end %>
+  <%= call_hook(:view_projects_show_left, :project => @project) %>
+</div>
+
+<div class="splitcontentright">
+  
+  <%= render :partial => 'bibliography_box' %>
+  
+  <%= render :partial => 'members_box' %>
+    
+  <% if @news.any? && authorize_for('news', 'index') %>
+  <div class="news box">
+    <h3><%=l(:label_news_latest)%></h3>  
+    <%= render :partial => 'news/news', :collection => @news %>
+    <p><%= link_to l(:label_news_view_all), project_news_index_path(@project) %></p>
+  </div>  
+  <% end %>
+
+  <%= render :partial => 'activities/recent' %>
+
+  <%= call_hook(:view_projects_show_right, :project => @project) %>
+</div>
+
+<% content_for :sidebar do %>
+    <%= call_hook(:view_projects_show_sidebar_top, :project => @project) %>
+    <% if @total_hours.present? && User.current.allowed_to?(:view_time_entries, @project) %>
+    <h3><%= l(:label_spent_time) %></h3>
+    <p><span class="icon icon-time"><%= l_hours(@total_hours) %></span></p>
+    <p>
+    <% if User.current.allowed_to?(:log_time, @project) %>
+      <%= link_to l(:button_log_time), new_project_time_entry_path(@project) %> |
+    <% end %>
+    <%= link_to(l(:label_details), project_time_entries_path(@project)) %> |
+    <%= link_to(l(:label_report), report_project_time_entries_path(@project)) %></p>
+    <% end %>
+    <%= call_hook(:view_projects_show_sidebar_bottom, :project => @project) %>
+<% end %>
+
+<% end %>
+
+<% content_for :header_tags do %>
+<%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :id => @project, :format => 'atom', :key => User.current.rss_key}) %>
+<% end %>
+
+<% html_title('') -%>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/_add_project_form.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,26 @@
+<%= form_for(:publication,
+            :remote => true,
+            :url => {:controller => 'publications', :action => 'add_project', :id => @publication, :project_id => @project},
+            :method => :post,
+            :html => { :id => 'add_project_form' },
+            :loading => "$('project-add-submit').disable()",
+            :complete => "$('project-add-submit').enable()") do |f| %>
+
+              <fieldset><legend><%=l(:label_add_project_to_publication)%></legend>
+                <p>
+                  <%= label_tag "project_search", l(:label_project_search) %><%= text_field_tag 'project_search', nil %>
+	              </p>
+
+                <%= javascript_tag "observeSearchfield('project_search', 'projects', '#{ escape_javascript url_for(:controller => 'publications',
+                 :action => 'autocomplete_for_project',
+                 :id => @publication.id) }')" %>
+
+                <div id="projects">
+                  <% if params[:q] && params[:q].length > 1 %>
+                    <%= projects_check_box_tags 'project[project_ids][]', @projects %>
+                  <% end %>
+                </div>
+
+                <p><%= submit_tag l(:button_add), :id => 'project-add-submit' %></p>
+              </fieldset>
+  <% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/_authorship_fields.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,30 @@
+<div id="authors" class="fields">
+  <div class="author_edit" id="<%= form_tag_id( f.object_name, :edit_author_info ) %>">
+
+    <p><%= f.label :name_on_paper %><%= f.text_field :name_on_paper, :class => "author_name_on_paper" -%></p>
+    <p><%= f.label :institution %><%= f.text_field :institution -%></p>
+    <p><%= f.label :email %><%= f.text_field :email -%></p>
+
+    <p class="author_associated search_author_tie">
+      <%= f.check_box :search_author_tie, :label => '' -%>
+      <% unless f.object.search_author_tie %>
+        <span>Not associated with any SoundSoftware site user.</span>
+      <% else %>
+        <span>Associated with <%= render_authorship_link(f.object.search_author_class, f.object.search_author_id) -%>.</span>
+      <% end %>
+    </p>
+
+    <%= f.hidden_field :search_author_class -%>
+    <%= f.hidden_field :search_author_id -%>
+  </div>
+
+  <div>
+    <p>
+      <%= button_to_function l(:label_save_author), '', :id => form_tag_id(f.object_name, :edit_save_button), :class => 'author_save_btn' -%>
+
+      <%= button_to_function l(:label_edit_author), '', :id => form_tag_id(f.object_name, :edit_button), :class => 'author_edit_btn', :style => "display:none;" -%>
+
+      <%= link_to_remove_fields l("remove_author"), f %>
+    </p>
+  </div>
+</div>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/_bibtex_fields.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,78 @@
+<p>
+  <label for="publication_bibtex_entry_attributes_entry_type"><%=l("field_entry_type")%> <span class="required">*</span></label>
+	<%= f.collection_select :entry_type,
+	        BibtexEntryType.find(:all).reject { |x| x.redundant? },
+	        :id,
+	        :label,
+	        { :selected => @selected_bibtex_entry_type_id, :prompt => true } %>
+</p>
+
+<p class="bibtex hol">
+  <%= f.label :year %><%= f.text_field :year, :size => 5 -%>
+</p>
+<p class="bibtex hol">
+  <%= f.label :month %><%= f.text_field :month, :size => 5 -%>
+</p>
+<p class="bibtex hol">
+  <%= f.label :chapter %><%= f.text_field :chapter, :size => 5 -%>
+</p>
+<p class="bibtex hol">
+  <%= f.label :editor %><%= f.text_field :editor -%>
+</p>
+<p class="bibtex hol">
+  <%= f.label :booktitle %><%= f.text_field :booktitle -%>
+</p>
+<p class="bibtex hol">
+  <%= f.label :publisher %><%= f.text_field :publisher -%>
+</p>
+<p class="bibtex hol">
+  <%= f.label :pages %><%= f.text_field :pages, :size => 5 -%>
+</p>
+<p class="bibtex hol">
+ <%= f.label :address %><%= f.text_field :address -%>
+</p>
+<p class="bibtex hol">
+ <%= f.label :annote %><%= f.text_field :annote -%>
+</p>
+<p class="bibtex hol">
+ <%= f.label :crossref %><%= f.text_field :crossref -%>
+</p>
+<p class="bibtex hol">
+ <%= f.label :edition %><%= f.text_field :edition -%>
+</p>
+<p class="bibtex hol">
+ <%= f.label :eprint %><%= f.text_field :eprint -%>
+</p>
+<p class="bibtex hol">
+ <%= f.label :howpublished %><%= f.text_field :howpublished -%>
+</p>
+<p class="bibtex hol">
+ <%= f.label :journal %><%= f.text_field :journal -%>
+</p>
+<p class="bibtex hol">
+ <%= f.label :key %><%= f.text_field :key -%>
+</p>
+<p class="bibtex hol">
+ <%= f.label :note %><%= f.text_field :note -%>
+</p>
+<p class="bibtex hol">
+ <%= f.label :number %><%= f.text_field :number, :size => 5 -%>
+</p>
+<p class="bibtex hol">
+ <%= f.label :organization %><%= f.text_field :organization %>
+</p>
+<p class="bibtex hol">
+ <%= f.label :school %><%= f.text_field :school %>
+</p>
+<p class="bibtex hol">
+ <%= f.label :series %><%= f.text_field :series %>
+</p>
+<p class="bibtex hol">
+ <%= f.label :type %><%= f.text_field :type %>
+</p>
+<p class="bibtex hol">
+ <%= f.label :url %><%= f.text_field :url %>
+</p>
+<p class="bibtex hol">
+ <%= f.label :volume %><%= f.text_field :volume %>
+</p>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/_form.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,34 @@
+<h3><%= f.label :title %>&nbsp;<span class="required">*</span>&nbsp;<%= f.text_field :title, :required => true, :size => 70 %></h3>
+
+<div class="splitcontentleft">
+  <h3><%= l(:label_publication_other_details) %></h3>
+  <div class="box tabular">
+    <%= f.fields_for :bibtex_entry do |builder| -%>
+      <%= render :partial => 'bibtex_fields', :locals => { :f => builder}  %>
+    <%- end -%>
+
+    <p>
+      <%= f.label :external_url %><%= f.text_field :external_url -%>
+      <br />
+      <em><%= l(:text_external_url) -%></em>
+    </p>
+    <p>
+      <%= f.label :doi %><%= f.text_field :doi -%>
+      <br />
+      <em><%= l(:text_doi) %></em>
+    </p>
+
+  </div>
+</div>
+
+<div class="splitcontentright">
+  <h3><%= l(:authors) %></h3>
+  <div class="box tabular">
+    <%= f.fields_for :authorships do |builder| -%>
+      <%= render "authorship_fields", :f => builder %>
+    <%- end -%>
+    <%= link_to_add_author_fields l(:label_add_an_author), f, :authorships, params[:action] %>
+  </div>
+</div>
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/_identify_author_form.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,25 @@
+<legend><%= "Identify Authors in the system…" %></legend>
+
+<%= link_to_remote "It's me!", 
+   { :url => { :controller => 'publications', 
+     :action => 'add_me_as_author',
+     :project_id => @project }, :method => 'post'}, 
+   { :class => 'icon icon-add', :id => "add_me_as_author" } %>
+
+<p>
+  <%= label_tag "author_search", l(:label_project_search) %><%= text_field_tag 'author_search', nil %>
+</p>
+	     
+<%= observe_field( form_tag_id(f.object_name, :name),
+     :frequency => 0.5,
+     :update => :identify_author,
+     :url => { :controller => 'publications', :action => 'autocomplete_for_author' },
+     :with => 'q')
+%>
+
+<div id="identify_author">
+  <% if params[:q] && params[:q].length > 1 %>
+    <%= select_author_links 'author[author_ids][]', @authors %>
+  <% end %>
+</div>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/_list_projects.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1 @@
+<%= render_projects_list(@publication, true) %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/_new_bibtex_step.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+<h3>New Bibtex</h3>
+
+<h4>Paste your Bibtex entries here</h4>
+<p>
+  <%=label_tag :bibtex_entry %>
+  <%=text_area_tag :bibtex_entry%>  
+</p>
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/_review_bibtex_step.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,2 @@
+<h2>Review new entries</h2>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/add_project.rjs	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,3 @@
+page.replace_html :list_projects, :partial => 'list_projects'
+page[:add_project_form].reset
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/autocomplete_for_author.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,12 @@
+<%= raw @results.map { |result|
+    {
+    'label' => result.name,
+    'value' => result.name,
+    'search_author_class' => result.class.name,
+    'search_author_id' => result.id,
+    'name' => result.name,
+    'institution' => result.institution,
+    'email' => result.mail,
+    'authorship_link' => " Keep associated with #{render_authorship_link(result.class.name, result.id)}"
+    }
+}.to_json %>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/autocomplete_for_project.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,3 @@
+<% if params[:q] && params[:q].length > 1 %>
+	<%= projects_check_box_tags 'publication[project_ids][]', @projects %>
+<% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/create.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1 @@
+<h2>Publications#create</h2>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/edit.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,39 @@
+<% content_for :header_tags do %>
+  <%= stylesheet_link_tag 'bibliography', :plugin => 'redmine_bibliography' -%>
+  <%= javascript_include_tag 'bibliography', :plugin => 'redmine_bibliography' -%>
+  <%= javascript_include_tag 'authors', :plugin => 'redmine_bibliography' -%>
+
+  <%= javascript_include_tag 'edit_publication', :plugin => 'redmine_bibliography' -%>
+
+  <%= javascript_tag "$('#publication_bibtex_entry_attributes_entry_type').live('change', function() {
+                        $this = $(this);
+                        $.ajax({
+                            type: 'get',
+                            url: '#{url_for(:controller => :publications, :action => :show_bibtex_fields)}',
+                            data: {
+                                value: $this.val()
+                            },
+                            dataType: 'script'
+                        }); return false; });" -%>
+
+  <%= javascript_tag "authorship_autocomplete('#{url_for :controller => :publications, :action => :autocomplete_for_author}');" -%>
+
+<% end %>
+
+<%= error_messages_for 'publication' %>
+
+<h2><%=l(:label_publication_show)%></h2>
+
+<%= form_for @publication, :url => { :project_id  => @project, :action => :update } do |f| -%>
+
+    <%= render :partial => 'form', :locals => { :f => f }  %>
+
+    <div style="clear:both"></div>
+    <%= f.submit %>
+<% end %>
+
+<p>
+    <%= link_to l(:label_publication_show), { :controller => "publications", :action => "show", :id => @publication, :project_id => @project_id } %> |
+    <%= link_to l(:label_publication_index), { :controller => "publications", :action => "index", :project_id => @project } %>
+</p>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/import.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,12 @@
+<h1>New Publication</h1>
+
+<% form_for @publication, :url => { :action => "create" } do |f| %>
+  <% f.error_messages %>
+
+  <%= render :partial  => "#{@publication.current_step}_bibtex_step", :locals => { :f => f }  %>
+
+  <p><%= f.submit "Submit" %></p>
+  <p><%= f.submit "Back", :name => "back_button" unless @publication.first_step? %></p>
+  
+<% end %>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/index.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,40 @@
+<div class="contextual">
+  <% if User.current.allowed_to?(:add_publication, @project) %>			
+	  <%= link_to l(:label_publication_new), {:controller => 'publications', :action => 'new', :project_id => @project}, :class => 'icon icon-add' %>
+	<% end %>
+</div>
+
+  <% if @project %>
+    <h3><%= l(:label_all_publications_for_project, :project => @project.name) %></h3>
+  <% else %>
+    <h3><%= l(:label_all_publications) %></h3>
+  <% end %>
+
+  <div class="autoscroll">
+  <table class="list publications">
+    <thead><tr>
+      <th><%= l(:title) %></th> 
+      <th><%= l(:authors) %></th> 
+      <th><%= l(:year) %></th>
+      <th><%= l(:associated_projects) %></th>
+    </tr></thead>
+
+    <%- @publications.each do |publication| -%>        
+      <%- if publication.projects.visible.length > 0 -%>
+        <tr class="<%= cycle('odd', 'even') %>">
+          <td class="firstcol title" align="top"><%= link_to publication.title, :controller => "publications", :action => "show", :id => publication, :project_id => @project %></td>
+          <td class="authors" align="top">
+            <%= render_authorships_list(publication) %>
+          <td class="year"><%= publication.bibtex_entry.year %></td>
+          <td class="projects">
+              <%= render_projects_list(publication, false) %>
+          </td>
+        </tr>
+      <%- end -%>
+    <%- end -%>
+  </table>
+  </div>
+
+<% content_for :sidebar do %>
+<% end %>
+  
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/new.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,35 @@
+<% content_for :header_tags do %>
+    <%= javascript_include_tag 'authors', :plugin => 'redmine_bibliography' %>
+    <%= stylesheet_link_tag 'bibliography', :plugin => 'redmine_bibliography' %>
+    <%= javascript_include_tag 'bibliography', :plugin => 'redmine_bibliography' -%>
+    <%= javascript_include_tag 'new_publication', :plugin => 'redmine_bibliography' -%>
+
+    <%= javascript_tag "
+      $('#publication_bibtex_entry_attributes_entry_type').live('change', function() {
+          $this = $(this);
+          $.ajax({
+              type: 'get',
+              url: '#{url_for(:controller => :publications, :action => :show_bibtex_fields)}',
+              data: {
+                  value: $this.val()
+              },
+              dataType: 'script'
+          });
+          return false;
+      });"-%>
+
+  <%= javascript_tag "authorship_autocomplete('#{url_for :controller => :publications, :action => :autocomplete_for_author}');" -%>
+
+<% end %>
+
+<%= error_messages_for 'publication' %>
+
+<h2><%=l(:label_publication_new)%></h2>
+
+<%= form_for @publication, :url => { :project_id  => @project, :action => :create } do |f| -%>
+
+    <%= render :partial => 'form', :locals => { :f => f }  %>
+
+    <div style="clear:both"></div>
+    <%= f.submit %>
+<% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/show.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,90 @@
+<%= javascript_tag "$(document).ready(function(){
+
+   $('#authorships').sortable({
+       axis: 'y',
+       dropOnEmpty: false,
+       handle: '.handle',
+       cursor: 'crosshair',
+       items: 'li',
+       opacity: 0.4,
+       scroll: true,
+       update: function(){
+          $.ajax({
+              type: 'post',
+              data: $('#authorships').sortable('serialize'),
+              dataType: 'script',
+              complete: function(request){
+                 $('#authorship').effect('highlight');
+              },
+                 url: '#{url_for(:controller => :authorships, :action => :sort)}'});
+              }
+          });
+     });
+" -%>
+
+<h2><%=l(:label_publication_show)%></h2>
+
+<div class="box">
+  <h3>Publication Info</h3>
+  <p><%= print_ieee_format(@publication)%></p>
+
+  <h3>B<small>IB</small>T<sub>E</sub>X Format</h3>
+  <pre><%=h print_bibtex_format(@publication) %></pre>
+</div>
+
+<div class="box">
+
+<h4><%= l(:authors) %></h4>
+
+<ul id="authorships">
+  <% for authorship in @publication.authorships.find(:all, :order => :auth_order) %>
+    <%= content_tag_for :li, authorship do -%>
+      <%- if User.current.allowed_to?(:edit_publication, @project) && @publication.authorships.length > 1 -%>
+        <span class="handle">[drag to reorder]</span>
+      <%- end -%>
+
+      <%= link_to_authorship authorship %> <em><%= h(authorship.institution) %></em>
+
+      <br />
+    <%- end -%>
+  <%- end -%>
+</ul>
+
+<%- if @publication.bibtex_entry != nil -%>
+  <%= show_bibtex_fields(@publication.bibtex_entry).html_safe -%>
+<%- end -%>
+
+<%- unless @publication.external_url.blank? -%>
+  <p>
+    <b><%= l(:field_external_url) %>:</b> <%= link_to h(@publication.external_url), @publication.external_url, {:target => "_blank"} -%>
+  </p>
+<%- end -%>
+
+<% unless @publication.doi.blank? %>
+  <p>
+    <b><%= l(:field_doi)-%>:</b> <%= link_to h(@publication.doi), "http://dx.doi.org/#{@publication.doi}", {:target => "_blank"} -%>
+  </p>
+<% end %>
+
+<br / >
+  <% if User.current.allowed_to?(:add_publication, @project) %>
+    <%= link_to l(:label_publication_edit), { :controller => "publications", :action => "edit", :id => @publication, :project_id => @project } %> |
+    <%= link_to "Delete", {:controller => 'publications', :action => 'destroy', :id => @publication, :project_id => @project },
+                                                     :confirm => l(:text_are_you_sure), :method => :delete, :title => l(:button_delete) %> |
+  <% end %>
+  <%= link_to l(:view_all_publications), {:controller => 'publications', :action => 'index', :project_id => @project } %>
+</div>
+
+<% projects = Project.active.find(:all, :limit => 100, :order => 'name ASC') - @publication.projects %>
+
+<% content_for :sidebar do %>
+  <h3><%=l(:label_publication_project_index)%></h3>
+
+  <p id="list_projects">
+    <%= render :partial => 'list_projects' %>
+  </p>
+
+  <%- if User.current.allowed_to?(:edit_publication, @project) -%>
+    <%= render :partial => 'add_project_form' %>
+  <%- end -%>
+<% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/show_bibtex_fields.js.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,15 @@
+fields = <%= @fields.to_json.html_safe -%>;
+
+$.each($(".bibtex"), function( key, value ) {
+    $this = $(this);
+
+    input_id = $this.children('input').attr('id');
+    name = input_id.split('_')[4];
+
+    if ($.inArray(name, fields) !== -1){
+        $this.show();
+    }
+    else{
+        $this.hide();
+    }
+});
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/publications/update.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1 @@
+<h2>Publications#update</h2>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/settings/_bibliography.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,3 @@
+<p><label>Menu caption</label>
+<%= text_field_tag 'settings[menu]', @settings['menu'], :size => 30 %>
+<br /><em>Clear this field if you don't want to add a tab to the project menu</em></p>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/app/views/users/show.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,104 @@
+<%= stylesheet_link_tag 'bibliography', :plugin => 'redmine_bibliography' %>
+
+<div class="contextual">
+<%= link_to(l(:button_edit), edit_user_path(@user), :class => 'icon icon-edit') if User.current.admin? %>
+</div>
+
+<h2><%= avatar @user, :size => "50" %> <%=h @user.name %></h2>
+
+<div class="splitcontentleft">
+<ul>
+	<% unless @user.pref.hide_mail %>
+		<li><%=l(:field_mail)%>: <%= mail_to(h(@user.mail), nil, :encode => 'javascript') %></li>
+	<% end %>
+	<% @user.visible_custom_field_values.each do |custom_value| %>
+	<% if !custom_value.value.blank? %>
+    <li><%=h custom_value.custom_field.name%>: <%=h show_value(custom_value) %></li>
+	<% end %>
+	<% end %>
+    <li><%=l(:label_registered_on)%>: <%= format_date(@user.created_on) %></li>
+	<% unless @user.last_login_on.nil? %>
+		<li><%=l(:field_last_login_on)%>: <%= format_date(@user.last_login_on) %></li>
+	<% end %>
+</ul>
+
+<h3><%=l(:label_ssamr_description)%></h3>
+<%= textilizable @description %>
+
+<h3><%=l(:label_ssamr_institution)%></h3>
+<p><%= h @institution_name %></p>
+
+
+<% unless @memberships.empty? %>
+<h3><%=l(:label_project_plural)%></h3>
+<ul>
+<% for membership in @memberships %>
+	<li><%= link_to_project(membership.project) %>
+    (<%=h membership.roles.sort.collect(&:to_s).join(', ') %>, <%= format_date(membership.created_on) %>)</li>
+<% end %>
+</ul>
+<% end %>
+<%= call_hook :view_account_left_bottom, :user => @user %>
+</div>
+
+<div class="splitcontentright">
+
+  <% if @user.author %>
+  <div id="bibliography">
+    <% @publications = Publication.all(:include => :authors, :conditions => "authors.id = #{@user.author.id}") %>
+
+    <h3><%=l(:publications) %> <%= "(" + @publications.count.to_s + ")" %> </h3>
+
+    <% @publications.each do |publication|%>    
+      <dt>
+        <span class="authors">
+          <%= publication.authorships.map { |a| h a.name_on_paper }.join(', ') %><% if !publication.authorships.empty? %>.<% end %>
+        </span>
+        <span class="title"><%= link_to publication.title, :controller => 'publications', :action => 'show', :id => publication %></span>
+        <% if publication.bibtex_entry.year.to_s != "" %>
+          <span class="year">
+            &nbsp;(<%= publication.bibtex_entry.year %>)
+          </span>
+        <% end %>
+      </dt>
+      <dd>
+      </dd>
+  	<% end %>
+  </div>
+  <% end %>
+
+
+<% unless @events_by_day.empty? %>
+<h3><%= link_to l(:label_activity), :controller => 'activities', :action => 'index', :id => nil, :user_id => @user, :from => @events_by_day.keys.first %></h3>
+
+<p>
+<%=l(:label_reported_issues)%>: <%= Issue.where(:author_id => @user.id).count %>
+</p>
+
+<div id="activity">
+<% @events_by_day.keys.sort.reverse.each do |day| %>
+<h4><%= format_activity_day(day) %></h4>
+<dl>
+<% @events_by_day[day].sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| -%>
+  <dt class="<%= e.event_type %>">
+  <span class="time"><%= format_time(e.event_datetime, false) %></span>
+  <%= content_tag('span', h(e.project), :class => 'project') %>
+  <%= link_to format_activity_title(e.event_title), e.event_url %></dt>
+  <dd><span class="description"><%= format_activity_description(e.event_description) %></span></dd>
+<% end -%>
+</dl>
+<% end -%>
+</div>
+
+<% other_formats_links do |f| %>
+	<%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'index', :id => nil, :user_id => @user, :key => User.current.rss_key} %>
+<% end %>
+
+<% content_for :header_tags do %>
+		<%= auto_discovery_link_tag(:atom, :controller => 'activities', :action => 'index', :user_id => @user, :format => :atom, :key => User.current.rss_key) %>
+<% end %>
+<% end %>
+<%= call_hook :view_account_right_bottom, :user => @user %>
+</div>
+
+<% html_title @user.name %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/assets/javascripts/authors.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,45 @@
+function add_author_fields(link, association, content, action) {
+    var new_id = new Date().getTime();
+    var regexp = new RegExp("new_" + association, "g");
+
+    $(link).before(content.replace(regexp, new_id));
+}
+
+function remove_fields(link) {
+  $(link).prev("input[type=hidden]").val("1");
+  $(link).closest(".fields").hide();
+}
+
+function authorship_autocomplete(url){
+    $(".author_name_on_paper").live('keyup.autocomplete', function(){
+         $this = $(this);
+
+         $this.autocomplete({
+            source: url,
+            minLength: 2,
+            focus: function(event, ui) {
+                $this.val(ui.item.label);
+                return false;
+            },
+            select: function(event, ui){
+                $this.closest('div').find("input[id$='institution']").val(ui.item.institution);
+                $this.closest('div').find("input[id$='email']").val(ui.item.email);
+
+                $this.closest('div').find("input[id$='search_author_class']").val(ui.item.  search_author_class);
+                $this.closest('div').find("input[id$='search_author_id']").val(ui.item. search_author_id);
+
+                $this.closest('div').find("input[id$='search_author_tie']").attr('checked', '   checked');
+                $this.closest('div').find("input[id$='search_author_tie']").next('span').replaceWith("<span>" + ui.item.authorship_link + "</span>");
+
+                // triggers the save button
+                $this.closest('div').next('div').find('.author_save_btn').click();
+            }
+            })
+            .data( "autocomplete" )._renderItem = function( ul, item ) {
+                return $( "<li>" )
+                    .data("item.autocomplete", item )
+                    .append( "<a>" + item.label + "<br><em>" + item.email + "</em><br>" + item. institution + "</a>" )
+                    .appendTo(ul);
+                };
+            });
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/assets/javascripts/bibliography.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,36 @@
+// bibliography.js
+
+function disable_fields(){
+	$this = $(this);
+
+	$author_info = $this.closest('div').prev();
+//    $author_info.children('.description').toggle();
+	$author_info.find('p :input').attr("readonly", true);
+    $author_info.find('p :input').addClass('readonly');
+
+    // Always hides on save
+    $this.closest('div').prev().find('p.search_author_tie').hide();
+
+    $this.siblings('.author_edit_btn').show();
+    $this.hide();
+
+    return false;
+}
+
+function enable_fields(){
+    $this = $(this);
+
+    $author_info = $this.closest('div').prev();
+//    $author_info.children('.description').toggle();
+    $author_info.find('p :input').attr("readonly", false);
+    $author_info.find('p :input').removeClass('readonly');
+
+    // Always shows on edit
+    $this.closest('div').prev().find('p.search_author_tie').show();
+
+    $this.siblings('.author_save_btn').show();
+    $this.hide();
+
+    return false;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/assets/javascripts/bibtex.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,5 @@
+
+$('.bibtex-link').live("click", function() {
+  $this = $(this);
+  $this.closest('dd').next('dd').toggle();
+});
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/assets/javascripts/edit_publication.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,13 @@
+// edit_publication.js
+
+$(document).ready(function(){
+    // shows the correct bibtex fields
+    $('#publication_bibtex_entry_attributes_entry_type').trigger('change');
+
+    // adds the events to the edit/save authorship button
+    $('.author_save_btn').live('click', disable_fields);
+    $('.author_edit_btn').live('click', enable_fields);
+
+    // clicks all authorships
+    $('.author_save_btn').trigger('click');
+});
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/assets/javascripts/new_publication.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+// edit_publication.js
+
+$(document).ready(function(){
+    // adds the events to the edit/save authorship button
+    $('.author_save_btn').live('click', disable_fields);
+    $('.author_edit_btn').live('click', enable_fields);
+
+
+});
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/assets/stylesheets/bibliography.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,76 @@
+li .handle {
+    font-size: 12px;
+    cursor: move;
+    color: #777;
+}
+
+.readonly {
+    border: none;
+    padding: 0;
+    margin: 0;
+    background-color: transparent;
+}
+
+.tabular .author_identify label {
+    font-weight: normal;
+}
+
+.tabular .author_edit p {
+    padding-bottom: 0;
+}
+
+.tabular .author_edit .description {
+    font-style: italic;
+    font-size: small;
+}
+
+.publication_project {
+    margin-right: 18px;
+}
+
+div#bibliography dd { margin-bottom: 1em; font-size: 0.9em; }
+
+div#bibliography dd .authors { font-style: italic; }
+div#bibliography dd span.authors { color: #808080; }
+div#bibliography dd span.year { padding-left: 0.6em; }
+
+div#bibliography .box dt {
+  background: url(../../../images/document.png) no-repeat 0% 4px;
+  padding-left: 20px;
+  margin-left: 0;
+}
+div#bibliography .box dd {
+  padding-left: 20px;
+  margin-left: 0;
+}
+
+div#bibliography h3 {
+  background: url(../../../images/table_multiple.png) no-repeat 0% 50%;
+  padding-left: 20px;
+}
+
+div#bibliography textarea {
+  width: 90%;
+  height: 200px;
+  font: normal 8px;
+  padding: 2px 10px;
+  border: solid 1px #ddd;
+}
+
+input.author_search {width:100%}
+input.author_search {
+  background: url(../../../images/magnifier.png) no-repeat 2px 50%; padding-left:20px;
+  border:1px solid #9EB1C2; border-radius:3px; height:1.5em; width:95%;
+}
+input.author_search.ajax-loading {
+  background-image: url(../../../images/loading.gif);
+}
+
+.search_author_tie {
+  display: none;
+  float: left;
+}
+
+.author_edit_btn {
+  display: none;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/config/locales/en.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,154 @@
+# English strings go here for Rails i18n
+en:
+  project_module_redmine_bibliography: "Publications (references to papers related to the project)"
+
+  title: "Title"
+  authors: "Authors"
+  author: "Author"
+  name: "Name"
+  year: "Year"
+  associated_projects: "Associated Projects"
+  publications_box: "My Publications"
+  label_my_publications_box: "My Publications"
+  view_all_publications: "View All Project's Publications"
+  publications: Publications
+
+  identify_author_question: Is the right person selected above?
+  identify_author_yes: "Yes"
+  identify_author_correct: "Yes, but I need to correct some details below"
+  identify_author_no: "No, the author was not found in the search"
+
+  error_no_authors: "Please add at least one author to this publication."
+
+  label_all_publications: All Publications
+  label_all_publications_for_project: Publications associated with %{project}
+  label_authors_show: "Authorships associated with this author"
+  label_authors_index: "List of authors"
+
+  field_authorship_publication_title: "Publication"
+  field_authorship_name: "Name on Paper"
+  field_authorship_email: "Email"
+  field_authorship_institution: "Institution"
+
+  field_external_url: "External URL"
+  field_doi: "DOI"
+  field_publication_title: Title
+  field_publication_authors: Authors
+  field_publication_projects: "Associated projects"
+  field_author_name: Author
+  field_author_user: User Name
+  field_author_username: "Associated user"
+  field_author_publications: "Publications by this Author"
+  field_identify_author_yes: "Yes"
+  field_identify_author_correct: "Corrections"
+  field_identify_author_no: "No"
+
+  label_author_is_me: "(I am this author)"
+  label_add_me_as_author: "Add me as an author"
+  label_add_an_author: "Add an author"
+  label_add_another_author: "Add another author"
+  field_search_name: "Search by name"
+  field_search_results: ""
+  label_save_author: "Save author"
+  label_edit_author: "Edit author"
+  label_author_information: "Author Information"
+
+  remove_author: "Remove this author"
+
+  label_publication_plural: "Publications"
+  label_related_publication_plural: "Related publications"
+  label_publication_new: "Create New Publication"
+  label_publication_index: "List of Publication"
+  label_add_publication_to_project: "Add publication to this project"
+  label_publication_edit: "Edit Publication"
+  label_publication_show: "Publication Details"
+  label_add_project_to_publication: "Add this publication to a project"
+  label_project_search: "Find project by name: "
+  label_publication_project_index: "Projects associated with this publication"
+  label_publication_index: "View all publications"
+  label_publication_other_details: "Details"
+
+  text_external_url: "Link to the publication or to an external page about it."
+  text_doi: "DOI (Digital Object Identifier)."
+  text_author_name_on_paper: "Author's name as it appears on the paper."
+  text_author_institution: "Author's institution as on the paper."
+  text_author_email: "Author's email address as on the paper."
+
+  text_author_search: "Search existing authors"
+
+  # authorships model
+  field_institution: "Institution"
+  field_name_on_paper: "Name"
+  field_email: "Email Address"
+
+  # bibtex_entries model
+  field_entry_type: "Publication Type"
+  field_id: "id"
+  field_publication_id: "Publication_id"
+  field_address: "Address"
+  field_annote: "Annote"
+  field_booktitle: "Title of Book or Proceedings"
+  field_chapter: "Chapter"
+  field_crossref: "Cross Reference"
+  field_edition: "Edition"
+  field_editor: "Editor"
+  field_eprint: "eprint"
+  field_howpublished: "How was it Published"
+  field_journal: "Journal"
+  field_key: "Key"
+  field_month: "Month"
+  field_note: "Note"
+  field_number: "Number"
+  field_organization: "Organization"
+  field_pages: "Pages"
+  field_publisher: "Publisher"
+  field_school: "School"
+  field_series: "Series"
+  field_type: "Type"
+  field_url: "URL"
+  field_volume: "Volume"
+  field_year: "Year"
+
+  field_bibtex_article: Journal article
+  field_bibtex_book: Book
+  field_bibtex_booklet: Booklet
+  field_bibtex_conference: Article in conference proceedings
+  field_bibtex_inbook: Book chapter or part
+  field_bibtex_incollection: Part of a collection
+  field_bibtex_inproceedings: Article in conference proceedings
+  field_bibtex_manual: Technical manual
+  field_bibtex_masterthesis: "Master's thesis"
+  field_bibtex_misc: Other
+  field_bibtex_phdthesis: "PhD thesis"
+  field_bibtex_proceedings: Conference proceedings
+  field_bibtex_techreport: Technical report
+  field_bibtex_unpublished: Unpublished
+
+  label_author_1: First author
+  label_author_2: Second author
+  label_author_3: Third author
+  label_author_4: Fourth author
+  label_author_5: Fifth author
+  label_author_6: Sixth author
+  label_author_7: Seventh author
+  label_author_8: Eighth author
+  label_author_9: Ninth author
+  label_author_10: Tenth author
+  label_author_11: Eleventh author
+  label_author_12: Twelfth author
+  label_author_13: Thirteenth author
+  label_author_14: Fourteenth author
+  label_author_15: Fifteenth author
+  label_author_16: Sixteenth author
+  label_author_17: Seventeenth author
+  label_author_18: Eighteenth author
+  label_author_19: Nineteenth author
+  label_author_20: Twentieth author
+
+  mail_subject_publication_added: "You have been added as an author to a new publication"
+  mail_body_publication_added: "A new publication (%{publication}) has been added to the project '%{project}.'"
+
+  bibtex_link: "[B<small>IB</small>T<sub>E</sub>X]"
+  more_details_link: "[More Details]"
+  external_url_link: "[URL (ext.)]"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/config/routes.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,19 @@
+RedmineApp::Application.routes.draw do
+    match "publications/show_bibtex_fields", :to => 'publications#show_bibtex_fields', :via => "get"
+
+    match "publications/autocomplete_for_author", :to => 'publications#autocomplete_for_author', :via => "get"
+
+    match "authors/show/:id", :to => 'authors#show'
+
+    match "publications/add_project/:id", :to => 'publications#add_project'
+
+    match "publications/autocomplete_for_project", :to => 'publications#autocomplete_for_project'
+
+    resources :authorships do
+        collection do
+            post 'sort', :action => 'sort'
+        end
+    end
+
+    resources :publications
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/db/migrate/001_create_authors.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,12 @@
+class CreateAuthors < ActiveRecord::Migration
+  def self.up
+    create_table :authors do |t|
+      t.column :user_id, :integer
+      t.column :name, :string
+    end
+  end
+
+  def self.down
+    drop_table :authors
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/db/migrate/002_create_publications.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,12 @@
+class CreatePublications < ActiveRecord::Migration
+  def self.up
+    create_table :publications do |t|
+      t.column :title, :string
+      t.column :reviewed, :boolean, :default => false
+    end
+  end
+
+  def self.down
+    drop_table :publications
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/db/migrate/003_create_authorships.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,16 @@
+class CreateAuthorships < ActiveRecord::Migration
+  def self.up
+    create_table :authorships do |t|
+      t.column :author_id, :integer
+      t.column :publication_id, :integer
+      t.column :name_on_paper, :string
+      t.column :auth_order, :integer
+      t.column :institution, :string
+      t.column :email, :string
+    end
+  end
+
+  def self.down
+    drop_table :authorships
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/db/migrate/004_create_bibtex_entries.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,35 @@
+class CreateBibtexEntries < ActiveRecord::Migration
+  def self.up
+    create_table :bibtex_entries do |t|
+      t.column :publication_id, :integer
+      t.column :entry_type, :integer
+      t.column :address, :string
+      t.column :annote, :string
+      t.column :booktitle, :string
+      t.column :chapter, :string
+      t.column :crossref, :string
+      t.column :edition, :string
+      t.column :editor, :string
+      t.column :eprint, :string
+      t.column :howpublished, :string
+      t.column :journal, :string
+      t.column :key, :string
+      t.column :month, :string
+      t.column :note, :text
+      t.column :number, :string
+      t.column :organization, :string
+      t.column :pages, :string
+      t.column :publisher, :string
+      t.column :school, :string
+      t.column :series, :string
+      t.column :type, :string
+      t.column :url, :string
+      t.column :volume, :integer
+      t.column :year, :integer
+    end
+  end
+
+  def self.down
+    drop_table :bibtex_entries
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/db/migrate/005_create_projects_publications_join_table.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,12 @@
+class CreateProjectsPublicationsJoinTable < ActiveRecord::Migration
+  def self.up
+    create_table :projects_publications, :id => false do |t|
+      t.integer :project_id
+      t.integer :publication_id
+    end
+  end
+
+  def self.down
+    drop_table :projects_publications
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/db/migrate/006_create_bibtex_entry_types.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,13 @@
+class CreateBibtexEntryTypes < ActiveRecord::Migration
+  def self.up
+    create_table :bibtex_entry_types do |t|
+      t.string :name
+
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :bibtex_entry_types
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/db/migrate/007_add_external_url_column_to_publications.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+class AddExternalUrlColumnToPublications < ActiveRecord::Migration
+  def self.up
+    add_column :publications, :external_url, :string
+  end
+
+  def self.down
+    remove_column :publications, :external_url
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/db/migrate/008_add_doi_and_timestamp_columns_to_publications.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,12 @@
+class AddDoiAndTimestampColumnsToPublications < ActiveRecord::Migration
+  def self.up
+    add_column :publications, :doi, :string
+    add_timestamps :publications
+
+  end
+
+  def self.down
+    remove_column :publications, :doi
+    remove_timestamps :publications
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/db/migrate/009_add_timestamp_columns_to_authors.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+class AddTimestampColumnsToAuthors < ActiveRecord::Migration
+  def self.up
+    add_timestamps :authors
+  end
+
+  def self.down
+    remove_timestamps :authors
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/db/seed_data/bibtex_entry_types_list.txt	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,14 @@
+inproceedings
+conference
+article
+masterthesis
+phdthesis
+book
+booklet
+inbook
+incollection
+manual
+techreport
+proceedings
+unpublished
+misc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/init.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,64 @@
+require 'redmine'
+
+require 'bibtex'
+require 'citeproc'
+
+# Patches to the Redmine core.
+ActionDispatch::Callbacks.to_prepare do
+  require_dependency 'project'
+  require_dependency 'user'
+  require_dependency 'mailer'
+
+  unless Project.included_modules.include? Bibliography::ProjectPublicationsPatch
+    Project.send(:include, Bibliography::ProjectPublicationsPatch)
+  end
+
+  unless User.included_modules.include? Bibliography::UserAuthorPatch
+    User.send(:include, Bibliography::UserAuthorPatch)
+  end
+
+  unless Mailer.included_modules.include? Bibliography::MailerPatch
+    Mailer.send(:include, Bibliography::MailerPatch)
+  end
+
+  unless ProjectsHelper.included_modules.include?(Bibliography::ProjectsHelperPatch)
+    ProjectsHelper.send(:include, Bibliography::ProjectsHelperPatch)
+  end
+
+  unless MyHelper.included_modules.include?(Bibliography::MyHelperPatch)
+    MyHelper.send(:include, Bibliography::MyHelperPatch)
+  end
+end
+
+
+# Plugin Info
+Redmine::Plugin.register :redmine_bibliography do
+  name 'Redmine Bibliography plugin'
+  author 'Chris Cannam, Luis Figueira'
+  description 'This is a bibliography management plugin for Redmine'
+  version '0.0.1'
+  url 'http://example.com/path/to/plugin'
+  author_url 'http://example.com/about'
+
+  settings :default => { 'menu' => 'Publications' }, :partial => 'settings/bibliography'
+
+  project_module :redmine_bibliography do
+    permission :view_publication, {:publications => :show}, :public => :true
+    permission :publications, { :publications => :index }, :public => true
+    permission :edit_publication, {:publications => [:edit, :update]}
+    permission :add_publication, {:publications => [:new, :create]}
+    permission :delete_publication, {:publications => :destroy}
+
+
+  end
+
+  # extending the Project Menu
+  menu :project_menu, :publications, { :controller => 'publications', :action => 'index', :path => nil }, :after => :activity, :param => :project_id, :caption => Proc.new { Setting.plugin_redmine_bibliography['menu'] },
+   :if => Proc.new { !Setting.plugin_redmine_bibliography['menu'].blank? }
+
+  activity_provider :publication, :class_name => 'Publication', :default => true
+
+end
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/lang/en.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,2 @@
+# English strings go here
+my_label: "My label"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/lib/bibliography/mailer_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,30 @@
+require_dependency 'mailer'
+
+module Bibliography
+  module MailerPatch
+      def self.included(base) # :nodoc:
+
+        # Builds a tmail object used to email the specified user that a publication was created and the user is
+        # an author of that publication
+        #
+        # Example:
+        #   publication_added(user) => tmail object
+        #   Mailer.deliver_add_to_project(user) => sends an email to the registered user
+        def publication_added(user, publication, project)
+
+          @publication = publication
+          @project = project
+
+          set_language_if_valid user.language
+
+          mail :to => user.mail,
+          :subject => l(:mail_subject_publication_added, Setting.app_title)
+
+          @publication_url = url_for( :controller => 'publications', :action => 'show', :id => publication.id )
+          @publication_title = publication.title
+        end
+
+
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/lib/bibliography/my_helper_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,52 @@
+module Bibliography
+  module MyHelperPatch
+
+    def self.included(base) # :nodoc:
+      base.send(:include, InstanceMethods)
+
+      base.class_eval do
+        unloadable
+      end
+    end
+
+    module InstanceMethods
+
+      def get_my_publications()
+        if not User.current.author.nil?
+          @my_publications = Publication.all(:include => :authors, :conditions => "authors.id = #{User.current.author.id}")
+        else
+          @my_publications = []
+        end
+      end 
+
+      def render_publications_projects(publication)    
+        s = ""
+        projs = []
+        
+        publication.projects.each do |proj|
+          projs << link_to(proj.name, proj)
+        end
+        
+        s << projs.join(', ')
+        
+        s.html_safe
+      end
+
+      def render_publications_authors(publication)    
+        s = ""
+        auths = []
+        
+        publication.authorships.each do |auth|
+          auths << h(auth.name_on_paper)
+        end
+        
+        s << auths.join(', ')
+
+        s.html_safe
+      end
+
+
+    end
+  end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/lib/bibliography/project_publications_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,25 @@
+require_dependency 'project'
+
+module Bibliography
+  module ProjectPublicationsPatch
+    def self.included(base)
+          base.class_eval do
+            has_and_belongs_to_many :publications, :uniq => true
+
+            scope :name_or_homepage_like, lambda {|q|
+              s = "%#{q.to_s.strip.downcase}%"
+              {:conditions => ["LOWER(name) LIKE :s OR LOWER(homepage) LIKE :s", {:s => s}],
+               :order => 'name'
+              }
+            }
+          end
+    end #self.included
+
+    module ProjectMethods
+
+
+
+
+    end #ProjectMethods
+  end #ProjectPublicationsPatch
+end #RedmineBibliography
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/lib/bibliography/projects_helper_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,17 @@
+module Bibliography
+  module ProjectsHelperPatch
+
+    def self.included(base) # :nodoc:
+      base.send(:include, InstanceMethods)
+      base.send(:include, PublicationsHelper)
+
+      base.class_eval do
+        unloadable
+      end
+    end
+
+    module InstanceMethods
+    end
+  end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/lib/bibliography/user_author_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,47 @@
+require_dependency 'user'
+
+module Bibliography
+  module UserAuthorPatch
+    def self.included(base)
+      base.send(:include, InstanceMethods)
+
+      base.class_eval do
+        # adapted from the app/models/principals_model.rb
+        # to remove the email address from the search
+        scope :name_like, lambda {|q|
+          q = q.to_s
+          if q.blank?
+            where({})
+          else
+            pattern = "%#{q}%"
+            sql = %w(login firstname lastname).map {|column| "LOWER(#{  table_name}.    #{column}) LIKE LOWER(:p)"}.join(" OR ")
+            params = {:p => pattern}
+            if q =~ /^(.+)\s+(.+)$/
+              a, b = "#{$1}%", "#{$2}%"
+              sql << " OR (LOWER(#{table_name}.firstname) LIKE LOWER(:a) AND  LOWER    (#{table_name}.lastname) LIKE LOWER(:b))"
+              sql << " OR (LOWER(#{table_name}.firstname) LIKE LOWER(:b) AND  LOWER    (#{table_name}.lastname) LIKE LOWER(:a))"
+              params.merge!(:a => a, :b => b)
+            end
+          where(sql, params)
+          end
+        }
+      end #base.class_eval
+
+    end #self.included
+
+    module InstanceMethods
+
+      # todo: deprecated? ~lf.20131011
+      def institution
+        unless self.ssamr_user_detail.nil?
+          institution_name = self.ssamr_user_detail.institution_name
+        else
+          institution_name = "No Institution Set"
+        end
+        return institution_name
+      end
+
+    end #InstanceMethods
+
+  end #UserPublicationsPatch
+end #RedmineBibliography
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/lib/tasks/seed_bibtex_entry_types.rake	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,20 @@
+namespace :redmine do
+  namespace :plugins do
+    namespace :redmine_bibliography do
+
+      task :seed_bibtex_entry_types  => :environment do    
+        desc "Seeds the Bibtex Entry Types Table"
+  
+        quoted = ActiveRecord::Base.connection.quote_table_name('bibtex_entry_types')
+        ActiveRecord::Base.connection.execute("TRUNCATE #{quoted}")
+
+        open(File.dirname(__FILE__) + "/../../db/seed_data/bibtex_entry_types_list.txt") do |bibtex_entry_types|
+          bibtex_entry_types.read.each_line do |bibtex_entry_type|
+            BibtexEntryType.create(:name => bibtex_entry_type.chomp)
+          end
+        end
+      end 
+
+    end 
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/fixtures/authors.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,18 @@
+# authors.yml
+---
+one:
+  id: 1
+  user_id: 1
+  name:
+two:
+  id: 2
+  user_id:
+  name:
+three:
+  id: 3
+  user_id:
+  name:
+four:
+  id: 4
+  user_id:
+  name:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/fixtures/authorships.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,42 @@
+# authorships.yml
+---
+one:
+  id: 1
+  author_id: 1
+  publication_id: 1
+  name_on_paper: Yih-Farn R. Chen
+  auth_order: 1
+  institution: Imperial College London
+  email: chen@imperial.ac.uk
+two:
+  id: 2
+  author_id: 2
+  publication_id: 1
+  name_on_paper: Glenn S. Fowler
+  auth_order: 2
+  institution:
+  email: MyString
+three:
+  id: 3
+  author_id: 3
+  publication_id: 1
+  name_on_paper: Jackie Brown
+  auth_order: 1
+  institution:
+  email: j.brown@m.com
+four:
+  id: 4
+  author_id: 4
+  publication_id: 1
+  name_on_paper: Captain Boomtime
+  auth_order: 2
+  institution:
+  email: cpt.boom@time.co.uk
+five:
+  id: 5
+  author_id: 1
+  publication_id: 2
+  name_on_paper: Yih-Farn Chen
+  auth_order: 1
+  institution: "Imperial College, London"
+  email: yfc@gmail.com
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/fixtures/bibtex_entries.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,7 @@
+# bibtex_entries.yml
+---
+one:
+  id: 1
+  entry_type: InProceedings
+  booktitle: International Conference on Software Maintenance
+  year: 1995
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/fixtures/publications.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,13 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+one:
+    id: 1
+    title: Publication Number 1
+    reviewed: true
+    external_url:
+    doi:
+two:
+    id: 2
+    title: Publication Number 2
+    reviewed: false
+    external_url:
+    doi:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/fixtures/users.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,167 @@
+# users.yml
+---
+users_004:
+  created_on: 2006-07-19 19:34:07 +02:00
+  status: 1
+  last_login_on:
+  language: en
+  # password = foo
+  salt: 3126f764c3c5ac61cbfc103f25f934cf
+  hashed_password: 9e4dd7eeb172c12a0691a6d9d3a269f7e9fe671b
+  updated_on: 2006-07-19 19:34:07 +02:00
+  admin: false
+  mail: rhill@somenet.foo
+  lastname: Hill
+  firstname: Robert
+  id: 4
+  auth_source_id:
+  mail_notification: all
+  login: rhill
+  type: User
+users_001:
+  created_on: 2006-07-19 19:12:21 +02:00
+  status: 1
+  last_login_on: 2006-07-19 22:57:52 +02:00
+  language: en
+  # password = admin
+  salt: 82090c953c4a0000a7db253b0691a6b4
+  hashed_password: b5b6ff9543bf1387374cdfa27a54c96d236a7150
+  updated_on: 2006-07-19 22:57:52 +02:00
+  admin: true
+  mail: admin@somenet.foo
+  lastname: Admin
+  firstname: redMine
+  id: 1
+  auth_source_id:
+  mail_notification: all
+  login: admin
+  type: User
+users_002:
+  created_on: 2006-07-19 19:32:09 +02:00
+  status: 1
+  last_login_on: 2006-07-19 22:42:15 +02:00
+  language: en
+  # password = jsmith
+  salt: 67eb4732624d5a7753dcea7ce0bb7d7d
+  hashed_password: bfbe06043353a677d0215b26a5800d128d5413bc
+  updated_on: 2006-07-19 22:42:15 +02:00
+  admin: false
+  mail: jsmith@somenet.foo
+  lastname: Smith
+  firstname: John
+  id: 2
+  auth_source_id:
+  mail_notification: all
+  login: jsmith
+  type: User
+users_003:
+  created_on: 2006-07-19 19:33:19 +02:00
+  status: 1
+  last_login_on:
+  language: en
+  # password = foo
+  salt: 7599f9963ec07b5a3b55b354407120c0
+  hashed_password: 8f659c8d7c072f189374edacfa90d6abbc26d8ed
+  updated_on: 2006-07-19 19:33:19 +02:00
+  admin: false
+  mail: dlopper@somenet.foo
+  lastname: Lopper
+  firstname: Dave
+  id: 3
+  auth_source_id:
+  mail_notification: all
+  login: dlopper
+  type: User
+users_005:
+  id: 5
+  created_on: 2006-07-19 19:33:19 +02:00
+  # Locked
+  status: 3
+  last_login_on:
+  language: en
+  hashed_password: 1
+  updated_on: 2006-07-19 19:33:19 +02:00
+  admin: false
+  mail: dlopper2@somenet.foo
+  lastname: Lopper2
+  firstname: Dave2
+  auth_source_id:
+  mail_notification: all
+  login: dlopper2
+  type: User
+users_006:
+  id: 6
+  created_on: 2006-07-19 19:33:19 +02:00
+  status: 0
+  last_login_on:
+  language: ''
+  hashed_password: 1
+  updated_on: 2006-07-19 19:33:19 +02:00
+  admin: false
+  mail: ''
+  lastname: Anonymous
+  firstname: ''
+  auth_source_id:
+  mail_notification: only_my_events
+  login: ''
+  type: AnonymousUser
+users_007:
+  id: 7
+  created_on: 2006-07-19 19:33:19 +02:00
+  status: 1
+  last_login_on:
+  language: ''
+  hashed_password: 1
+  updated_on: 2006-07-19 19:33:19 +02:00
+  admin: false
+  mail: someone@foo.bar
+  lastname: One
+  firstname: Some
+  auth_source_id:
+  mail_notification: only_my_events
+  login: someone
+  type: User
+users_008:
+  id: 8
+  created_on: 2006-07-19 19:33:19 +02:00
+  status: 1
+  last_login_on:
+  language: 'it'
+  # password = foo
+  salt: 7599f9963ec07b5a3b55b354407120c0
+  hashed_password: 8f659c8d7c072f189374edacfa90d6abbc26d8ed
+  updated_on: 2006-07-19 19:33:19 +02:00
+  admin: false
+  mail: miscuser8@foo.bar
+  lastname: Misc
+  firstname: User
+  auth_source_id:
+  mail_notification: only_my_events
+  login: miscuser8
+  type: User
+users_009:
+  id: 9
+  created_on: 2006-07-19 19:33:19 +02:00
+  status: 1
+  last_login_on:
+  language: 'it'
+  hashed_password: 1
+  updated_on: 2006-07-19 19:33:19 +02:00
+  admin: false
+  mail: miscuser9@foo.bar
+  lastname: Misc
+  firstname: User
+  auth_source_id:
+  mail_notification: only_my_events
+  login: miscuser9
+  type: User
+groups_010:
+  id: 10
+  lastname: A Team
+  type: Group
+groups_011:
+  id: 11
+  lastname: B Team
+  type: Group
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/functional/authors_controller_test.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,13 @@
+# authors_controller_test.rb
+
+require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
+
+class AuthorsControllerTest < ActionController::TestCase
+  self.fixture_path = File.dirname(__FILE__) + "/../fixtures/"
+  fixtures :users, :authors, :authorships
+
+  def test_truth
+    assert true
+  end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/functional/authorships_controller_test.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,8 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class AuthorshipsControllerTest < ActionController::TestCase
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/functional/publications_controller_test.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,8 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class PublicationsControllerTest < ActionController::TestCase
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/test_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,27 @@
+ENV['RAILS_ENV'] ||= 'test'
+
+# Load the normal Rails helper
+require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper')
+require File.expand_path(File.dirname(__FILE__) + '/../app/controllers/publications_controller')
+
+class BibliographyControllerTest < ActionController::TestCase
+  self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
+
+  fixtures :authors
+
+  def setup
+
+  end
+
+  def test_truth
+    assert true
+  end
+
+  # def test_routing
+  #   assert_routing(
+  #         {:method => :get, :path => '/requirements'},
+  #         :controller => 'requirements', :action => 'index'
+  #       )
+  # end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/unit/author_test.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,17 @@
+# author_test.rb
+
+require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
+
+class AuthorTest < ActiveSupport::TestCase
+    self.fixture_path = File.dirname(__FILE__) + "/../fixtures/"
+    fixtures :users, :authors, :authorships
+
+    def test_relationships
+        author = Author.find(1)
+
+        assert_equal(author.authorships.first.name_on_paper, "Yih-Farn R. Chen")
+        assert_equal(author.authorships.count, 2)
+
+    end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/unit/authorship_test.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,16 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class AuthorshipTest < ActiveSupport::TestCase
+  fixtures :authorships
+
+  # Replace this with your real tests.
+  def test_truth
+        luis = Author.first
+
+
+    assert true
+  end
+
+
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/unit/bibtex_entry_test.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class BibtexEntryTest < ActiveSupport::TestCase
+  fixtures :bibtex_entries
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_bibliography/test/unit/publication_test.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,31 @@
+# publication_test
+
+require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
+
+class PublicationTest < ActiveSupport::TestCase
+    self.fixture_path = File.dirname(__FILE__) + "/../fixtures/"
+
+    fixtures :publications, :authorships
+
+    # Replace this with your real tests.
+    def test_truth
+        assert true
+    end
+
+    def test_relationships
+        # test authorships - publication relationship
+        publication = Publication.find(1)
+
+        assert 4, publication.authorships.count
+    end
+
+    def test_new_publication_validations
+        pub = Publication.create
+
+        assert !pub.valid?, "!pub.valid?"
+        assert_equal 2, pub.errors.count, "Number of errors"
+        assert_equal ["can't be blank"], pub.errors[:title]
+        assert_equal ["Please add at least one author to this publication."], pub.errors[:authorships]
+    end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/README.rdoc	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,43 @@
+= Redmine Checkout plugin
+
+Author:: Holger Just
+URL:: http://dev.holgerjust.de/projects/redmine-checkout
+
+This plugin to Redmine adds a link to the actual repository to the GUI.
+
+This plugin includes ZeroClipboard[http://code.google.com/p/zeroclipboard/]
+by Joseph Huckaby. This software is licensed under the
+{GNU Lesser General Public License}[http://www.gnu.org/licenses/lgpl.html].
+
+Copyright (c) 2009, 2010 Holger Just
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+= Installation
+
+The installation follows the standard installation path from
+http://www.redmine.org/projects/redmine/wiki/Plugins
+
+1. Copy the software to the vendor/plugins directory. Make sure that the name
+   of the directory is redmine_checkout.
+2. Run rake db:migrate_plugins RAILS_ENV=production
+3. Restart Redmine
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/Rakefile	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+require 'redmine_plugin_support'
+ 
+Dir[File.expand_path(File.dirname(__FILE__)) + "/lib/tasks/**/*.rake"].sort.each { |ext| load ext }
+ 
+RedminePluginSupport::Base.setup do |plugin|
+  plugin.project_name = 'redmine_checkout'
+  plugin.default_task = [:spec]
+  plugin.tasks = [:doc, :release, :clean, :spec, :stats]
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/app/views/projects/settings/_repository_checkout.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,54 @@
+<p><%= form.select(:checkout_overwrite, [
+    [l(:general_text_Yes), "1"],
+    [l(:general_text_No), "0"]
+  ],
+  {},
+  :onchange => <<-EOF
+    Effect.toggle($('checkout_settings'), 'slide', {duration:0.2});
+  EOF
+  )%></p>
+
+<div id="checkout_settings" <%= 'style="display:none;"' unless form.object.checkout_overwrite? %>><fieldset>
+  <legend><%=l :label_checkout %></legend>
+  
+  <p><%= form.text_area :checkout_description, :cols => 60, :rows => 5, :class => 'wiki-edit', :label => :field_description %></p>
+  <%= wikitoolbar_for 'repository_checkout_description' %>
+  
+  <% if form.object.scm_name == 'Subversion' %>
+  <p><%= form.select :checkout_display_login,[
+            [l(:label_display_login_none), ''],
+            [l(:label_display_login_username), 'username'],
+            [l(:label_display_login_password), 'password']
+          ],
+          :label => :setting_checkout_display_login %></p>
+  <% end %>
+  
+  <p><%= form.check_box :checkout_display_command %></p>
+  
+  <% javascript_tag do %>
+    protocolForm = new Subform(
+      '<%= escape_javascript(render(:partial => "projects/settings/repository_checkout_protocol", :locals => {:protocol => Checkout::Protocol.new({:protocol => form.object.scm_name, :append_path => form.object.allow_subtree_checkout? ? 1: 0, :repository => form.object})})) %>',
+      <%= form.object.checkout_protocols.length %>,
+      'checkout_protocol_table'
+    );
+  <% end %>
+  <p><label><%=l :label_protocol_plural %></label><%=l :help_repository_checkout_protocols %></p>
+  <%= hidden_field_tag 'repository[checkout_protocols][-1][protocol]', 'empty' %>
+  <table class="list checkout_protocol_table">
+    <thead><tr>
+      <th class="protocol_protocol"   ><%= l(:setting_protocol)%></th>
+      <th class="protocol_command"    ><%= l(:setting_checkout_command)%></th>
+      <th class="protocol_fixed_url"  ><%= l(:setting_checkout_fixed_url) %></th>
+      <th class="protocol_access"     ><%= l(:label_permissions) %></th>
+      <th class="protocol_append_path"><%= l(:label_append_path) %></th>
+      <th class="protocol_is_default" ><%= l(:label_default) %></th>
+      <th class="protocol_delete"     ></th>
+    </tr></thead>
+    <tbody id="checkout_protocol_table">
+      <% form.object.checkout_protocols.each_with_index do |protocol, index| %>
+        <%= render :partial => 'projects/settings/repository_checkout_protocol', :locals => {:protocol => protocol, :index => index, :classes => cycle('odd', 'even')} %>
+      <% end %>
+    </tbody>
+  </table>
+  <div style="text-align: right"><%= link_to_function l(:button_add_protocol), "protocolForm.add()", {:class => "icon icon-add"} %></div>
+</fieldset></div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/app/views/projects/settings/_repository_checkout_protocol.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,18 @@
+<%
+  index ||= "--INDEX--"
+  classes ||= ""
+  
+  protocol = Checkout::Protocol.new(protocol) unless protocol.is_a? Checkout::Protocol
+%>
+<tr id="<%= "checkout_protocols_#{index}" %>" class="<%= classes %>" <%= 'style="display:none"' if index == '--INDEX--' %>>
+  <td class="protocol_protocol"><%= text_field_tag "repository[checkout_protocols][#{index}][protocol]", protocol.protocol, :size => 10 %></td>
+  <td class="protocol_command"><%= text_field_tag "repository[checkout_protocols][#{index}][command]", protocol.command, :size => 15 %></td>
+  <td class="protocol_fixed_url"><%= text_field_tag "repository[checkout_protocols][#{index}][fixed_url]", protocol.fixed_url, :size => 60 %></td>
+  <td class="protocol_access"><%= select_tag "repository[checkout_protocols][#{index}][access]", options_for_select([
+    [l(:label_access_read_write), 'read+write'],
+    [l(:label_access_read_only), 'read-only'],
+    [l(:label_access_permission), 'permission']], protocol.access) %></td>
+  <td class="protocol_append_path"><%= check_box_tag "repository[checkout_protocols][#{index}][append_path]", 1, protocol.append_path? %></td>
+  <td class="protocol_is_default"><%= check_box_tag "repository[checkout_protocols][#{index}][is_default]", 1, protocol.default? %></td>
+  <td class="protocol_delete"><%= image_to_function 'delete.png', "var e=$('checkout_protocols_#{index}');var parent=e.up(\"tbody\");e.remove();recalculate_even_odd(parent);return false" %></td>
+</tr>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/app/views/redmine_checkout_hooks/_view_repositories_show_contextual.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,46 @@
+
+<% content_for :header_tags do %>
+  <%= stylesheet_link_tag 'checkout', :plugin => 'redmine_checkout' %>
+<% end %>
+
+<div class="repository-info">
+  <% if repository.checkout_description.present? %>
+  <div class="wiki<%= ' bottomline' if protocols.present? %>"><%= textilizable repository.checkout_description %></div>
+  <% end %>
+  <% if protocols.present? %>
+  <div id="checkout_box">
+    <ul id="checkout_protocols">
+    <% protocols.each do |p| -%>
+      <li>
+        <a <%= 'class="selected"' if p == default_protocol %> id="checkout_protocol_<%= p.protocol.to_s.underscore %>" data-permission="<%= p.access_rw(User.current) %>" href="<%= URI.escape p.url(checkout_path) %>"><%=h p.protocol %></a>
+      </li>
+    <% end -%>
+    </ul>
+    <%= text_field_tag :checkout_url, h(default_protocol.full_command(checkout_path)), :readonly => true %>
+
+    <p>
+    <% if User.current.logged? %>
+      <% if repository.is_external? %>
+      <%= (l :label_access_type_all, :type => l(:label_access_read_only)).html_safe %>
+      <% else %>
+      <% if default_protocol %><%= (l :label_access_type, :type => l(default_protocol.access_label(User.current))).html_safe %><% end %>
+      <% end %>
+    <% else %>
+      &nbsp;
+    <% end %>
+    </p>
+
+    <% javascript_tag do %>
+      var checkout_access   = $H({<%= protocols.inject([]){|r,p| r << "'checkout_protocol_#{p.protocol.to_s.underscore}': '#{l(p.access_label(User.current))}'"}.join(', ') %>});
+      var checkout_commands = $H({<%= protocols.inject([]){|r,p| r << "'checkout_protocol_#{p.protocol.to_s.underscore}': '#{escape_javascript(p.full_command(checkout_path))}'"}.join(', ') %>});
+    <% end %>
+
+  </div>
+  <% end%>
+  <% if repository.is_external? %>
+    <div style="clear: left">
+    </div>
+    <p class="topline" style="padding-top: 1em"><%= (l(:text_repository_external, :location => repository.external_url)).html_safe %></p>
+  <% end %>
+</div>
+<div style="clear: left"></div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/app/views/settings/_checkout.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,43 @@
+<% form_tag({:action => 'edit', :tab => 'checkout'}) do %>
+
+<% javascript_tag do %>
+protocolForms = $H();
+
+document.observe("dom:loaded", function() {
+  $('tab-content-checkout').select('fieldset.collapsed').each(function(e){
+    e.down('div').hide();
+  });
+  <%
+    CheckoutHelper.supported_scm.select{|scm| Setting.enabled_scm.include?(scm)}.each do |scm| 
+      next if Setting.send("checkout_overwrite_description_#{scm}?")
+  -%>
+  $('settings_checkout_description_<%= scm %>').up('div').up('div').hide();
+  <%- end %>
+});
+<% end %>
+
+
+<div class="box tabular settings">
+<p><%= setting_check_box :checkout_display_checkout_info %></p>
+
+<p><%= setting_text_area :checkout_description_Abstract, :cols => 60, :rows => 5, :class => 'wiki-edit', :label => :field_description %></p>
+<%= wikitoolbar_for 'settings_checkout_description_Abstract' %>
+
+<p><%= setting_check_box :checkout_use_zero_clipboard %></p>
+
+<% CheckoutHelper.supported_scm.select{|scm| Setting.enabled_scm.include?(scm)}.each do |scm| -%>
+<fieldset class="collapsible collapsed">
+  <legend onclick="toggleFieldset(this);"><%= "Repository::#{scm}".constantize.scm_name %></legend>
+  <div><%= render :partial => 'checkout_scm', :locals => {:scm => scm} %></div>
+</fieldset>
+<%- end %>
+
+</div>
+
+<%= submit_tag l(:button_save) %>
+<%- end %>
+
+<% content_for :header_tags do %>
+  <%= javascript_include_tag 'subform', :plugin => 'redmine_checkout' %>
+  <%= stylesheet_link_tag 'checkout', :plugin => 'redmine_checkout' %>
+<% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/app/views/settings/_checkout_protocol.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,19 @@
+<%
+  index ||= "--INDEX--"
+  classes ||= ""
+  
+  protocol = Checkout::Protocol.new(protocol) unless protocol.is_a? Checkout::Protocol
+%>
+<tr id="<%= "checkout_protocols_#{scm}_#{index}" %>" class="<%= classes %>" <%= 'style="display:none"' if index == '--INDEX--' %>>
+  <td class="protocol_protocol"         ><%= text_field_tag "settings[checkout_protocols_#{scm}][#{index}][protocol]", protocol.protocol, :size => 10 %></td>
+  <td class="protocol_command"          ><%= text_field_tag "settings[checkout_protocols_#{scm}][#{index}][command]", protocol.command, :size => 15 %></td>
+  <td class="protocol_regex"            ><%= text_field_tag "settings[checkout_protocols_#{scm}][#{index}][regex]", protocol.regex, :size => 30 %></td>
+  <td class="protocol_regex_replacement"><%= text_field_tag "settings[checkout_protocols_#{scm}][#{index}][regex_replacement]", protocol.regex_replacement, :size => 30 %></td>
+  <td class="protocol_access"           ><%= select_tag "settings[checkout_protocols_#{scm}][#{index}][access]", options_for_select([
+    [l(:label_access_read_write), 'read+write'],
+    [l(:label_access_read_only), 'read-only'],
+    [l(:label_access_permission), 'permission']], protocol.access) %></td>
+  <td class="protocol_append_path"><%= check_box_tag "settings[checkout_protocols_#{scm}][#{index}][append_path]", 1, protocol.append_path? %></td>
+  <td class="protocol_is_default"><%= check_box_tag "settings[checkout_protocols_#{scm}][#{index}][is_default]", 1, protocol.default? %></td>
+  <td class="protocol_delete"><%= image_to_function 'delete.png', "var e=$('checkout_protocols_#{scm}_#{index}');var parent=e.up(\"tbody\");e.remove();recalculate_even_odd(parent);return false" %></td>
+</tr>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/app/views/settings/_checkout_scm.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,46 @@
+<div>
+  <p><%= setting_check_box "checkout_overwrite_description_#{scm}", :label => :setting_checkout_overwrite_description, :onclick => <<-EOF
+    Effect.toggle($('settings_checkout_description_#{scm}').up("div").up("div"), 'slide', {duration:0.2});
+  EOF
+  %></p>
+  
+  <div>
+    <p><%= setting_text_area "checkout_description_#{scm}", :cols => 60, :rows => 5, :class => 'wiki-edit', :label => :field_description %></p>
+    <%= wikitoolbar_for "settings_checkout_description_#{scm}" %>
+  </div>
+  
+  <% if scm == 'Subversion' %>
+  <p><%= setting_select "checkout_display_login",[
+            [l(:label_display_login_username), 'username'],
+            [l(:label_display_login_password), 'password']
+          ],
+          :blank => :label_display_login_none %></p>
+  <% end %>
+  
+  <p><%= setting_check_box "checkout_display_command_#{scm}", :label => :field_checkout_display_command %></p>
+
+  <% javascript_tag do %>
+    <% repo = "Repository::#{scm}".constantize %>
+    var subform = new Subform('<%= escape_javascript(render(:partial => "checkout_protocol", :locals => {:protocol => Checkout::Protocol.new({:protocol => repo.scm_name, :append_path => (repo.allow_subtree_checkout? ? '1' : '0'), :command => repo.checkout_default_command}), :scm => scm})) %>',<%= Setting.send("checkout_protocols_#{scm}").length %>,'settings_checkout_protocols_<%= scm %>');
+    protocolForms.set('<%= scm %>', subform);
+  <% end %>
+  <p><label><%=l :label_protocol_plural %></label><%=l :help_checkout_protocols %></p>
+  <table class="list checkout_protocol_table">
+    <thead><tr>
+      <th class="protocol_protocol"         ><%= l(:setting_protocol)%></th>
+      <th class="protocol_command"          ><%= l(:setting_checkout_command)%></th>
+      <th class="protocol_regex"            ><%= l(:setting_checkout_url_regex) %></th>
+      <th class="protocol_regex_replacement"><%= l(:setting_checkout_url_regex_replacement) %></th>
+      <th class="protocol_access"           ><%= l(:label_permissions) %></th>
+      <th class="protocol_append_path"      ><%= l(:label_append_path) %></th>
+      <th class="protocol_is_default"       ><%= l(:label_default) %></th>
+      <th class="protocol_delete"           ></th>
+    </tr></thead>
+    <tbody id="settings_checkout_protocols_<%= scm %>">
+      <% Setting.send("checkout_protocols_#{scm}").each_with_index do |protocol, index| %>
+        <%= render :partial => 'checkout_protocol', :locals => {:protocol => Checkout::Protocol.new(protocol), :scm => scm, :index => index, :classes => cycle('odd', 'even')} %>
+      <% end %>
+    </tbody>
+  </table>
+  <div style="text-align: right"><%= link_to_function l(:button_add_protocol), "protocolForms.get('#{scm}').add()", {:class => "icon icon-add"} %></div>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/app/views/settings/_redmine_checkout.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1 @@
+<%=l(:help_moved_settings, :link => link_to(l(:label_settings_location), {:controller => 'settings', :action => 'index', :tab => 'checkout'})) %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/assets/images/ZeroClipboard.as	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,81 @@
+package {
+	// Simple Set Clipboard System
+	// Author: Joseph Huckaby
+	
+	import flash.display.Stage;
+	import flash.display.Sprite;
+	import flash.display.LoaderInfo;
+	import flash.display.StageScaleMode;
+	import flash.events.*;
+	import flash.display.StageAlign;
+	import flash.display.StageScaleMode;
+	import flash.external.ExternalInterface;
+	import flash.system.Security;
+	import flash.utils.*;
+	import flash.system.System;
+ 
+	public class ZeroClipboard extends Sprite {
+		
+		private var id:String = '';
+		private var button:Sprite;
+		private var clipText:String = '';
+		
+		public function ZeroClipboard() {
+			// constructor, setup event listeners and external interfaces
+			stage.scaleMode = StageScaleMode.EXACT_FIT;
+			flash.system.Security.allowDomain("*");
+			
+			// import flashvars
+			var flashvars:Object = LoaderInfo( this.root.loaderInfo ).parameters;
+			id = flashvars.id;
+			
+			// invisible button covers entire stage
+			button = new Sprite();
+			button.buttonMode = true;
+			button.useHandCursor = true;
+			button.graphics.beginFill(0xCCFF00);
+			button.graphics.drawRect(0, 0, Math.floor(flashvars.width), Math.floor(flashvars.height));
+			button.alpha = 0.0;
+			addChild(button);
+			button.addEventListener(MouseEvent.CLICK, clickHandler);
+			
+			button.addEventListener(MouseEvent.MOUSE_OVER, function(event:Event) {
+				ExternalInterface.call( 'ZeroClipboard.dispatch', id, 'mouseOver', null );
+			} );
+			button.addEventListener(MouseEvent.MOUSE_OUT, function(event:Event) {
+				ExternalInterface.call( 'ZeroClipboard.dispatch', id, 'mouseOut', null );
+			} );
+			button.addEventListener(MouseEvent.MOUSE_DOWN, function(event:Event) {
+				ExternalInterface.call( 'ZeroClipboard.dispatch', id, 'mouseDown', null );
+			} );
+			button.addEventListener(MouseEvent.MOUSE_UP, function(event:Event) {
+				ExternalInterface.call( 'ZeroClipboard.dispatch', id, 'mouseUp', null );
+			} );
+			
+			// external functions
+			ExternalInterface.addCallback("setHandCursor", setHandCursor);
+			ExternalInterface.addCallback("setText", setText);
+			
+			// signal to the browser that we are ready
+			ExternalInterface.call( 'ZeroClipboard.dispatch', id, 'load', null );
+		}
+		
+		public function setText(newText) {
+			// set the maximum number of files allowed
+			clipText = newText;
+		}
+		
+		public function setHandCursor(enabled:Boolean) {
+			// control whether the hand cursor is shown on rollover (true)
+			// or the default arrow cursor (false)
+			button.useHandCursor = enabled;
+		}
+		
+		private function clickHandler(event:Event):void {
+			// user click copies text to clipboard
+			// as of flash player 10, this MUST happen from an in-movie flash click event
+			System.setClipboard( clipText );
+			ExternalInterface.call( 'ZeroClipboard.dispatch', id, 'complete', clipText );
+		}
+	}
+}
Binary file plugins/redmine_checkout/assets/images/ZeroClipboard.swf has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/assets/images/button.svg	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,11 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="gradient" x1="100%" y1="100%">
+      <stop offset="0%" style="stop-color:#ddd; stop-opacity:1" />
+      <stop offset="100%" style="stop-color:#f8f8f8; stop-opacity:1" />
+    </linearGradient>
+  </defs>
+  <rect width="100%" height="100%" style="fill:url(#gradient)"/>
+</svg>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/assets/images/button_focus.svg	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,11 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="gradient" x1="100%" y1="100%">
+      <stop offset="0%" style="stop-color:#507AAA; stop-opacity:1" />
+      <stop offset="100%" style="stop-color:#759fcf; stop-opacity:1" />
+    </linearGradient>
+  </defs>
+  <rect width="100%" height="100%" style="fill:url(#gradient)"/>
+</svg>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/assets/images/button_selected.svg	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,11 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <linearGradient id="gradient" x1="100%" y1="100%">
+      <stop offset="0%" style="stop-color:#aaa; stop-opacity:1" />
+      <stop offset="100%" style="stop-color:#ccc; stop-opacity:1" />
+    </linearGradient>
+  </defs>
+  <rect width="100%" height="100%" style="fill:url(#gradient)"/>
+</svg>
\ No newline at end of file
Binary file plugins/redmine_checkout/assets/images/paste.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/assets/javascripts/ZeroClipboard.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,311 @@
+// Simple Set Clipboard System
+// Author: Joseph Huckaby
+
+var ZeroClipboard = {
+	
+	version: "1.0.7",
+	clients: {}, // registered upload clients on page, indexed by id
+	moviePath: 'ZeroClipboard.swf', // URL to movie
+	nextId: 1, // ID of next movie
+	
+	$: function(thingy) {
+		// simple DOM lookup utility function
+		if (typeof(thingy) == 'string') thingy = document.getElementById(thingy);
+		if (!thingy.addClass) {
+			// extend element with a few useful methods
+			thingy.hide = function() { this.style.display = 'none'; };
+			thingy.show = function() { this.style.display = ''; };
+			thingy.addClass = function(name) { this.removeClass(name); this.className += ' ' + name; };
+			thingy.removeClass = function(name) {
+				var classes = this.className.split(/\s+/);
+				var idx = -1;
+				for (var k = 0; k < classes.length; k++) {
+					if (classes[k] == name) { idx = k; k = classes.length; }
+				}
+				if (idx > -1) {
+					classes.splice( idx, 1 );
+					this.className = classes.join(' ');
+				}
+				return this;
+			};
+			thingy.hasClass = function(name) {
+				return !!this.className.match( new RegExp("\\s*" + name + "\\s*") );
+			};
+		}
+		return thingy;
+	},
+	
+	setMoviePath: function(path) {
+		// set path to ZeroClipboard.swf
+		this.moviePath = path;
+	},
+	
+	dispatch: function(id, eventName, args) {
+		// receive event from flash movie, send to client		
+		var client = this.clients[id];
+		if (client) {
+			client.receiveEvent(eventName, args);
+		}
+	},
+	
+	register: function(id, client) {
+		// register new client to receive events
+		this.clients[id] = client;
+	},
+	
+	getDOMObjectPosition: function(obj, stopObj) {
+		// get absolute coordinates for dom element
+		var info = {
+			left: 0, 
+			top: 0, 
+			width: obj.width ? obj.width : obj.offsetWidth, 
+			height: obj.height ? obj.height : obj.offsetHeight
+		};
+
+		while (obj && (obj != stopObj)) {
+			info.left += obj.offsetLeft;
+			info.top += obj.offsetTop;
+			obj = obj.offsetParent;
+		}
+
+		return info;
+	},
+	
+	Client: function(elem) {
+		// constructor for new simple upload client
+		this.handlers = {};
+		
+		// unique ID
+		this.id = ZeroClipboard.nextId++;
+		this.movieId = 'ZeroClipboardMovie_' + this.id;
+		
+		// register client with singleton to receive flash events
+		ZeroClipboard.register(this.id, this);
+		
+		// create movie
+		if (elem) this.glue(elem);
+	}
+};
+
+ZeroClipboard.Client.prototype = {
+	
+	id: 0, // unique ID for us
+	ready: false, // whether movie is ready to receive events or not
+	movie: null, // reference to movie object
+	clipText: '', // text to copy to clipboard
+	handCursorEnabled: true, // whether to show hand cursor, or default pointer cursor
+	cssEffects: true, // enable CSS mouse effects on dom container
+	handlers: null, // user event handlers
+	
+	glue: function(elem, appendElem, stylesToAdd) {
+		// glue to DOM element
+		// elem can be ID or actual DOM element object
+		this.domElement = ZeroClipboard.$(elem);
+		
+		// float just above object, or zIndex 99 if dom element isn't set
+		var zIndex = 99;
+		if (this.domElement.style.zIndex) {
+			zIndex = parseInt(this.domElement.style.zIndex, 10) + 1;
+		}
+		
+		if (typeof(appendElem) == 'string') {
+			appendElem = ZeroClipboard.$(appendElem);
+		}
+		else if (typeof(appendElem) == 'undefined') {
+			appendElem = document.getElementsByTagName('body')[0];
+		}
+		
+		// find X/Y position of domElement
+		var box = ZeroClipboard.getDOMObjectPosition(this.domElement, appendElem);
+		
+		// create floating DIV above element
+		this.div = document.createElement('div');
+		var style = this.div.style;
+		style.position = 'absolute';
+		style.left = '' + box.left + 'px';
+		style.top = '' + box.top + 'px';
+		style.width = '' + box.width + 'px';
+		style.height = '' + box.height + 'px';
+		style.zIndex = zIndex;
+		
+		if (typeof(stylesToAdd) == 'object') {
+			for (addedStyle in stylesToAdd) {
+				style[addedStyle] = stylesToAdd[addedStyle];
+			}
+		}
+		
+		// style.backgroundColor = '#f00'; // debug
+		
+		appendElem.appendChild(this.div);
+		
+		this.div.innerHTML = this.getHTML( box.width, box.height );
+	},
+	
+	getHTML: function(width, height) {
+		// return HTML for movie
+		var html = '';
+		var flashvars = 'id=' + this.id + 
+			'&width=' + width + 
+			'&height=' + height;
+			
+		if (navigator.userAgent.match(/MSIE/)) {
+			// IE gets an OBJECT tag
+			var protocol = location.href.match(/^https/i) ? 'https://' : 'http://';
+			html += '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+protocol+'download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="'+width+'" height="'+height+'" id="'+this.movieId+'" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+ZeroClipboard.moviePath+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+flashvars+'"/><param name="wmode" value="transparent"/></object>';
+		}
+		else {
+			// all other browsers get an EMBED tag
+			html += '<embed id="'+this.movieId+'" src="'+ZeroClipboard.moviePath+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+width+'" height="'+height+'" name="'+this.movieId+'" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+flashvars+'" wmode="transparent" />';
+		}
+		return html;
+	},
+	
+	hide: function() {
+		// temporarily hide floater offscreen
+		if (this.div) {
+			this.div.style.left = '-2000px';
+		}
+	},
+	
+	show: function() {
+		// show ourselves after a call to hide()
+		this.reposition();
+	},
+	
+	destroy: function() {
+		// destroy control and floater
+		if (this.domElement && this.div) {
+			this.hide();
+			this.div.innerHTML = '';
+			
+			var body = document.getElementsByTagName('body')[0];
+			try { body.removeChild( this.div ); } catch(e) {;}
+			
+			this.domElement = null;
+			this.div = null;
+		}
+	},
+	
+	reposition: function(elem) {
+		// reposition our floating div, optionally to new container
+		// warning: container CANNOT change size, only position
+		if (elem) {
+			this.domElement = ZeroClipboard.$(elem);
+			if (!this.domElement) this.hide();
+		}
+		
+		if (this.domElement && this.div) {
+			var box = ZeroClipboard.getDOMObjectPosition(this.domElement);
+			var style = this.div.style;
+			style.left = '' + box.left + 'px';
+			style.top = '' + box.top + 'px';
+		}
+	},
+	
+	setText: function(newText) {
+		// set text to be copied to clipboard
+		this.clipText = newText;
+		if (this.ready) this.movie.setText(newText);
+	},
+	
+	addEventListener: function(eventName, func) {
+		// add user event listener for event
+		// event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel
+		eventName = eventName.toString().toLowerCase().replace(/^on/, '');
+		if (!this.handlers[eventName]) this.handlers[eventName] = [];
+		this.handlers[eventName].push(func);
+	},
+	
+	setHandCursor: function(enabled) {
+		// enable hand cursor (true), or default arrow cursor (false)
+		this.handCursorEnabled = enabled;
+		if (this.ready) this.movie.setHandCursor(enabled);
+	},
+	
+	setCSSEffects: function(enabled) {
+		// enable or disable CSS effects on DOM container
+		this.cssEffects = !!enabled;
+	},
+	
+	receiveEvent: function(eventName, args) {
+		// receive event from flash
+		eventName = eventName.toString().toLowerCase().replace(/^on/, '');
+				
+		// special behavior for certain events
+		switch (eventName) {
+			case 'load':
+				// movie claims it is ready, but in IE this isn't always the case...
+				// bug fix: Cannot extend EMBED DOM elements in Firefox, must use traditional function
+				this.movie = document.getElementById(this.movieId);
+				if (!this.movie) {
+					var self = this;
+					setTimeout( function() { self.receiveEvent('load', null); }, 1 );
+					return;
+				}
+				
+				// firefox on pc needs a "kick" in order to set these in certain cases
+				if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) {
+					var self = this;
+					setTimeout( function() { self.receiveEvent('load', null); }, 100 );
+					this.ready = true;
+					return;
+				}
+				
+				this.ready = true;
+				this.movie.setText( this.clipText );
+				this.movie.setHandCursor( this.handCursorEnabled );
+				break;
+			
+			case 'mouseover':
+				if (this.domElement && this.cssEffects) {
+					this.domElement.addClass('hover');
+					if (this.recoverActive) this.domElement.addClass('active');
+				}
+				break;
+			
+			case 'mouseout':
+				if (this.domElement && this.cssEffects) {
+					this.recoverActive = false;
+					if (this.domElement.hasClass('active')) {
+						this.domElement.removeClass('active');
+						this.recoverActive = true;
+					}
+					this.domElement.removeClass('hover');
+				}
+				break;
+			
+			case 'mousedown':
+				if (this.domElement && this.cssEffects) {
+					this.domElement.addClass('active');
+				}
+				break;
+			
+			case 'mouseup':
+				if (this.domElement && this.cssEffects) {
+					this.domElement.removeClass('active');
+					this.recoverActive = false;
+				}
+				break;
+		} // switch eventName
+		
+		if (this.handlers[eventName]) {
+			for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) {
+				var func = this.handlers[eventName][idx];
+			
+				if (typeof(func) == 'function') {
+					// actual function reference
+					func(this, args);
+				}
+				else if ((typeof(func) == 'object') && (func.length == 2)) {
+					// PHP style object + method, i.e. [myObject, 'myMethod']
+					func[0][ func[1] ](this, args);
+				}
+				else if (typeof(func) == 'string') {
+					// name of function
+					window[func](this, args);
+				}
+			} // foreach event handler defined
+		} // user defined handler for event
+	}
+	
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/assets/javascripts/checkout.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,33 @@
+document.observe("dom:loaded", function() {
+  /* update the checkout URL if clicked on a protocol */
+  $('checkout_protocols').select('a').each(function(e) {
+    e.observe('click', function(event) {
+      $('checkout_url').value = checkout_commands.get(this.id);
+      $('checkout_protocols').select('a').each(function(e) {
+        e.removeClassName("selected");
+      });
+      this.addClassName("selected")
+      
+      var value = checkout_access.get(this.id);
+      $('checkout_access').innerHTML = value;
+      
+      event.stop();
+    });
+  });
+  /* select the text field contents if activated */
+  Event.observe('checkout_url', 'click', function(event) {
+   this.activate();
+  });
+
+  if (typeof('ZeroClipboard') != 'undefined') {
+    $('clipboard_container').show();
+    clipboard = new ZeroClipboard.Client();
+    clipboard.setHandCursor( true );
+    clipboard.glue('clipboard_button', 'clipboard_container');
+
+    clipboard.addEventListener('mouseOver', function (client) {
+      clipboard.setText( $('checkout_url').value );
+    });
+  }
+});
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/assets/javascripts/subform.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,45 @@
+var Subform = Class.create({
+  lineIndex: 1,
+  parentElement: "",
+  initialize: function(rawHTML, lineIndex, parentElement) {
+    this.rawHTML        = rawHTML;
+    this.lineIndex      = lineIndex;
+    this.parentElement  = parentElement;
+  },
+  
+  parsedHTML: function() {
+    return this.rawHTML.replace(/--INDEX--/g, this.lineIndex++);
+  },
+  
+  add: function() {
+    var e = $(this.parentElement);
+    Element.insert(e, { bottom: this.parsedHTML()});
+    Effect.toggle(e.childElements().last(), 'slide', {duration:0.2});
+    recalculate_even_odd(e);
+  },
+  
+  add_after: function(e) {
+    Element.insert(e, { after: this.parsedHTML()});
+    Effect.toggle(e.next(), 'slide', {duration:0.2});
+    recalculate_even_odd($(this.parentElement));
+  },
+  
+  add_on_top: function() {
+    var e = $(this.parentElement);
+    Element.insert(e, { top: this.parsedHTML()});
+    Effect.toggle(e.childElements().first(), 'slide', {duration:0.2});
+    recalculate_even_odd(e);
+  }
+});
+
+function recalculate_even_odd(element) {
+  $A(element.childElements()).inject(
+    0,
+    function(acc, e)
+    {
+      e.removeClassName("even");
+      e.removeClassName("odd");
+      e.addClassName( (acc%2==0) ? "odd" : "even"); return ++acc;
+    }
+  )
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/assets/stylesheets/checkout.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,166 @@
+/* Uncomment the following line for nicer tables if you use the alternate theme (or derived). */
+/* @import url(checkout_alternate.css); */
+
+table.checkout_protocol_table td { padding-right: 6px; vertical-align: middle; /* Double the border with of text input boxes */ }
+table.checkout_protocol_table td.protocol_access { padding-right: 0; }
+table.checkout_protocol_table td input[type=text], .checkout_protocol_table td select { width: 100%; }
+table.checkout_protocol_table td.protocol_delete { width: 16px; }
+table.checkout_protocol_table td.protocol_append_path, table.checkout_protocol_table td.protocol_is_default { text-align: center; }
+
+.icon-changeset { background-image: url(../../../images/changeset.png);}
+
+.repository-info {
+  background-color: #eee;
+  border: 1px solid #E4E4E4;
+  padding: 0 10px;
+  margin: 4px 0 10px;
+}
+
+.bottomline {
+  border-bottom: 1px solid #ccc;
+}
+
+.topline {
+  border-top: 1px solid #ccc;
+}
+
+#checkout_box {
+  margin: 10px 0;
+}
+
+#checkout_protocols {
+  height: 23px;
+  float: left;
+  margin: 0;
+  padding: 0;
+}
+
+#checkout_protocols li {
+  float: left;
+  list-style-type: none;
+  margin: 0;
+  padding: 0;
+}
+
+#checkout_protocols li:first-child a {
+  border-left-width: 1px;
+  
+  /* Standard, Opera 10, IE 9 */
+  border-top-left-radius: 3px;
+  border-bottom-left-radius: 3px;
+  /* Konquerer */
+  -khtml-border-top-left-radius: 3px;
+  -khtml-border-bottom-left-radius: 3px;
+  /* Gecko (Firefox, ...) */
+  -moz-border-radius: 3px 0 0 3px;
+  /* Webkit (Chrome, Safari, ...) */
+  -webkit-border-top-left-radius: 3px;
+  -webkit-border-bottom-left-radius: 3px;
+  /* IE <= 9 not supported */
+}
+
+#checkout_protocols li a,
+#clipboard_button {
+  background-color: #eee;
+  background:  url(../images/button.svg) 0 0 no-repeat; /* Opera  needs an "image" :( - using svg for this so it will scale properly without looking too ugly */
+  background: -khtml-gradient(linear, left top, left bottom, from(#f8f8f8), to(#ddd)); /* Konquerer */
+  background: -moz-linear-gradient(top,  #f8f8f8,  #ddd); /* Gecko (Firefox, ...) */
+  background: -webkit-gradient(linear, left top, left bottom, from(#f8f8f8), to(#ddd));   /* Webkit (Chrome, Safari, ...) */
+  filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f8f8f8', endColorstr='#dddddd'); /* IE 5.5 - 7 */
+  -ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f8f8f8', endColorstr='#dddddd'); /* IE 8 */
+  
+  border-color: #bbb;
+  border-style: solid;
+  border-width: 1px 1px 1px 0;
+  
+  color: #333;
+  display: block;
+  font-size: 11px;
+  font-weight: bold;
+  line-height: 21px;
+  margin: 0;
+  padding: 0 10px 0 11px;
+  text-decoration: none;
+  text-shadow: 1px 1px 0 #fff;
+  position: relative; /* to please IE */
+}
+
+#checkout_protocols li a:hover,
+#checkout_protocols li a:focus {
+  background-color: #507AAA;
+  background:  url(../images/button_focus.svg) 0 0 no-repeat; /* Opera  needs an "image" :( - using svg for this so it will scale properly without looking too ugly */
+  background: -khtml-gradient(linear, left top, left bottom, from(#759fcf), to(#507AAA)); /* Konquerer */
+  background: -moz-linear-gradient(top,  #759fcf,  #507AAA); /* Gecko (Firefox, ...) */
+  background: -webkit-gradient(linear, left top, left bottom, from(#759fcf), to(#507AAA)); /* Webkit (Chrome, Safari, ...) */
+  filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#759fcf', endColorstr='#507AAA'); /* IE 5.5 - IE 7 */
+  -ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#759fcf', endColorstr='#507AAA'); /* IE 8 */
+  
+  color: #fff;
+  text-shadow: -1px -1px 0 rgba(0,0,0,0.4);
+  border-top-color: #759fcf;
+  border-bottom-color: #507AAA;
+}
+
+#checkout_protocols li a.selected,
+#clipboard_button.active {
+  background-color: #bbb;
+  background:  url(../images/button_selected.svg) 0 0 no-repeat; /* Opera  needs an "image" :( - using svg for this so it will scale properly without looking too ugly */
+  background: -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#aaa));  /* Konquerer */
+  background: -moz-linear-gradient(top,  #ccc,  #aaa); /* Gecko (Firefox, ...) */
+  background: -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#aaa));  /* Webkit (Chrome, Safari, ...) */
+  filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#cccccc', endColorstr='#aaaaaa'); /* IE 5.5 - IE 7 */
+  -ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#cccccc', endColorstr='#aaaaaa'); /* IE 8 */
+  
+  color: #000;
+  text-shadow: 1px 1px 0 rgba(255,255,255,0.4);
+  border-color: #bbb;
+}
+
+#checkout_url {
+  border: 1px solid #bbb;
+  border-width: 1px 1px 1px 0;
+  background-color: #fff;
+  color: #000;
+  font-size: 11px;
+  height: 16px;
+  padding: 3px 5px 2px;
+  width: 400px;
+  font-family: Monaco,"DejaVu Sans Mono","Bitstream Vera Sans Mono","Courier New",monospace;
+  margin: 0 5px 0 0;
+  float: left;
+}
+
+#checkout_box p {
+  color: #666;
+  line-height: 23px;
+  font-size: 11px;
+  margin: 0 0 0 5px;
+}
+
+span#checkout_access {
+  font-weight: bold;
+}
+
+#clipboard_container {
+  position: relative;
+  float: left;
+  margin-right: 5px;
+}
+
+#clipboard_button {
+  height: 21px;
+  width: 23px;
+  padding: 0;
+  border-width: 1px;
+  text-align: center;
+  
+  border-radius: 5px; /* Standard, Opera 10, IE 9 */
+  -khtml-border-radius: 3px; /* Konquerer */
+  -moz-border-radius: 3px ; /* Gecko (Firefox, ...) */
+  -webkit-border-radius: 3px; /* Webkit (Chrome, Safari, ...) */
+  /* IE <= 9 not supported */
+}
+
+#clipboard_button img {
+  padding-top: 2px;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/assets/stylesheets/checkout_alternate.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,2 @@
+table.checkout_protocol_table td { padding-right: 10px !important; /* Double the border with of text input boxes */ }
+table.checkout_protocol_table td.protocol_access { padding-right: 2px !important; }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/config/locales/de.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,39 @@
+de:
+  label_checkout: "Checkout"
+
+  setting_checkout_display_checkout_info: "Checkout-Informationen anzeigen"
+  setting_checkout_fixed_url: "Checkout-URL"
+  setting_checkout_url_regex: "Regulärer Ausdruck"
+  setting_checkout_url_regex_replacement: "Ersatztext"
+  setting_checkout_display_login: "Mitgliedsnamen anzeigen"
+  setting_checkout_command: "Checkout-Befehl"
+  setting_checkout_use_zero_clipboard: "Zwischenablagen-Helfer anzeigen"
+
+  setting_checkout_overwrite_description: "Standard-Beschreibung überschreiben"
+  field_checkout_overwrite: "Überschreibe Standardeinstellung für Checkout-Protokolle"
+  field_checkout_display_command: "Checkout-Befehl anzeigen"
+
+  label_protocol_plural: "Protokolle"
+  button_add_protocol: "Protokoll hinzufügen"
+
+  label_access_type: 'Diese URL erlaubt Zugriff zum <span id="checkout_access">{{type}}</span>.'
+  label_access_read_only: 'Nur-Lesen'
+  label_access_read_write: "Lesen+Schreiben"
+  label_access_permission: "Abhängig von Benutzer-Rechten"
+
+  label_append_path: "Pfad anhängen"
+
+  label_display_login_none: "Mitgliedsnamen nicht anzeigen"
+  label_display_login_username: "Mitgliedsnamen anzeigen, aber kein Kennwort"
+  label_display_login_password: "Mitgliedsnamen und Kennwort anzeigen"
+
+  label_copy_to_clipboard: "In die Zwischenablage kopieren"
+
+  help_checkout_protocols: |
+    Die URLs in Protokollen werden aus der originalen URL erzeugt, auf die der
+    reguläre Ausdruck und der Ersatztext angewendet werden. Der Ersatztext
+    erlaubt Rückwärtsreferenzen zu geklammerten Audrücken mit der \1 Notation.
+  help_repository_checkout_protocols: |
+    Lassen Sie das Checkout-URL-Feld leer um die URL des Projektarchivs zu verwenden.
+  help_moved_settings: "Die Konfigurationsseite wurde nach {{link}} verschoben."
+  label_settings_location: "Administration -> Konfiguration -> Checkout"
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/config/locales/en-GB.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,2 @@
+en-GB:
+  dummy_translation_key: Dummy translation value
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/config/locales/en.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,43 @@
+en:
+  label_checkout: "Checkout"
+
+  setting_checkout_display_checkout_info: "Display checkout information"
+  setting_checkout_fixed_url: "Checkout URL"
+  setting_checkout_url_regex: "Regular expression"
+  setting_checkout_url_regex_replacement: "Replacement text"
+  setting_checkout_display_login: "Display Login"
+  setting_checkout_command: "Checkout command"
+  setting_checkout_use_zero_clipboard: "Display clipboard helper"
+
+  setting_checkout_overwrite_description: "Overwrite default description"
+  field_checkout_overwrite: "Overwrite default settings for checkout protocols"
+  field_checkout_display_command: "Display checkout command"
+
+  label_protocol_plural: "Protocols"
+  button_add_protocol: "Add Protocol"
+
+  label_access_type: 'You have <span id="checkout_access">%{type}</span> access to this URL.'
+  label_access_type_all: 'All access to this URL is <span id="checkout_access">%{type}</span>.'
+  label_access_read_only: 'Read Only'
+  label_access_read_write: "Read and Write"
+  label_access_permission: "Depending on user's permissions"
+
+  label_append_path: "Append path"
+
+  label_display_login_none: "Do not show login or password"
+  label_display_login_username: "Show login but no password"
+  label_display_login_password: "Show login and password"
+
+  label_copy_to_clipboard: "Copy to clipboard"
+
+  help_checkout_protocols: |
+    The URLs in protocols are generated from applying the regular expression
+    and the replacement text to the original URL. The replacement text
+    supports back-references to braced expressions using the \1 notation.
+  help_repository_checkout_protocols: |
+    Leave the Checkout URL field empty to use the defined repository URL.
+  help_moved_settings: "The settings page has been moved to %{link}."
+  label_settings_location: "Administration -> Settings -> Checkout"
+
+  text_repository_external: "The primary repository for this project is hosted at <code>%{location}</code> .<br>This repository is a read-only copy which is updated automatically every hour."
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/config/locales/es.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,39 @@
+es:
+  label_checkout: "Checkout"
+
+  setting_checkout_display_checkout_info: "Display checkout information"
+  setting_checkout_fixed_url: "URL de checkout"
+  setting_checkout_url_regex: "Expresion regular"
+  setting_checkout_url_regex_replacement: "Texto de remplazo"
+  setting_checkout_display_login: "Mostrar usuario"
+  setting_checkout_command: "Comando de checkout"
+  setting_checkout_use_zero_clipboard: "Display clipboard helper"
+
+  setting_checkout_overwrite_description: "Overwrite default description"
+  field_checkout_overwrite: "Overwrite default settings for checkout protocols"
+  field_checkout_display_command: "Display checkout command"
+
+  label_protocol_plural: "Protocolos"
+  button_add_protocol: "Crear Protocolo"
+
+  label_access_type: 'This URL has <span id="checkout_access">{{type}}</span> access.'
+  label_access_read_only: 'Read-Only'
+  label_access_read_write: "Read+Write"
+  label_access_permission: "Depending on user's permissions"
+
+  label_append_path: "Append path"
+
+  label_display_login_none: "No mostrar usuario o contraseña"
+  label_display_login_username: "Mostrar usuario pero no contraseña"
+  label_display_login_password: "Mostrar usuario y contraseña"
+
+  label_copy_to_clipboard: "Copy to clipboard"
+
+  help_checkout_protocols: |
+    The URLs in protocols are generated from applying the regular expression
+    and the replacement text to the original URL. The replacement text
+    supports back-references to braced expressions using the \1 notation.
+  help_repository_checkout_protocols: |
+    Leave the Checkout URL field empty to use the defined repository URL.
+  help_moved_settings: "The settings page has been moved to {{link}}."
+  label_settings_location: "Administration -> Settings -> Checkout"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/config/locales/fr.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,39 @@
+fr:
+  label_checkout: "Dépôt"
+
+  setting_checkout_display_checkout_info: "Display checkout information"
+  setting_checkout_fixed_url: "URL du dépôt"
+  setting_checkout_url_regex: "Expression Régulière"
+  setting_checkout_url_regex_replacement: "Texte de substitution"
+  setting_checkout_display_login: "Affiche le login"
+  setting_checkout_command: "Checkout command"
+  setting_checkout_use_zero_clipboard: "Display clipboard helper"
+
+  setting_checkout_overwrite_description: "Overwrite default description"
+  field_checkout_overwrite: "Overwrite default settings for checkout protocols"
+  field_checkout_display_command: "Affiche l'URL du dépôt"
+
+  label_protocol_plural: "Protocoles"
+  button_add_protocol: "Créer un protocole"
+
+  label_access_type: 'This URL has <span id="checkout_access">{{type}}</span> access.'
+  label_access_read_only: 'Read-Only'
+  label_access_read_write: "Read+Write"
+  label_access_permission: "Depending on user's permissions"
+
+  label_append_path: "Append path"
+
+  label_display_login_none: "Ne pas afficher le login ni le mot de passe"
+  label_display_login_username: "Afficher le login, pas le mot de passe"
+  label_display_login_password: "Afficher le login et le mot de passe"
+
+  label_copy_to_clipboard: "Copy to clipboard"
+
+  help_checkout_protocols: |
+    The URLs in protocols are generated from applying the regular expression
+    and the replacement text to the original URL. The replacement text
+    supports back-references to braced expressions using the \1 notation.
+  help_repository_checkout_protocols: |
+    Leave the Checkout URL field empty to use the defined repository URL.
+  help_moved_settings: "The settings page has been moved to {{link}}."
+  label_settings_location: "Administration -> Settings -> Checkout"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/config/locales/it.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,40 @@
+it:
+  label_checkout: "Checkout"
+
+  setting_checkout_display_checkout_info: "Visualizza le informazioni sul checkout"
+  setting_checkout_fixed_url: "URL di checkout"
+  setting_checkout_url_regex: "Espressione regolare"
+  setting_checkout_url_regex_replacement: "Testo sostituito"
+  setting_checkout_display_login: "Login visualizzato"
+  setting_checkout_command: "Comando per il checkout"
+  setting_checkout_use_zero_clipboard: "Visualizza l'utility per la copia negli appunti"
+
+  setting_checkout_overwrite_description: "Sovrascrivi la descrizione predefinita"
+  field_checkout_overwrite: "Sovrascrivi le impostazioni di checkout predefinite"
+  field_checkout_display_command: "Visualizza il comando per il checkout"
+
+  label_protocol_plural: "Protocolli"
+  button_add_protocol: "Aggiungi Protocollo"
+
+  label_access_type: 'Questo URL ha accesso <span id="checkout_access">{{type}}</span>.'
+  label_access_read_only: 'Sola-Lettura'
+  label_access_read_write: "Lettura+Scrittura"
+  label_access_permission: "Dipende dai permessi dell'utente"
+
+  label_append_path: "Aggiungi percorso"
+
+  label_display_login_none: "Non mostrare il login e password"
+  label_display_login_username: "Mostra il login senza password"
+  label_display_login_password: "Mostra il login e la password"
+
+  label_copy_to_clipboard: "Copia negli appunti"
+
+  help_checkout_protocols: |
+    Gli URL dei protocolli sono generati applicando le espressioni regolari
+    ed effettuando la sostituzione dell'URL originale con il testo specificato.
+    Il testo per la sostituzione può contenere riferimenti a più match usando
+    la notazione \1 \2...
+  help_repository_checkout_protocols: |
+    Lascia il campo URL di checkout bianco per usare l'URL definito nel repository.
+  help_moved_settings: "La pagina delle impostazioni è stata spostata in {{link}}."
+  label_settings_location: "Amministrazione -> Impostazioni -> Checkout"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/config/locales/ja.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,39 @@
+ja:
+  label_checkout: "Checkout"
+
+  setting_checkout_display_checkout_info: "Display checkout information"
+  setting_checkout_fixed_url: "チェックアウト URL"
+  setting_checkout_url_regex: "Regular expression"
+  setting_checkout_url_regex_replacement: "Replacement text"
+  setting_checkout_display_login: "ログインの表示"
+  setting_checkout_command: "Checkout command"
+  setting_checkout_use_zero_clipboard: "Display clipboard helper"
+
+  setting_checkout_overwrite_description: "Overwrite default description"
+  field_checkout_overwrite: "Overwrite default settings for checkout protocols"
+  field_checkout_display_command: "Display checkout command"
+
+  label_protocol_plural: "Protocols"
+  button_add_protocol: "Add Protocol"
+  
+  label_access_type: 'This URL has <span id="checkout_access">{{type}}</span> access.'
+  label_access_read_only: 'Read-Only'
+  label_access_read_write: "Read+Write"
+  label_access_permission: "Depending on user's permissions"
+
+  label_append_path: "Append path"
+
+  label_display_login_none: "ログインとパスワードを非表示"
+  label_display_login_username: "ログインのみ表示"
+  label_display_login_password: "ログインとパスワードを表示"
+
+  label_copy_to_clipboard: "Copy to clipboard"
+
+  help_checkout_protocols: |
+    The URLs in protocols are generated from applying the regular expression
+    and the replacement text to the original URL. The replacement text
+    supports back-references to braced expressions using the \1 notation.
+  help_repository_checkout_protocols: |
+    Leave the Checkout URL field empty to use the defined repository URL.
+  help_moved_settings: "The settings page has been moved to {{link}}."
+  label_settings_location: "Administration -> Settings -> Checkout"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/config/locales/ko.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,40 @@
+ko:
+  label_checkout: "Checkout"
+
+  setting_checkout_display_checkout_info: "Display checkout information"
+  setting_checkout_fixed_url: "체크 아웃 URL"
+  setting_checkout_url_regex: "정규식"
+  setting_checkout_url_regex_replacement: "대체 문자열"
+  setting_checkout_display_login: "로그인 표시"
+  setting_checkout_command: "Checkout command"
+  setting_checkout_use_zero_clipboard: "Display clipboard helper"
+  
+  setting_checkout_overwrite_description: "Overwrite default description"
+  field_checkout_overwrite: "Overwrite default settings for checkout protocols"
+  field_checkout_display_command: "Display checkout command"
+
+  label_protocol_plural: "Protocols"
+  button_add_protocol: "Add Protocol"
+
+  label_access_type: 'This URL has <span id="checkout_access">{{type}}</span> access.'
+  label_access_read_only: 'Read-Only'
+  label_access_read_write: "Read+Write"
+  label_access_permission: "Depending on user's permissions"
+
+  label_append_path: "Append path"
+
+  label_display_login_none: "로그인과 비밀번호를 보여주지 않습니다."
+  label_display_login_username: "로그인은 보여주지만 비밀번호는 보여주지 않습니다."
+  label_display_login_password: "로그인과 비밀번호를 보여줍니다."
+
+  label_copy_to_clipboard: "Copy to clipboard"
+
+  help_checkout_protocols: |
+    The URLs in protocols are generated from applying the regular expression
+    and the replacement text to the original URL. The replacement text
+    supports back-references to braced expressions using the \1 notation.
+  help_repository_checkout_protocols: |
+    Leave the Checkout URL field empty to use the defined repository URL.
+  help_moved_settings: "The settings page has been moved to {{link}}."
+  label_settings_location: "Administration -> Settings -> Checkout"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/config/locales/nl.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,40 @@
+nl:
+  label_checkout: "Checkout"
+
+  setting_checkout_display_checkout_info: "Checkout-informatie tonen"
+  setting_checkout_fixed_url: "Checkout-URL"
+  setting_checkout_url_regex: "Reguliere expressie"
+  setting_checkout_url_regex_replacement: "Vervangingstekst"
+  setting_checkout_display_login: "Geef login weer"
+  setting_checkout_command: "Checkout-opdracht"
+  setting_checkout_use_zero_clipboard: "Klembord-hulp tonen"
+
+  setting_checkout_overwrite_description: "Standaard omschrijving overschrijven"
+  field_checkout_overwrite: "Overschrijf standaard instellingen voor checkout-protocollen"
+  field_checkout_display_command: "Checkout-opdracht tonen"
+
+  label_protocol_plural: "Protocollen"
+  button_add_protocol: "Protocol toevoegen"
+
+  label_access_type: 'Deze URL heeft <span id="checkout_access">{{type}}</span> toegang.'
+  label_access_read_only: 'Alleen lezen'
+  label_access_read_write: "Lezen en schrijven"
+  label_access_permission: "Afhankelijk van gebruikersrechten"
+
+  label_append_path: "Pad toevoegen"
+
+  label_display_login_none: "Geen logingegevens tonen"
+  label_display_login_username: "Toon login zonder wachtwoord"
+  label_display_login_password: "Toon login en wachtwoord"
+
+  label_copy_to_clipboard: "Naar klembord kopiëren"
+
+  help_checkout_protocols: |
+    De URLs in protocollen worden samengesteld vanuit de originele URL, na
+    toepassing van de reguliere expressie en vervangingstekst. De vervangingstekst
+    ondersteunt referenties vanuit tussen haakjes geplaatste expressies
+    door middel van de \1 notatie.
+  help_repository_checkout_protocols: |
+    Laat het veld Checkout-URL leeg om de projectrepository te gebruiken.
+  help_moved_settings: "De instellingspagina is verplaatst naar {{link}}."
+  label_settings_location: "Administratie -> Instellingen -> Checkout"
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/config/locales/pl.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,39 @@
+pl:
+  label_checkout: "Pobieranie repozytorium"
+
+  setting_checkout_display_checkout_info: "Pokaż informację o możliwości pobrania repozytorium"
+  setting_checkout_fixed_url: "Adres URL pobierania repozytorium"
+  setting_checkout_url_regex: "Wyrażenie regularne"
+  setting_checkout_url_regex_replacement: "Wynikowy adres URL"
+  setting_checkout_display_login: "Pokaż dane logowania"
+  setting_checkout_command: "Komenda pobrania repozytorium"
+  setting_checkout_use_zero_clipboard: "Pokaż pomocnika schowka"
+
+  setting_checkout_overwrite_description: "Nadpisz domyślny opis"
+  field_checkout_overwrite: "Nadpisz domyślne ustawienia dla protokołów"
+  field_checkout_display_command: "Pokaż komendę pobrania repozytorium"
+
+  label_protocol_plural: "Protokoły"
+  button_add_protocol: "Dodaj protokół"
+
+  label_access_type: 'Ten adres URL ma dostęp <span id="checkout_access">{{type}}</span>.'
+  label_access_read_only: 'Tylko do odczytu'
+  label_access_read_write: "Odczyt+Zapis"
+  label_access_permission: "Zależne od uprawnień użytkownika"
+
+  label_append_path: "Dołącz ścieżkę"
+
+  label_display_login_none: "Nie pokazuj loginu ani hasła"
+  label_display_login_username: "Pokaż tylko login"
+  label_display_login_password: "Pokaż login i hasło"
+
+  label_copy_to_clipboard: "Kopiuj do schowka"
+
+  help_checkout_protocols: |
+    Wynikowe adresy URL w protokołach są generowane przez zamianę oryginalnego
+    adresu URL repozytorium na podstawie wyrażenia regularnego. Wynikowy adres
+    URL wspiera referencje do grup (tzw. back-references) używając notacji \1.
+  help_repository_checkout_protocols: |
+    Pozostaw adres URL pobierania repozytorium pusty aby uzyć adresu zdefiniowanego w ustawieniach projektu.
+  help_moved_settings: "Ustawienia zostały przeniesione do {{link}}."
+  label_settings_location: "Administracja -> Ustawienia -> Pobieranie repozytorium"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/config/locales/ro.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,39 @@
+ro:
+  label_checkout: "Checkout"
+
+  setting_checkout_display_checkout_info: "Display checkout information"
+  setting_checkout_fixed_url: "URL-ul pentru checkout"
+  setting_checkout_url_regex: "Expresie regulata (regexp)"
+  setting_checkout_url_regex_replacement: "Text inlocuitor (regexp)"
+  setting_checkout_display_login: "Arata datele pentru autentificare"
+  setting_checkout_command: "Comanda de checkout"
+  setting_checkout_use_zero_clipboard: "Display clipboard helper"
+
+  setting_checkout_overwrite_description: "Overwrite default description"
+  field_checkout_overwrite: "Overwrite default settings for checkout protocols"
+  field_checkout_display_command: "Display checkout command"
+
+  label_protocol_plural: "Protocols"
+  button_add_protocol: "Add Protocol"
+
+  label_access_type: 'This URL has <span id="checkout_access">{{type}}</span> access.'
+  label_access_read_only: 'Read-Only'
+  label_access_read_write: "Read+Write"
+  label_access_permission: "Depending on user's permissions"
+
+  label_append_path: "Append path"
+
+  label_display_login_none: "Nu afisa username sau parola"
+  label_display_login_username: "Afiseaza username-ul, dar nu si parola"
+  label_display_login_password: "Afiseaza atat username-ul, cat si parola"
+
+  label_copy_to_clipboard: "Copy to clipboard"
+
+  help_checkout_protocols: |
+    The URLs in protocols are generated from applying the regular expression
+    and the replacement text to the original URL. The replacement text
+    supports back-references to braced expressions using the \1 notation.
+  help_repository_checkout_protocols: |
+    Leave the Checkout URL field empty to use the defined repository URL.
+  help_moved_settings: "The settings page has been moved to {{link}}."
+  label_settings_location: "Administration -> Settings -> Checkout"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/db/migrate/20091208210439_add_checkout_url_info.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,11 @@
+class AddCheckoutUrlInfo < ActiveRecord::Migration
+  def self.up
+    add_column :repositories, :checkout_url_type, :string, :default => 'none', :null => false
+    add_column :repositories, :checkout_url, :string, :default => '', :null => false
+  end
+
+  def self.down
+    remove_column :repository, :checkout_url_type
+    remove_column :repository, :checkout_url
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/db/migrate/20091220173312_add_display_login.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+class AddDisplayLogin < ActiveRecord::Migration
+  def self.up
+    add_column :repositories, :display_login, :string, :default => 'none', :null => false
+  end
+
+  def self.down
+    remove_column :repositories, :display_login
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/db/migrate/20100118174556_add_render_link.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+class AddRenderLink < ActiveRecord::Migration
+  def self.up
+    add_column :repositories, :render_link, :boolean, :null => true
+  end
+  
+  def self.down
+    remove_column :repositories, :render_link
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/db/migrate/20100118235845_remove_defaults.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,14 @@
+class RemoveDefaults < ActiveRecord::Migration
+  def self.up
+    change_column :repositories, :checkout_url_type, :string, :default => nil, :null => true
+    change_column :repositories, :checkout_url, :string, :default => nil, :null => true
+    change_column :repositories, :display_login, :string, :default => nil, :null => true
+  end
+  
+  def self.down
+    change_column :repositories, :checkout_url_type, :string, :default => 'none', :null => false
+    change_column :repositories, :checkout_url, :string, :default => '', :null => false
+    change_column :repositories, :display_login, :string, :default => 'none', :null => false
+  end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/db/migrate/20100118235909_add_overwrite_option.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,15 @@
+class AddOverwriteOption < ActiveRecord::Migration
+  def self.up
+    add_column :repositories, :checkout_url_overwrite, :boolean, :default => false, :null => false
+    
+    # existing repositories are set to overwrite the default settings
+    # This is to keep continuity of settings.
+    Repository.reset_column_information
+    Repository.update_all({:checkout_url_overwrite, true})
+  end
+  
+  def self.down
+    remove_column :repositories, :checkout_url_overwrite
+  end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/db/migrate/20100203202320_update_settings.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,36 @@
+class UpdateSettings < ActiveRecord::Migration
+  def self.up
+    settings = Setting.plugin_redmine_checkout
+    if settings['checkout_url_type'] == "overwritten"
+      settings['checkout_url_type'] = "generated"
+    end
+    
+    if settings.has_key? "checkout_url_regex"
+      settings['checkout_url_regex_default'] = settings.delete("checkout_url_regex")
+    end
+
+    if settings.has_key? "checkout_url_regex_replacement"
+      settings['checkout_url_regex_replacement_default'] = settings.delete("checkout_url_regex_replacement")
+    end
+    
+    Setting.plugin_redmine_checkout = settings
+  end
+  
+  def self.down
+    settings = Setting.plugin_redmine_checkout
+    if settings['checkout_url_type'] == "generated"
+      settings['checkout_url_type'] = "overwritten"
+    end
+    
+    if settings.has_key? "checkout_url_regex_default"
+      settings['checkout_url_regex'] = settings.delete("checkout_url_regex_default")
+    end
+
+    if settings.has_key? "checkout_url_regex_replacement_default"
+      settings['checkout_url_regex_replacement'] = settings.delete("checkout_url_regex_replacement_default")
+    end
+    
+    Setting.plugin_redmine_checkout = settings
+  end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/db/migrate/20100426154202_rename_render_link_to_render_type.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,31 @@
+class RenameRenderLinkToRenderType < ActiveRecord::Migration
+  def self.up
+    render_link = Setting.plugin_redmine_checkout.delete 'render_link'
+    unless  render_link.nil?
+      Setting.plugin_redmine_checkout['render_type'] = (render_link == 'true' ? 'link' : 'url')
+      Setting.plugin_redmine_checkout = Setting.plugin_redmine_checkout
+    end
+    
+    add_column :repositories, :render_type, :string, :default => 'url', :null => false
+    
+    Repository.update_all({:render_type => 'link'}, :render_link => true)
+    Repository.update_all({:render_type => 'url'}, ["render_link != ?", true])
+    
+    remove_column :repositories, :render_link
+  end
+  
+  def self.down
+    render_type = Setting.plugin_redmine_checkout.delete 'render_type'
+    unless  render_type.nil?
+      Setting.plugin_redmine_checkout['render_link'] = (render_type == 'link' ? 'true' : 'false')
+      Setting.plugin_redmine_checkout = Setting.plugin_redmine_checkout
+    end
+    
+    add_column :repositories, :render_link, :boolean, :null => true
+    
+    Repository.update_all({:render_link => true}, :render_type => 'link')
+    Repository.update_all({:render_link => false}, ["render_type != ?", 'link'])
+    
+    remove_column :repositories, :render_type
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/db/migrate/20100512135418_consolidate_repository_options.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,49 @@
+class ConsolidateRepositoryOptions < ActiveRecord::Migration
+  class Repository < ActiveRecord::Base
+    def self.inheritance_column
+      # disable single table inheritance
+      nil
+    end
+    
+    serialize :checkout_settings, Hash
+  end
+
+  def self.up
+    add_column :repositories, :checkout_settings, :text
+    
+    Repository.all.each do |r|
+      r.checkout_settings = {
+        "checkout_url_type" => r.checkout_url_type,
+        "checkout_url" => r.checkout_url,
+        "display_login" => r.display_login,
+        "render_type" => r.render_type,
+        "checkout_url_overwrite" => r.checkout_url_overwrite
+      }
+      r.save!
+    end
+    remove_column :repositories, :checkout_url_type
+    remove_column :repositories, :checkout_url
+    remove_column :repositories, :display_login
+    remove_column :repositories, :render_type
+    remove_column :repositories, :checkout_url_overwrite
+  end
+  
+  def self.down
+    add_column :repositories, :checkout_url_type, :string, :default => nil, :null => true
+    add_column :repositories, :checkout_url, :string, :default => nil, :null => true
+    add_column :repositories, :display_login, :string, :default => nil, :null => true
+    add_column :repositories, :render_type, :string, :default => 'url', :null => false
+    add_column :repositories, :checkout_url_overwrite, :boolean, :default => false, :null => false
+    
+    Repository.all.each do |r|
+      r.checkout_url_type = r.checkout_settings["checkout_url_type"]
+      r.checkout_url = r.checkout_settings["checkout_url"]
+      r.display_login = r.checkout_settings["display_login"]
+      r.render_link = r.checkout_settings["render_link"]
+      r.checkout_url_overwrite = r.checkout_settings["checkout_url_overwrite"]
+      r.save!
+    end
+    
+    remove_column :repositories, :checkout_settings
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/db/migrate/20100609153630_apply_setting_changes.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,113 @@
+class ApplySettingChanges < ActiveRecord::Migration
+  class Repository < ActiveRecord::Base
+    def self.inheritance_column
+      # disable single table inheritance
+      nil
+    end
+    
+    def scm_name
+      self.type || 'Abstract'
+    end
+    
+    serialize :checkout_settings, Hash
+  end
+  
+  def self.up
+    default_commands = {
+      'Bazaar' => 'bzr checkout',
+      'Cvs' => 'cvs checkout',
+      'Darcs' => 'darcs get',
+      'Git' => 'git clone',
+      'Mercurial' => 'hg clone',
+      'Subversion' => 'svn checkout'
+    }
+    
+    ## First migrate the individual repositories
+    
+    Repository.all.each do |r|
+      allow_subtree_checkout = ['Cvs', 'Subversion'].include? r.scm_name
+      
+      protocol = case r.checkout_settings['checkout_url_type']
+      when 'none', 'generated'
+        nil
+      when 'original', 'overwritten'
+        HashWithIndifferentAccess.new({ "0" => HashWithIndifferentAccess.new({
+          :protocol => r.scm_name,
+          :command => Setting.plugin_redmine_checkout["checkout_cmd_#{r.scm_name}"] || default_commands[r.scm_name],
+          :regex => "",
+          :regex_replacement => "",
+          :fixed_url => (r.checkout_settings['checkout_url_type'] == 'original' ? (r.url || "") : r.checkout_settings["checkout_url"]),
+          :access => 'permission',
+          :append_path => (allow_subtree_checkout ? '1' : '0'),
+          :is_default => '1'})
+        })
+      end
+      
+      r.checkout_settings = Hash.new({
+        'checkout_protocols' => protocol,
+        'checkout_description' => "The data contained in this repository can be downloaded to your computer using one of several clients.
+Please see the documentation of your version control software client for more information.
+
+Please select the desired protocol below to get the URL.",
+        'checkout_display_login' => (r.checkout_settings['display_login'] == 'none' ? '' : r.checkout_settings['display_login']),
+        'checkout_overwrite' => (r.checkout_settings['checkout_url_overwrite'] == 'true') ? '1': '0',
+        'checkout_display_command' => (r.checkout_settings["render_type"].to_s == 'cmd') ? '1' : '0'
+      })
+      r.save!
+    end
+    
+    ## Then the global settings
+    
+    settings = HashWithIndifferentAccess.new({
+      'display_login' => Setting.plugin_redmine_checkout['display_login'],
+      'use_zero_clipboard' => '1',
+
+      'display_checkout_info' => (Setting.plugin_redmine_checkout['checkout_url_type'] == 'none' ? '0' : '1'),
+      'description_Abstract' => <<-EOF
+The data contained in this repository can be downloaded to your computer using one of several clients.
+Please see the documentation of your version control software client for more information.
+
+Please select the desired protocol below to get the URL.
+EOF
+    })
+
+    default_commands.keys.each do |scm|
+      settings["description_#{scm}"] = ''
+      settings["overwrite_description_#{scm}"] = '0'
+      
+      display_command = (Setting.plugin_redmine_checkout["render_type"].to_s == 'cmd') ? '1' : '0'
+      settings["display_command_#{scm}"] = display_command
+      
+      case Setting.plugin_redmine_checkout['checkout_url_type']
+      when 'generated', 'none':
+        regex = Setting.plugin_redmine_checkout["checkout_url_regex_#{scm}"]
+        replacement = Setting.plugin_redmine_checkout["checkout_url_regex_replacement_#{scm}"]
+      when 'original':
+        regex = ''
+        replacement = ''
+      end
+      
+      settings["protocols_#{scm}"] = HashWithIndifferentAccess.new({
+        # access can be one of
+        #   read+write => this protocol always allows read/write access
+        #   read-only => this protocol always allows read access only
+        #   permission => Access depends on redmine permissions
+        '0' => HashWithIndifferentAccess.new({
+                :protocol => scm,
+                :command => Setting.plugin_redmine_checkout["checkout_cmd_#{scm}"] || default_commands[scm],
+                :regex => regex,
+                :regex_replacement => replacement,
+                :fixed_url => '',
+                :access => 'permission',
+                :append_path => (['Cvs', 'Subversion'].include?(scm) ? '1' : '0'),
+                :is_default => '1'
+               })
+      })
+    end
+    Setting.plugin_redmine_checkout = settings
+  end
+  
+  def self.down
+    raise ActiveRecord::IrreversibleMigration.new "Sorry, there is no down migration yet. If you really need one, please create an issue on http://dev.holgerjust.de/projects/redmine-checkout"
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/db/migrate/20100808185600_change_protocol_storage_from_hash_to_array.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,58 @@
+class ChangeProtocolStorageFromHashToArray < ActiveRecord::Migration
+  class Repository < ActiveRecord::Base
+    def self.inheritance_column
+      # disable single table inheritance
+      nil
+    end
+    
+    def scm_name
+      self.type || 'Abstract'
+    end
+    
+    serialize :checkout_settings, Hash
+  end
+  
+  def self.up
+    ## First migrate the individual repositories
+    Repository.all.each do |r|
+      next unless r.checkout_settings
+      next unless r.checkout_settings['checkout_protocols'].is_a? Hash
+      r.checkout_settings['checkout_protocols'] = r.checkout_settings['checkout_protocols'].sort{|(ak,av),(bk,bv)|ak<=>bk}.collect{|id,protocol| protocol}
+      r.save!
+    end
+    
+    ## Then the global settings
+    settings = Setting.plugin_redmine_checkout
+    settings.keys.grep(/^protocols_/).each do |protocols|
+      next unless settings[protocols].is_a? Hash
+      settings[protocols] = settings[protocols].sort{|(ak,av),(bk,bv)|ak<=>bk}.collect{|id,protocol| protocol}
+    end
+    Setting.plugin_redmine_checkout = settings
+  end
+  
+  def self.down
+    ## First migrate the individual repositories
+    Repository.all.each do |r|
+      next unless r.checkout_settings['checkout_protocols'].is_a? Hash
+      r.checkout_settings['checkout_protocols'] = r.checkout_settings['checkout_protocols'].inject(HashWithIndifferentAccess.new) do |result, p|
+        result[result.length.to_s] = p
+      end
+      r.save!
+    end
+
+    ## Then the global settings
+    settings = Setting.plugin_redmine_checkout
+    settings.keys.grep(/^protocols_/).each do |protocols|
+      next unless r.checkout_settings['checkout_protocols'].is_a? Hash
+      settings[protocols] = settings[protocols].inject(HashWithIndifferentAccess.new) do |result, p|
+        result[result.length.to_s] = p
+      end
+    end
+    Setting.plugin_redmine_checkout = settings
+    
+    
+    
+    
+    raise ActiveRecord::IrreversibleMigration.new "Sorry, there is no down migration yet. If you really need one, please create an issue on http://dev.holgerjust.de/projects/redmine-checkout"
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/doc/COPYING	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,22 @@
+Copyright (c) 2009 Holger Just
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/init.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,101 @@
+require 'redmine'
+
+ActionDispatch::Callbacks.to_prepare do
+  # Patches
+  require_dependency 'checkout/settings_controller_patch'
+
+  require_dependency 'checkout/repositories_helper_patch'
+  require_dependency 'checkout/repository_patch'
+
+  require_dependency 'checkout/settings_helper_patch'
+  require_dependency 'checkout/setting_patch'
+end
+
+# Hooks
+require 'checkout/repository_hooks'
+
+Redmine::Plugin.register :redmine_checkout do
+  name 'Redmine Checkout plugin'
+  url 'http://dev.holgerjust.de/projects/redmine-checkout'
+  author 'Holger Just'
+  author_url 'http://meine-er.de'
+  description 'Add links to the actual repository to the repository view.'
+  version '0.5'
+
+  requires_redmine :version_or_higher => '0.9'
+
+  settings_defaults = HashWithIndifferentAccess.new({
+    'display_login' => nil,
+    'use_zero_clipboard' => '1',
+
+    'display_checkout_info' =>  '1',
+    'description_Abstract' => <<-EOF
+The data contained in this repository can be downloaded to your computer using one of several clients.
+Please see the documentation of your version control software client for more information.
+
+Please select the desired protocol below to get the URL.
+EOF
+  })
+
+  # this is needed for setting the defaults
+  require 'checkout/repository_patch'
+
+  CheckoutHelper.supported_scm.each do |scm|
+    klazz = "Repository::#{scm}".constantize
+
+    settings_defaults["description_#{scm}"] = ''
+    settings_defaults["overwrite_description_#{scm}"] = '0'
+    settings_defaults["display_command_#{scm}"] = '0'
+
+    # access can be one of
+    #   read+write => this protocol always allows read/write access
+    #   read-only => this protocol always allows read access only
+    #   permission => Access depends on redmine permissions
+    settings_defaults["protocols_#{scm}"] = [HashWithIndifferentAccess.new({
+      :protocol => scm,
+      :command => klazz.checkout_default_command,
+      :regex => '',
+      :regex_replacement => '',
+      :fixed_url => '',
+      :access => 'permission',
+      :append_path => (klazz.allow_subtree_checkout? ? '1' : '0'),
+      :is_default => '1'
+    })]
+  end
+
+  settings :default => settings_defaults, :partial => 'settings/redmine_checkout'
+
+  Redmine::WikiFormatting::Macros.register do
+    desc <<-EOF
+Creates a checkout link to the actual repository. Example:
+
+  use the default checkout protocol !{{repository}}
+  or use a specific protocol !{{repository(SVN)}}
+  or use the checkout protocol of a specific specific project: !{{repository(projectname:SVN)}}"
+EOF
+
+    macro :repository do |obj, args|
+      proto = args.first
+      if proto.to_s =~ %r{^([^\:]+)\:(.*)$}
+        project_identifier, proto = $1, $2
+        project = Project.find_by_identifier(project_identifier) || Project.find_by_name(project_identifier)
+      else
+        project = @project
+      end
+
+      if project && project.repository
+        protocols = project.repository.checkout_protocols.select{|p| p.access_rw(User.current)}
+
+        if proto.present?
+          proto_obj = protocols.find{|p| p.protocol.downcase == proto.downcase}
+        else
+          proto_obj = protocols.find(&:default?) || protocols.first
+        end
+      end
+      raise "Checkout protocol #{proto} not found" unless proto_obj
+
+      cmd = (project.repository.checkout_display_command? && proto_obj.command.present?) ? proto_obj.command.strip + " " : ""
+      cmd + link_to(proto_obj.url, proto_obj.url)
+    end
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/lib/checkout/protocol.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,116 @@
+module Checkout
+  class <<self
+    def awesome?
+      # Yes, this plugin is awesome!
+      true
+    end
+  end
+  
+  class Protocol
+    attr_accessor :protocol, :regex, :regex_replacement, :access, :repository
+    attr_writer :default, :command, :fixed_url, :append_path
+    
+    
+    def initialize(args={})
+      args = args.dup
+      
+      @protocol = args.delete :protocol
+      @command = args.delete :command # optional, if not set the default from the repo is used
+      
+      # either a fixed url
+      @fixed_url = args.delete :fixed_url
+
+      # or a regex
+      @regex = args.delete :regex
+      @regex_replacement = args.delete :regex_replacement
+      
+      @access = args.delete :access
+      @append_path = args.delete :append_path
+      @default = args.delete :is_default
+      
+      @repository = args.delete :repository
+    end
+    
+    def full_command(path = "")
+      cmd = ""
+      if repository.checkout_display_command?
+        cmd = self.command.present? ? self.command.strip + " " : ""
+      end
+      cmd + URI.escape(self.url(path))
+    end
+    
+    def default?
+      @default.to_i > 0
+    end
+    
+    def command
+      @command || self.repository && self.repository.checkout_default_command || ""
+    end
+    
+    def append_path?
+      @append_path.to_i > 0
+    end
+    
+    def access_rw(user)
+      # reduces the three available access levels 'read+write', 'read-only' and 'permission'
+      # to 'read+write' and 'read-only' and nil (not allowed)
+
+      @access_rw ||= {}
+      return @access_rw[user] if @access_rw.key? user
+      @access_rw[user] = case access
+      when 'permission'
+        case
+        when user.allowed_to?(:commit_access, repository.project) && user.allowed_to?(:browse_repository, repository.project)
+          'read+write'
+        when user.allowed_to?(:browse_repository, repository.project)
+          'read-only'
+        else
+          nil
+        end
+      else
+        @access
+      end
+    end
+    
+    def access_label(user)
+      case access_rw(user)
+        when 'read+write'
+          :label_access_read_write
+        when 'read-only'
+          :label_access_read_only
+      end
+    end
+  
+    def fixed_url
+      @fixed_url.present? ? @fixed_url : begin
+        if (regex.blank? || regex_replacement.blank?)
+          repository.url
+        else
+          repository.url.gsub(Regexp.new(regex), regex_replacement)
+        end
+      end
+    rescue RegexpError
+      repository.url || ""
+    end
+
+    def url(path = "")
+      return "" unless repository
+      
+      url = fixed_url.sub(/\/+$/, "")
+      if repository.allow_subtree_checkout? && self.append_path? && path.present?
+        url = "#{url}/#{path}"
+      end
+      
+      if repository.checkout_display_login?
+        begin
+          uri = URI.parse url
+          (uri.user = repository.login) if repository.login
+          (uri.password = repository.password) if (repository.checkout_display_login == 'password' && repository.login && repository.password)
+          url = uri.to_s
+        rescue URI::InvalidURIError
+        end
+      end
+      url
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/lib/checkout/repositories_helper_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,34 @@
+require_dependency 'repositories_helper'
+
+module Checkout
+  module RepositoriesHelperPatch
+    def self.included(base) # :nodoc:
+      base.send(:include, InstanceMethods)
+
+      base.class_eval do
+        alias_method_chain :repository_field_tags, :checkout
+        alias_method_chain :scm_select_tag, :javascript
+      end
+    end
+  
+    module InstanceMethods
+      def repository_field_tags_with_checkout(form, repository)    
+        tags = repository_field_tags_without_checkout(form, repository) || ""
+        return tags if repository.class.name == "Repository"
+      
+        tags + @controller.send(:render_to_string, :partial => 'projects/settings/repository_checkout', :locals => {:form => form, :repository => repository, :scm => repository.scm_name})
+      end
+      
+      def scm_select_tag_with_javascript(*args)
+        content_for :header_tags do
+          javascript_include_tag('subform', :plugin => 'redmine_checkout') +
+          stylesheet_link_tag('checkout', :plugin => 'redmine_checkout')
+        end
+        scm_select_tag_without_javascript(*args)
+      end
+    end
+  end
+end
+
+RepositoriesHelper.send(:include, Checkout::RepositoriesHelperPatch)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/lib/checkout/repository_hooks.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,40 @@
+module Checkout
+  class RepositoryHooks < Redmine::Hook::ViewListener
+    # Renders the checkout URL
+    #
+    # Context:
+    # * :project => Current project
+    # * :repository => Current Repository
+    #
+    def view_repositories_show_contextual(context={})
+      if context[:repository].present? && Setting.checkout_display_checkout_info?
+        protocols = context[:repository].checkout_protocols.select do |p|
+          p.access_rw(User.current)
+        end
+        
+        path = context[:controller].instance_variable_get("@path")
+        if path && context[:controller].instance_variable_get("@entry")
+          # a single file is showing, so we return only the directory
+          path = File.dirname(path)
+        end
+        
+        default = protocols.find(&:default?) || protocols.first
+        
+        context.merge!({
+          :protocols => protocols,
+          :default_protocol => default,
+          :checkout_path => path
+        })
+      
+        options = {:partial => "redmine_checkout_hooks/view_repositories_show_contextual"}
+
+        # cc: cribbed from
+        # http://www.redmine.org/projects/redmine/repository/revisions/9785/diff/trunk/lib/redmine/hook.rb
+        # for http://www.redmine.org/issues/11105 (formerly used
+        # render_to_string on this controller, wasn't working for
+        # :header_tags)
+        context[:hook_caller].send(:render, {:locals => context}.merge(options))
+      end
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/lib/checkout/repository_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,186 @@
+require_dependency 'repository'
+require_dependency 'checkout_helper'
+
+module Checkout
+  module RepositoryPatch
+    def self.included(base) # :nodoc:
+      base.extend(ClassMethods)
+      base.send(:include, InstanceMethods)
+      
+      base.class_eval do
+        unloadable
+        serialize :checkout_settings, Hash
+      end
+    end
+    
+    module ClassMethods
+      def allow_subtree_checkout?
+        # default implementation
+        false
+      end
+      
+      def checkout_default_command
+        # default implementation
+        ""
+      end
+    end
+    
+    module InstanceMethods
+      def after_initialize
+        self.checkout_settings ||= {}
+      end
+    
+      def checkout_overwrite=(value)
+        checkout_settings['checkout_overwrite'] = value
+      end
+    
+      def checkout_overwrite
+        (checkout_settings['checkout_overwrite'].to_i > 0) ? '1' : '0'
+      end
+
+      def checkout_overwrite?
+        self.scm_name != 'Abstract' && checkout_overwrite.to_i > 0
+      end
+    
+      def checkout_description=(value)
+        checkout_settings['checkout_description'] = value
+      end
+    
+      def checkout_description
+        if checkout_overwrite?
+          checkout_settings['checkout_description']
+        else
+          if CheckoutHelper.supported_scm.include?(scm_name) && Setting.send("checkout_overwrite_description_#{scm_name}?")
+            Setting.send("checkout_description_#{scm_name}")
+          else
+            Setting.send("checkout_description_Abstract")
+          end
+        end
+      end
+    
+      def checkout_protocols
+        @checkout_protocols ||= begin
+          if CheckoutHelper.supported_scm.include? scm_name
+            if checkout_overwrite?
+              protocols = checkout_settings['checkout_protocols'] || []
+            else
+              protocols = Setting.send("checkout_protocols_#{scm_name}") || []
+            end
+          else
+            protocols = []
+          end
+          
+          protocols.collect do |p|
+            Checkout::Protocol.new p.merge({:repository => self})
+          end
+        end
+      end
+    
+      def checkout_protocols=(value)
+        # value is an Array or a Hash
+        if value.is_a? Hash
+          value = value.dup.delete_if {|id, protocol| id.to_i < 0 }
+          value = value.sort{|(ak,av),(bk,bv)|ak<=>bk}.collect{|id,protocol| protocol}
+        end
+        
+        checkout_settings['checkout_protocols'] = value
+      end
+
+      def checkout_display_login
+        if checkout_overwrite? && self.scm_name == "Subversion"
+          result = checkout_settings['checkout_display_login']
+        else
+          result = Setting.checkout_display_login
+        end
+        (result.to_i > 0) ? '1' : '0'
+      end
+    
+      def checkout_display_login?
+        checkout_display_login.to_i > 0
+      end
+    
+      def checkout_display_login=(value)
+        value = nil unless self.scm_name == "Subversion"
+        checkout_settings['checkout_display_login'] = value
+      end
+      
+      def checkout_display_command?
+        checkout_display_command.to_i > 0
+      end
+      
+      def checkout_display_command=(value)
+        checkout_settings['checkout_display_command'] = value
+      end
+      
+      def checkout_display_command
+        if checkout_overwrite?
+          checkout_settings['checkout_display_command']
+        else
+          Setting.send("checkout_display_command_#{scm_name}")
+        end
+      end
+      
+      def allow_subtree_checkout?
+        self.class.allow_subtree_checkout?
+      end
+      
+      def checkout_default_command
+        self.class.checkout_default_command
+      end
+    end
+  end
+end
+
+Repository.send(:include, Checkout::RepositoryPatch)
+
+subtree_checkout_repos = ["Subversion", "Cvs"]
+commands = {
+  'Bazaar' => 'bzr checkout',
+  'Cvs' => 'cvs checkout',
+  'Darcs' => 'darcs get',
+  'Git' => 'git clone',
+  'Mercurial' => 'hg clone',
+  'Subversion' => 'svn checkout'
+}
+
+CheckoutHelper.supported_scm.each do |scm|
+  require_dependency "repository/#{scm.underscore}"
+  cls = Repository.const_get(scm)
+  
+  allow_subtree_checkout = ""
+  if subtree_checkout_repos.include? scm
+    allow_subtree_checkout = <<-EOS
+     def allow_subtree_checkout?
+        true
+      end
+    EOS
+  end
+  
+  checkout_command = ""
+  if commands[scm]
+    checkout_command = <<-EOS
+      def checkout_default_command
+        '#{commands[scm]}'
+      end
+    EOS
+  end
+  
+  class_mod = Module.new
+  class_mod.module_eval(<<-EOF
+    def self.included(base)
+      base.extend ChildClassMethods
+
+      base.class_eval do
+        unloadable
+        serialize :checkout_settings, Hash
+      end
+    end
+
+    module ChildClassMethods
+      #{allow_subtree_checkout}
+      #{checkout_command}
+    end
+  EOF
+  )
+  cls.send(:include, class_mod)
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/lib/checkout/setting_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,75 @@
+require_dependency 'setting'
+
+module Checkout
+  module SettingPatch
+    def self.included(base) # :nodoc:
+      base.extend(ClassMethods)
+      
+      base.class_eval do
+        unloadable
+        
+        # Defines getter and setter for each setting
+        # Then setting values can be read using: Setting.some_setting_name
+        # or set using Setting.some_setting_name = "some value"
+        Redmine::Plugin.find(:redmine_checkout).settings[:default].keys.each do |name|
+          if name.start_with?('protocols_')
+            default = "[]"
+          else
+            default = <<-END_SRC
+              begin
+                default = Setting.available_settings['plugin_redmine_checkout']['default']['#{name}']
+                # perform a deep copy of the default
+                Marshal::load(Marshal::dump(default))
+              end
+            END_SRC
+          end
+          
+          src = <<-END_SRC
+            def self.checkout_#{name}
+              self.plugin_redmine_checkout[:#{name}] || #{default}
+            end
+
+            def self.checkout_#{name}?
+              self.checkout_#{name}.to_i > 0
+            end
+
+            def self.checkout_#{name}=(value)
+              setting = Setting.plugin_redmine_checkout
+              setting[:#{name}] = value
+              Setting.plugin_redmine_checkout = setting
+            end
+          END_SRC
+          class_eval src, __FILE__, __LINE__
+        end
+        
+        class <<self
+          alias_method :store_without_checkout, :[]=
+          alias_method :[]=, :store_with_checkout
+          
+          alias_method :retrieve_without_checkout, :[]
+          alias_method :[], :retrieve_with_checkout
+        end
+      end
+    end
+    
+    module ClassMethods
+      def store_with_checkout(name, value)
+        if name.to_s.starts_with? "checkout_"
+          self.send("#{name}=", value)
+        else
+          store_without_checkout(name, value)
+        end
+      end
+      
+      def retrieve_with_checkout(name)
+        if name.to_s.starts_with? "checkout_"
+          self.send("#{name}")
+        else
+          retrieve_without_checkout(name)
+        end
+      end
+    end
+  end
+end
+
+Setting.send(:include, Checkout::SettingPatch)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/lib/checkout/settings_controller_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,44 @@
+require_dependency 'settings_controller'
+
+module Checkout
+  module SettingsControllerPatch
+    def self.included(base) # :nodoc:
+      base.send(:include, InstanceMethods)
+
+      base.class_eval do
+        unloadable
+      
+        alias_method_chain :edit, :checkout
+      end
+    end
+    
+    module InstanceMethods
+      def edit_with_checkout
+        if request.post? && params['tab'] == 'checkout'
+          if params[:settings] && params[:settings].is_a?(Hash)
+            settings = HashWithIndifferentAccess.new
+            (params[:settings] || {}).each do |name, value|
+              if name = name.to_s.slice(/checkout_(.+)/, 1)
+                case value
+                when Array
+                  # remove blank values in array settings
+                  value.delete_if {|v| v.blank? }
+                when Hash
+                  # change protocols hash to array.
+                  value = value.sort{|(ak,av),(bk,bv)|ak<=>bk}.collect{|id,protocol| protocol} if name.start_with? "protocols_"
+                end
+                settings[name.to_sym] = value
+              end
+            end
+                        
+            Setting.plugin_redmine_checkout = settings
+            params[:settings] = {}
+          end
+        end
+        edit_without_checkout
+      end
+    end
+  end
+end
+
+SettingsController.send(:include, Checkout::SettingsControllerPatch)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/lib/checkout/settings_helper_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,23 @@
+require_dependency 'settings_helper'
+
+module Checkout
+  module SettingsHelperPatch
+    def self.included(base) # :nodoc:
+      base.send(:include, InstanceMethods)
+
+      base.class_eval do
+        alias_method_chain :administration_settings_tabs, :checkout
+      end
+    end
+  
+    module InstanceMethods
+      def administration_settings_tabs_with_checkout
+        tabs = administration_settings_tabs_without_checkout
+        tabs << {:name => 'checkout', :partial => 'settings/checkout', :label => :label_checkout}
+      end
+    end
+  end
+end
+
+SettingsHelper.send(:include, Checkout::SettingsHelperPatch)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/lib/checkout_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,7 @@
+module CheckoutHelper
+  class <<self
+    def supported_scm
+      Object.const_defined?("REDMINE_SUPPORTED_SCM") ? REDMINE_SUPPORTED_SCM : Redmine::Scm::Base.all
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/lib/tasks/set_default.rake	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,10 @@
+namespace :redmine do
+  namespace :plugins do
+    namespace :redmine_checkout do
+      desc "Sets all repositories to inherit the default setting for the checkout URL."
+      task :set_default => :environment do
+        Repository.all.each{|r| r.update_attributes(:checkout_overwrite => "0")}
+      end
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/lib/tasks/spec.rake	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,14 @@
+begin
+  require "spec/rake/spectask"
+  namespace :spec do
+    namespace :plugins do
+      desc "Runs the examples for redmine_checkout"
+      Spec::Rake::SpecTask.new(:redmine_checkout) do |t|
+        t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
+        t.spec_files = FileList['vendor/plugins/redmine_checkout/spec/**/*_spec.rb']
+      end
+    end
+  end
+  task :spec => "spec:plugins:redmine_checkout"
+rescue LoadError
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/controllers/repositories_controller_spec.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,48 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe RepositoriesController do
+  fixtures :settings, :repositories, :projects, :roles, :users, :enabled_modules
+  integrate_views
+  
+  before(:each) do
+    Setting.default_language = 'en'
+    User.current = nil
+  end
+  
+  def get_repo
+    get :show, :id => 1
+  end
+  
+  it "should display the protocol selector" do
+    get_repo
+    response.should be_success
+    response.should render_template('show')
+    
+    response.should have_tag('ul#checkout_protocols') do
+      with_tag('a[id=?][href=?]', 'checkout_protocol_subversion', "file:///#{RAILS_ROOT.gsub(%r{config\/\.\.}, '')}/tmp/test/subversion_repository")
+      with_tag('a[id=?][href=?]', 'checkout_protocol_svn+ssh', 'svn+ssh://subversion_repository@svn.foo.bar/svn')
+    end
+  end
+  
+  it "should display the description" do
+    get_repo
+    response.should be_success
+    response.should render_template('show')
+    
+    response.should have_tag('div.repository-info', /Please select the desired protocol below to get the URL/)
+  end
+  
+  it 'should respect the use zero clipboard option' do
+    Setting.checkout_use_zero_clipboard = '1'
+    get_repo
+    response.should be_success
+    response.should render_template('show')
+    response.should have_tag('script[src*=?]', 'ZeroClipboard')
+
+    Setting.checkout_use_zero_clipboard = '0'
+    get_repo
+    response.should be_success
+    response.should render_template('show')
+    response.should_not have_tag('script[src*=]', 'ZeroClipboard')
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/fixtures/enabled_modules.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,5 @@
+---
+enabled_modules_001:
+  name: repository
+  project_id: 1
+  id: 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/fixtures/projects.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,13 @@
+--- 
+projects_001: 
+  created_on: 2006-07-19 19:13:59 +02:00
+  name: eCookbook
+  updated_on: 2006-07-19 22:53:01 +02:00
+  id: 1
+  description: Recipes management application
+  homepage: http://ecookbook.somenet.foo/
+  is_public: true
+  identifier: ecookbook
+  parent_id: 
+  lft: 1
+  rgt: 10
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/fixtures/repositories.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+---
+svn: 
+  project_id: 1
+  url: file:///<%= RAILS_ROOT.gsub(%r{config\/\.\.}, '') %>/tmp/test/subversion_repository
+  id: 1
+  root_url: file:///<%= RAILS_ROOT.gsub(%r{config\/\.\.}, '') %>/tmp/test/subversion_repository
+  password: ""
+  login: ""
+  type: Subversion
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/fixtures/roles.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,187 @@
+--- 
+roles_001: 
+  name: Manager
+  id: 1
+  builtin: 0
+  permissions: |
+    --- 
+    - :add_project
+    - :edit_project
+    - :manage_members
+    - :manage_versions
+    - :manage_categories
+    - :view_issues
+    - :add_issues
+    - :edit_issues
+    - :manage_issue_relations
+    - :manage_subtasks
+    - :add_issue_notes
+    - :move_issues
+    - :delete_issues
+    - :view_issue_watchers
+    - :add_issue_watchers
+    - :delete_issue_watchers
+    - :manage_public_queries
+    - :save_queries
+    - :view_gantt
+    - :view_calendar
+    - :log_time
+    - :view_time_entries
+    - :edit_time_entries
+    - :delete_time_entries
+    - :manage_news
+    - :comment_news
+    - :view_documents
+    - :manage_documents
+    - :view_wiki_pages
+    - :export_wiki_pages
+    - :view_wiki_edits
+    - :edit_wiki_pages
+    - :delete_wiki_pages_attachments
+    - :protect_wiki_pages
+    - :delete_wiki_pages
+    - :rename_wiki_pages
+    - :add_messages
+    - :edit_messages
+    - :delete_messages
+    - :manage_boards
+    - :view_files
+    - :manage_files
+    - :browse_repository
+    - :manage_repository
+    - :view_changesets
+    - :manage_project_activities
+
+  position: 1
+roles_002: 
+  name: Developer
+  id: 2
+  builtin: 0
+  permissions: |
+    --- 
+    - :edit_project
+    - :manage_members
+    - :manage_versions
+    - :manage_categories
+    - :view_issues
+    - :add_issues
+    - :edit_issues
+    - :manage_issue_relations
+    - :manage_subtasks
+    - :add_issue_notes
+    - :move_issues
+    - :delete_issues
+    - :view_issue_watchers
+    - :save_queries
+    - :view_gantt
+    - :view_calendar
+    - :log_time
+    - :view_time_entries
+    - :edit_own_time_entries
+    - :manage_news
+    - :comment_news
+    - :view_documents
+    - :manage_documents
+    - :view_wiki_pages
+    - :view_wiki_edits
+    - :edit_wiki_pages
+    - :protect_wiki_pages
+    - :delete_wiki_pages
+    - :add_messages
+    - :edit_own_messages
+    - :delete_own_messages
+    - :manage_boards
+    - :view_files
+    - :manage_files
+    - :browse_repository
+    - :view_changesets
+
+  position: 2
+roles_003: 
+  name: Reporter
+  id: 3
+  builtin: 0
+  permissions: |
+    --- 
+    - :edit_project
+    - :manage_members
+    - :manage_versions
+    - :manage_categories
+    - :view_issues
+    - :add_issues
+    - :edit_issues
+    - :manage_issue_relations
+    - :add_issue_notes
+    - :move_issues
+    - :view_issue_watchers
+    - :save_queries
+    - :view_gantt
+    - :view_calendar
+    - :log_time
+    - :view_time_entries
+    - :manage_news
+    - :comment_news
+    - :view_documents
+    - :manage_documents
+    - :view_wiki_pages
+    - :view_wiki_edits
+    - :edit_wiki_pages
+    - :delete_wiki_pages
+    - :add_messages
+    - :manage_boards
+    - :view_files
+    - :manage_files
+    - :browse_repository
+    - :view_changesets
+
+  position: 3
+roles_004: 
+  name: Non member
+  id: 4
+  builtin: 1
+  permissions: |
+    --- 
+    - :view_issues
+    - :add_issues
+    - :edit_issues
+    - :manage_issue_relations
+    - :add_issue_notes
+    - :move_issues
+    - :save_queries
+    - :view_gantt
+    - :view_calendar
+    - :log_time
+    - :view_time_entries
+    - :comment_news
+    - :view_documents
+    - :manage_documents
+    - :view_wiki_pages
+    - :view_wiki_edits
+    - :edit_wiki_pages
+    - :add_messages
+    - :view_files
+    - :manage_files
+    - :browse_repository
+    - :view_changesets
+
+  position: 4
+roles_005: 
+  name: Anonymous
+  id: 5
+  builtin: 2
+  permissions: |
+    --- 
+    - :view_issues
+    - :add_issue_notes
+    - :view_gantt
+    - :view_calendar
+    - :view_time_entries
+    - :view_documents
+    - :view_wiki_pages
+    - :view_wiki_edits
+    - :view_files
+    - :browse_repository
+    - :view_changesets
+
+  position: 5
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/fixtures/settings.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,115 @@
+---
+  settings:
+    name: plugin_redmine_checkout
+    value: |
+      --- !map:HashWithIndifferentAccess 
+      display_checkout_info: "1"
+      description_Abstract: |
+        The data contained in this repository can be downloaded to your computer using one of several clients.
+        Please see the documentation of your version control software client for more information.
+
+        Please select the desired protocol below to get the URL.
+      display_command_Bazaar: '1'
+      use_zero_clipboard: "1"
+
+      overwrite_description_Bazaar: "0"
+      description_Bazaar: ""
+      display_command_Bazaar: '1'
+      protocols_Bazaar: 
+        "0": !map:HashWithIndifferentAccess 
+          command: "bzr checkout"
+          regex: ""
+          regex_replacement: ""
+          read_write: readwrite
+          append_path: "0"
+          is_default: "1"
+          protocol: Bazaar
+      overwrite_description_Cvs: "0"
+      description_Cvs: ""
+      display_command_Cvs: '1'
+      protocols_Cvs: 
+        "0": !map:HashWithIndifferentAccess 
+          command: "cvs checkout"
+          regex: ""
+          regex_replacement: ""
+          read_write: readwrite
+          append_path: "0"
+          is_default: "1"
+          protocol: Cvs
+      overwrite_description_Darcs: "0"
+      description_Darcs: ""
+      display_command_Darcs: '1'
+      protocols_Darcs: 
+        "0": !map:HashWithIndifferentAccess 
+          command: "darcs get"
+          regex: ""
+          regex_replacement: ""
+          read_write: readwrite
+          append_path: "0"
+          is_default: "1"
+          protocol: Darcs
+      overwrite_description_Filesystem: "0"
+      description_Filesystem: ""
+      display_command_Filesystem: '1'
+      protocols_Filesystem: !map:HashWithIndifferentAccess 
+        "0": !map:HashWithIndifferentAccess 
+          command: ""
+          regex: ""
+          append_path: "0"
+          is_default: "1"
+          protocol: Filesystem
+          access: read+write
+          regex_replacement: ""
+      overwrite_description_Git: "0"
+      description_Git: ""
+      display_command_Git: '1'
+      protocols_Git: !map:HashWithIndifferentAccess 
+        "0": !map:HashWithIndifferentAccess 
+          command: "git clone"
+          regex: ""
+          append_path: "0"
+          is_default: "1"
+          protocol: Git
+          access: read+write
+          regex_replacement: ""
+      overwrite_description_Mercurial: "0"
+      description_Mercurial: ""
+      display_command_Mercurial: '1'
+      protocols_Mercurial: !map:HashWithIndifferentAccess 
+        "0": !map:HashWithIndifferentAccess 
+          command: "hg clone"
+          regex: ""
+          append_path: "0"
+          is_default: "1"
+          protocol: Mercurial
+          access: read+write
+          regex_replacement: ""
+      display_login: username
+      overwrite_description_Subversion: "0"
+      description_Subversion: ""
+      display_command_Subversion: '1'
+      protocols_Subversion: !map:HashWithIndifferentAccess 
+        "0": !map:HashWithIndifferentAccess 
+          command: "svn checkout"
+          regex: foo
+          append_path: "1"
+          is_default: "1"
+          protocol: Subversion
+          access: permission
+          regex_replacement: bar
+        "1": !map:HashWithIndifferentAccess 
+          command: "svn co"
+          regex: "^.*?([^/]+)/?$"
+          append_path: "1"
+          is_default: "0"
+          protocol: SVN+SSH
+          access: read-only
+          regex_replacement: svn+ssh://\1@svn.foo.bar/svn
+        "2": !map:HashWithIndifferentAccess 
+          command: "svn checkout"
+          append_path: "0"
+          is_default: "0"
+          regex: ""
+          protocol: Root
+          access: read+write
+          regex_replacement: ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/macros/macro_spec.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,55 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe "Macros" do
+  fixtures :settings, :repositories, :projects, :enabled_modules
+  
+  include ERB::Util
+  include ApplicationHelper
+  include ActionView::Helpers::TextHelper
+  include ActionView::Helpers::TagHelper
+  include ActionView::Helpers::UrlHelper
+
+  before(:each) do
+    Setting.checkout_display_command_Subversion = '0'
+    
+    @project = projects :projects_001
+  end
+  
+  
+  it "should display default checkout url" do
+    text = "{{repository}}"
+    
+    url = "file:///#{RAILS_ROOT.gsub(%r{config\/\.\.}, '')}/tmp/test/subversion_repository"
+    textilizable(text).should eql "<p><a href=\"#{url}\">#{url}</a></p>"
+  end
+
+  it "should display forced checkout url" do
+    text = "{{repository(svn+ssh)}}"
+    
+    url = 'svn+ssh://subversion_repository@svn.foo.bar/svn'
+    textilizable(text).should eql "<p><a href=\"#{url}\">#{url}</a></p>"
+  end
+
+  it "should fail without set project" do
+    @project = nil
+    
+    text = "{{repository(svn+ssh)}}"
+    textilizable(text).should eql "<p><div class=\"flash error\">Error executing the <strong>repository</strong> macro (Checkout protocol svn+ssh not found)</div></p>"
+  end
+
+  it "should display checkout url from stated project" do
+    @project = nil
+    text = "{{repository(ecookbook:svn+ssh)}}"
+    
+    url = 'svn+ssh://subversion_repository@svn.foo.bar/svn'
+    textilizable(text).should eql "<p><a href=\"#{url}\">#{url}</a></p>"
+  end
+  
+  it "should display command" do
+    Setting.checkout_display_command_Subversion = '1'
+    
+    text = "{{repository(svn+ssh)}}"
+    url = 'svn+ssh://subversion_repository@svn.foo.bar/svn'
+    textilizable(text).should eql "<p>svn co <a href=\"#{url}\">#{url}</a></p>"
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/models/protocol_spec.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,36 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe Checkout::Protocol do
+  fixtures :settings, :repositories, :projects, :enabled_modules
+  
+  before(:each) do
+    @admin = User.new
+    @admin.admin = true
+    @user = User.new
+    
+    @repo = repositories :svn
+    @repo.url = "http://example.com/svn/testrepo"
+  end
+  
+  it "should use regexes for generated URL" do
+    protocol = @repo.checkout_protocols.find{|r| r.protocol == "SVN+SSH"}
+    protocol.url.should eql "svn+ssh://testrepo@svn.foo.bar/svn"
+  end
+  
+  it "should resolve access properties" do
+    protocol = @repo.checkout_protocols.find{|r| r.protocol == "Subversion"}
+    protocol.access.should eql "permission"
+    protocol.access_rw(@admin).should eql "read+write"
+    
+    User.current = @user
+    protocol.access_rw(@user).should eql "read-only"
+  end
+  
+  it "should display the checkout command" do
+    subversion = @repo.checkout_protocols.find{|r| r.protocol == "Subversion"}
+    svn_ssh = @repo.checkout_protocols.find{|r| r.protocol == "SVN+SSH"}
+
+    subversion.command.should eql "svn checkout"
+    svn_ssh.command.should eql "svn co"
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/models/repository_spec.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,49 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe Repository do
+  fixtures :settings, :repositories
+  
+  describe "initialize" do
+    before(:each) do
+      @repo = Repository.new()
+    end
+
+    it "should properly set default values" do
+      @repo.checkout_overwrite?.should be_false
+      @repo.checkout_description.should match /Please select the desired protocol below to get the URL/
+      @repo.checkout_display_login?.should be_false # no subversion repo
+      @repo.allow_subtree_checkout?.should be_false
+      @repo.checkout_protocols.should eql []
+    end
+  end
+  
+  describe "subtree checkout" do
+    before(:each) do
+      @svn = Repository::Subversion.new
+      @git = Repository::Git.new
+    end
+    it "should be allowed on subversion" do
+      @svn.allow_subtree_checkout?.should eql true
+    end
+    it "should only be possible if checked" do
+      
+    end
+    
+    it "should be forbidden on git" do
+      @git.allow_subtree_checkout?.should eql false
+    end
+  end
+  
+  describe "extensions" do
+    before(:each) do
+      @repo = Repository::Subversion.new
+    end
+    
+    it "should provide protocols" do
+      protocols = @repo.checkout_protocols
+      protocols[0].protocol.should eql "Subversion"
+      protocols[1].protocol.should eql "SVN+SSH"
+      protocols[2].protocol.should eql "Root"
+    end
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/models/setting_spec.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,14 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe Setting do
+  fixtures :settings
+  
+  before(:each) do
+    Setting.default_language = 'en'
+  end
+  
+  it "should recognize checkout methods" do
+    Setting.checkout_display_checkout_info.should eql Setting.plugin_redmine_checkout['display_checkout_info']
+    Setting.checkout_display_checkout_info.should eql Setting.plugin_redmine_checkout[:display_checkout_info]
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/sanity_spec.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,11 @@
+require File.dirname(__FILE__) + '/spec_helper'
+ 
+describe Class do
+  it "should be a class of Class" do
+    Class.class.should eql(Class)
+  end
+  
+  it "should be awesome" do
+    Checkout.awesome?.should be_true
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/spec.opts	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,7 @@
+--colour
+--format
+progress
+--loadby
+mtime
+--reverse
+--backtrace
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_checkout/spec/spec_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,27 @@
+ENV['RAILS_ENV'] ||= 'test'
+
+# prevent case where we are using rubygems and test-unit 2.x is installed
+begin
+  require 'rubygems'
+  gem "test-unit", "~> 1.2.3"
+rescue LoadError
+end
+
+begin
+  require "config/environment" unless defined? RAILS_ROOT
+  require RAILS_ROOT + '/spec/spec_helper'
+rescue LoadError => error
+  puts <<-EOS
+
+    You need to install rspec in your Redmine project.
+    Please execute the following code:
+    
+      gem install rspec-rails
+      script/generate rspec
+
+  EOS
+  raise error
+end
+
+Fixtures.create_fixtures File.join(File.dirname(__FILE__), "fixtures"), ActiveRecord::Base.connection.tables
+require File.join(File.dirname(__FILE__), "..", "init.rb")
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/COPYING	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/README.md	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,8 @@
+Redmine  Embedded
+================
+
+Plugin for Redmine to embed Doxygen/Javadoc files based on the original by Jean-Philippe Lang but updated for Redmine 2 and Rails 3
+
+To use, create a directory for the HTML documentation and set the owner to the same user as the webserver user running your Redmine installation. Default setting is "/var/doc".
+
+Documentation should be a zip of the root html directory, prefereably compiled without search (CSS needs some work).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/app/controllers/redmine_embedded_controller.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,152 @@
+# Redmine - project management software
+# Copyright (C) 2008  Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require 'iconv'
+
+class DataFile < ActiveRecord::Base
+  def self.save(directory, zipname, upload)
+    path = File.join(directory, zipname)
+    File.open(path, "wb") { |f| f.write(upload['datafile'].read) }
+  end
+end
+
+class RedmineEmbeddedController < ApplicationController
+  class RedmineEmbeddedControllerError < StandardError; end
+  
+  unloadable
+  layout 'base'
+  before_filter :find_project, :authorize
+  
+  def index
+    file = params[:request_path]
+    path = get_real_path(file)
+    if File.directory?(path)
+      file = get_index_file(path)
+      target = file || []
+      #target << file
+      # Forces redirect to the index file when the requested path is a directory
+      # so that relative links in embedded html pages work
+      redirect_to :request_path => target
+      return
+    end
+    
+    # Check file extension
+    raise RedmineEmbeddedControllerError.new('This file can not be viewed (invalid extension).') unless Redmine::Plugins::RedmineEmbedded.valid_extension?(path)
+    
+    if Redmine::MimeType.is_type?('image', path)
+      send_file path, :disposition => 'inline', :type => Redmine::MimeType.of(path)
+    else
+      embed_file path
+    end
+    
+  rescue Errno::ENOENT => e
+    @content = "No documentation found"
+    @title = ""
+    render :index
+  rescue Errno::EACCES => e
+    # Can not read the file
+    render_error "Unable to read the file: #{e.message}"
+  rescue RedmineEmbeddedControllerError => e
+    render_error e.message
+  end
+
+  def upload
+    if params[:upload]
+      file = params[:upload]
+      zipname = sanitize_filename(params[:upload]['datafile'].original_filename)
+      if ["zip"].include?(File.extname(zipname).downcase[1..-1])
+        dir = get_project_directory.gsub("/html", "")
+        if File.directory? dir
+          `rm -rf #{dir}/*` #clean up any exisiting docs
+        else
+         Dir.mkdir dir 
+        end
+        filename = DataFile.save(dir, zipname, params[:upload])
+        Dir.chdir(dir)
+        `unzip #{zipname}`
+        redirect_to show_embedded_url(@project), :notice => "Documentation uploaded"
+      else 
+        render :index, :error => "File must be ZIP format"
+      end
+    else
+      render :index, :error => "No file uploaded"
+    end
+  end
+  
+  private
+  
+  def sanitize_filename(file_name)
+    # get only the filename, not the whole path (from IE)
+    just_filename = File.basename(file_name) 
+    # replace all none alphanumeric, underscore or perioids
+    # with underscore
+    just_filename.sub(/[^\w\.\-]/,'_') 
+  end
+  
+  def find_project
+    @project = Project.find(params[:id])
+  rescue ActiveRecord::RecordNotFound
+    render_404
+  end
+  
+  # Return the path to the html root directory for the current project
+  def get_project_directory
+    @project_directory ||= Setting.plugin_redmine_embedded['path'].to_s.gsub('{PROJECT}', @project.identifier)
+  end
+  
+  # Returns the absolute path of the requested file
+  # Parameter is an Array
+  def get_real_path(path)
+    real = get_project_directory
+    real = File.join(real, path) unless path.nil? || path.empty?
+    dir = File.expand_path(get_project_directory)
+    real = File.expand_path(real)
+    raise Errno::ENOENT unless real.starts_with?(dir) && File.exist?(real)
+    real
+  end
+  
+  # Returns the index file in the given directory
+  # and raises an exception if none is found
+  def get_index_file(dir)
+    indexes = Setting.plugin_redmine_embedded['index'].to_s.split
+    file = indexes.find {|f| File.exist?(File.join(dir, f))}
+    raise RedmineEmbeddedControllerError.new("No index file found in #{dir} (#{indexes.join(', ')}).") if file.nil?
+    file
+  end
+  
+  # Renders a given HTML file
+  def embed_file(path)
+    @content = File.read(path)
+    
+    # Extract html title from embedded page
+    if @content =~ %r{<title>([^<]*)</title>}mi
+      @title = $1.strip
+    end
+    
+    # Keep html body only
+    @content.gsub!(%r{^.*<body[^>]*>(.*)</body>.*$}mi, '\\1')
+    
+    # Re-encode content if needed
+    source_encoding = Setting.plugin_redmine_embedded['encoding'].to_s
+    unless source_encoding.blank?
+      begin; @content = Iconv.new('UTF-8', source_encoding).iconv(@content); rescue; end
+    end
+    
+    @doc_template = Redmine::Plugins::RedmineEmbedded.detect_template_from_path(path)
+    render :action => 'index'
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/app/helpers/redmine_embedded_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,35 @@
+# Redmine - project management software
+# Copyright (C) 2008  Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+module RedmineEmbeddedHelper
+
+  # Adds include tags for assets of the given template
+  def asset_include_tags(template)
+  
+    Redmine::Plugins::RedmineEmbedded.assets(template).each { |asset| content_for(:header_tags) { asset_include_tag(asset) } }
+  end
+  
+  private
+
+  def asset_include_tag(asset)
+    if asset =~ %r{\.js$}
+      javascript_include_tag(asset, :plugin => 'redmine_embedded')
+    else
+      stylesheet_link_tag(asset, :plugin => 'redmine_embedded')
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/app/views/redmine_embedded/_upload.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,14 @@
+<div class="row">
+  <%= form_tag(upload_embedded_path(@project), :multipart => true) do -%> 
+    <fieldset id="embedded_doc" class="collapsible collapsed">
+      <legend onclick="toggleFieldset(this);">Upload documentation</legend>
+      <div style="display: none;">
+        Select File (ZIP format only, root must be html directory): 
+        <%= file_field "upload", "datafile" %>
+        <%= submit_tag "Upload" %>
+        </div>
+    </fieldset>
+  <% end -%>
+</div>  
+<div class="clearfix"></div>
+<hr />
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/app/views/redmine_embedded/index.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,9 @@
+<%= render :partial => "upload" if User.current.allowed_to?(:edit_embedded_doc, @project) -%>
+
+<!-- Embedded page -->
+<%= @content.html_safe %>
+<!-- Embedded page end -->
+
+<% html_title(@title) if @title %>
+
+<% asset_include_tags(@doc_template) %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/app/views/settings/_redmine_embedded.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,22 @@
+<p><label>HTML directory</label>
+<%= text_field_tag 'settings[path]', @settings['path'], :size => 80 %>
+<br /><em>Use {PROJECT} to include the project identifier in the path</em></p>
+
+<p><label>Index files</label>
+<%= text_area_tag 'settings[index]', @settings['index'], :cols => 60, :rows => 3 %>
+<br /><em>Space separated list of index files by priority</em></p>
+
+<p><label>Valid extensions</label>
+<%= text_area_tag 'settings[extensions]', @settings['extensions'], :cols => 60, :rows => 3 %>
+<br /><em>Space separated list of file extensions that can be viewed (case insensitive)</em></p>
+
+<p><label>Default template</label>
+<%= select_tag 'settings[template]', options_for_select([''] + Redmine::Plugins::RedmineEmbedded.available_templates, @settings['template']) %></p>
+
+<p><label>Files encoding</label>
+<%= text_field_tag 'settings[encoding]', @settings['encoding'] %>
+<br /><em>Eg. ISO-8859-1<br />Leave this field empty if HTML files are UTF-8 encoded</em></p>
+
+<p><label>Menu caption</label>
+<%= text_field_tag 'settings[menu]', @settings['menu'], :size => 30 %>
+<br /><em>Clear this field if you don't want to add a tab to the project menu</em></p>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/assets/javascripts/rcov.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,18 @@
+function toggleCode( id ) {
+  if ( document.getElementById )
+    elem = document.getElementById( id );
+  else if ( document.all )
+    elem = eval( "document.all." + id );
+  else
+    return false;
+
+  elemStyle = elem.style;
+  
+  if ( elemStyle.display != "block" ) {
+    elemStyle.display = "block"
+  } else {
+    elemStyle.display = "none"
+  }
+
+  return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/assets/stylesheets/doxygen.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,224 @@
+#content {
+	font-family: Geneva, Arial, Helvetica, sans-serif;
+}
+#content h1, h2, h3 {border: 0;}
+#content h1 {margin-bottom: 1em;}
+#content CAPTION { font-weight: bold }
+#content DIV.qindex {
+	line-height: 120%;
+	margin-bottom: 1em;
+}
+#content A.qindex {
+   text-decoration: none;
+   font-weight: bold;
+   padding: 2px;
+}
+#content A.qindexHL {
+	text-decoration: underline;
+    font-weight: bold;
+	padding: 2px;
+}
+#content A.el { text-decoration: none; font-weight: bold }
+#content A.elRef { font-weight: bold }
+#content A.code { text-decoration: none; font-weight: normal; color: #1A419D}
+#content A.codeRef { font-weight: normal; color: #1A419D}
+#content DL.el { margin-left: -1cm }
+#content PRE.fragment {
+	border: 1px solid #CCCCCC;
+	background-color: #f5f5f5;
+	margin-top: 4px;
+	margin-bottom: 4px;
+	margin-left: 2px;
+	margin-right: 8px;
+	padding-left: 6px;
+	padding-right: 6px;
+	padding-top: 4px;
+	padding-bottom: 4px;
+}
+#content DIV.fragment {
+	border: 1px solid #CCCCCC;
+	background-color: #f5f5f5;
+	padding: 6px;
+}
+#content DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px }
+#content TD.md { background-color: #F4F4FB; font-weight: bold; }
+#content TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; }
+#content TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; }
+#content DIV.groupHeader {
+       margin-left: 16px;
+       margin-top: 12px;
+       margin-bottom: 6px;
+       font-weight: bold;
+	font-family: Geneva, Arial, Helvetica, sans-serif;
+}
+#content DIV.groupText { margin-left: 16px; font-style: italic; font-size: 14px }
+#content TD.indexkey {
+	background-color: #eeeeff;
+	font-weight: bold;
+	padding-right  : 10px;
+	padding-top    : 2px;
+	padding-left   : 10px;
+	padding-bottom : 2px;
+	margin-left    : 0px;
+	margin-right   : 0px;
+	margin-top     : 2px;
+	margin-bottom  : 2px;
+	border: 1px solid #CCCCCC;
+}
+#content TD.indexvalue {
+	background-color: #eeeeff;
+	font-style: italic;
+	padding-right  : 10px;
+	padding-top    : 2px;
+	padding-left   : 10px;
+	padding-bottom : 2px;
+	margin-left    : 0px;
+	margin-right   : 0px;
+	margin-top     : 2px;
+	margin-bottom  : 2px;
+	border: 1px solid #CCCCCC;
+}
+#content TR.memlist {
+   background-color: #f0f0f0; 
+}
+#content P.formulaDsp { text-align: center; }
+#content IMG.formulaDsp { }
+#content IMG.formulaInl { vertical-align: middle; }
+#content SPAN.keyword       { color: #008000 }
+#content SPAN.keywordtype   { color: #604020 }
+#content SPAN.keywordflow   { color: #e08000 }
+#content SPAN.comment       { color: #800000 }
+#content SPAN.preprocessor  { color: #806020 }
+#content SPAN.stringliteral { color: #002080 }
+#content SPAN.charliteral   { color: #008080 }
+#content .mdTable {
+	border: 1px solid #868686;
+	background-color: #F4F4FB;
+}
+#content .mdRow {
+	padding: 8px 10px;
+}
+#content .mdescLeft {
+       padding: 0px 8px 4px 8px;
+	font-size: 14px;
+	font-style: italic;
+	background-color: #FAFAFA;
+	border-top: 1px none #E0E0E0;
+	border-right: 1px none #E0E0E0;
+	border-bottom: 1px none #E0E0E0;
+	border-left: 1px none #E0E0E0;
+	margin: 0px;
+}
+#content .mdescRight {
+       padding: 0px 8px 4px 8px;
+	font-size: 14px;
+	font-style: italic;
+	background-color: #FAFAFA;
+	border-top: 1px none #E0E0E0;
+	border-right: 1px none #E0E0E0;
+	border-bottom: 1px none #E0E0E0;
+	border-left: 1px none #E0E0E0;
+	margin: 0px;
+}
+#content .memItemLeft {
+	padding: 1px 0px 0px 8px;
+	margin: 4px;
+	border-top-width: 1px;
+	border-right-width: 1px;
+	border-bottom-width: 1px;
+	border-left-width: 1px;
+	border-top-style: solid;
+	border-top-color: #E0E0E0;
+	border-right-color: #E0E0E0;
+	border-bottom-color: #E0E0E0;
+	border-left-color: #E0E0E0;
+	border-right-style: none;
+	border-bottom-style: none;
+	border-left-style: none;
+	background-color: #FAFAFA;
+	font-family: Geneva, Arial, Helvetica, sans-serif;
+	font-size: 12px;
+}
+#content .memItemRight {
+	padding: 1px 8px 0px 8px;
+	margin: 4px;
+	border-top-width: 1px;
+	border-right-width: 1px;
+	border-bottom-width: 1px;
+	border-left-width: 1px;
+	border-top-style: solid;
+	border-top-color: #E0E0E0;
+	border-right-color: #E0E0E0;
+	border-bottom-color: #E0E0E0;
+	border-left-color: #E0E0E0;
+	border-right-style: none;
+	border-bottom-style: none;
+	border-left-style: none;
+	background-color: #FAFAFA;
+	font-family: Geneva, Arial, Helvetica, sans-serif;
+	font-size: 13px;
+}
+#content .search     { color: #003399; font-weight: bold;
+}
+#content FORM.search {
+              margin-bottom: 0px;
+              margin-top: 0px;
+}
+#content INPUT.search { font-size: 75%;
+               color: #000080;
+               font-weight: normal;
+               background-color: #eeeeff;
+}
+#content TD.tiny      { font-size: 75%;
+}
+#content .tabs ul li a#MSearchClose { 
+  width: 12px; 
+  display: inline-block; 
+  overflow: hidden; 
+  padding: 2px; 
+  height: 12px; 
+  line-height: 12px; 
+}
+
+#content .tabs ul li #MSearchField {
+  width: 120px;
+  margin: 0;
+}
+
+#content .tabs ul li #MSearchBox {
+  margin-bottom: -2px;
+}
+
+#content .memitem {
+    padding: 0;
+    margin-bottom: 10px;
+}
+#content .memname {
+    white-space: nowrap;
+    font-weight: bold;
+}
+#content .memproto, .memdoc {
+    border: 1px solid #84b0c7;    
+}
+#content .memdoc {
+    padding: 2px 5px;
+    background-color: #eef3f5;
+    border-top-width: 0;
+    -webkit-border-bottom-left-radius: 8px;
+    -webkit-border-bottom-right-radius: 8px;
+        -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+    -moz-border-radius-bottomleft: 8px;
+    -moz-border-radius-bottomright: 8px;
+        -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+}
+#content .memproto {
+    padding: 0;
+    background-color: #d5e1e8;
+    font-weight: bold;
+    -webkit-border-top-left-radius: 8px;
+    -webkit-border-top-right-radius: 8px;
+        -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+    -moz-border-radius-topleft: 8px;
+    -moz-border-radius-topright: 8px;
+        -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/assets/stylesheets/javadoc.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,23 @@
+/* Javadoc style sheet */
+
+/* Table colors */
+.TableHeadingColor     { background: #eef; } /* Dark mauve */
+.TableHeadingColor th b { font-size: 70%; }
+.TableSubHeadingColor  { background: #EEEEee;  } /* Light mauve */
+.TableRowColor         { background: #FFFFFF;  } /* White */
+
+/* Font used in left-hand frame lists */
+.FrameTitleFont   { font-size: 100%; font-family: Helvetica, Arial, sans-serif; color:#000000 }
+.FrameHeadingFont { font-size:  90%; font-family: Helvetica, Arial, sans-serif; color:#000000 }
+.FrameItemFont    { font-size:  90%; font-family: Helvetica, Arial, sans-serif; color:#000000 }
+
+/* Navigation bar fonts and colors */
+.NavBarCell1    { background-color: inherit; } /* Light mauve */
+.NavBarCell1Rev { background-color: inherit; } /* Dark Blue */
+.NavBarFont1    { font-family: Arial, Helvetica, sans-serif;}
+.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; font-style: underline; }
+
+.NavBarCell2    { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF; color:#000000; display: none;}
+.NavBarCell3    { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF; color:#000000}
+
+#content table { border-collapse: collapse; border-color: #ddd; }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/assets/stylesheets/rcov.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,525 @@
+span.cross-ref-title {
+ font-size: 140%;
+}
+span.cross-ref a {
+ text-decoration: none;
+}
+span.cross-ref {
+ background-color:#f3f7fa;
+ border: 1px dashed #333;
+ margin: 1em;
+ padding: 0.5em;
+ overflow: hidden;
+}
+a.crossref-toggle {
+ text-decoration: none;
+}
+span.marked0 {
+ background-color: rgb(185, 210, 200);
+ display: block;
+}
+span.marked1 {
+ background-color: rgb(190, 215, 205);
+ display: block;
+}
+span.inferred0 {
+ background-color: rgb(175, 200, 200);
+ display: block;
+}
+span.inferred1 {
+ background-color: rgb(180, 205, 205);
+ display: block;
+}
+span.uncovered0 {
+ background-color: rgb(225, 110, 110);
+ display: block;
+}
+span.uncovered1 {
+ background-color: rgb(235, 120, 120);
+ display: block;
+}
+span.overview {
+ border-bottom: 8px solid black;
+}
+div.overview {
+ border-bottom: 8px solid black;
+}
+#content div.footer {
+ font-size: 68%;
+ margin-top: 1.5em;
+}
+#content h1, h2, h3, h4, h5, h6 {
+ margin-bottom: 0.5em;
+}
+h5 {
+ margin-top: 0.5em;
+}
+.hidden {
+ display: none;
+}
+div.separator {
+ height: 10px;
+}
+/* Commented out for better readability, esp. on IE */
+/*
+table tr td, table tr th {
+ font-size: 68%;
+}
+td.value table tr td {
+ font-size: 11px;
+}
+*/
+table.percent_graph {
+ height: 12px;
+ border: #808080 1px solid;
+ empty-cells: show;
+}
+table.percent_graph td.covered {
+ height: 10px;
+ background: #00f000;
+}
+table.percent_graph td.uncovered {
+ height: 10px;
+ background: #e00000;
+}
+table.percent_graph td.NA {
+ height: 10px;
+ background: #eaeaea;
+}
+table.report {
+ border-collapse: collapse;
+ width: 100%;
+}
+table.report td.heading {
+ background: #dcecff;
+ border: #d0d0d0 1px solid;
+ font-weight: bold;
+ text-align: center;
+}
+table.report td.heading:hover {
+ background: #c0ffc0;
+}
+table.report td.text {
+ border: #d0d0d0 1px solid;
+}
+table.report td.value,
+table.report td.lines_total,
+table.report td.lines_code {
+ text-align: right;
+ border: #d0d0d0 1px solid;
+}
+table.report tr.light {
+ background-color: #f6f7f8;
+}
+table.report tr.dark {
+ background-color: #fff;
+}
+span.run0 {
+  background-color: rgb(178, 204, 255);
+  display: block;
+}
+span.run1 {
+  background-color: rgb(178, 206, 255);
+  display: block;
+}
+span.run2 {
+  background-color: rgb(178, 209, 255);
+  display: block;
+}
+span.run3 {
+  background-color: rgb(178, 211, 255);
+  display: block;
+}
+span.run4 {
+  background-color: rgb(178, 214, 255);
+  display: block;
+}
+span.run5 {
+  background-color: rgb(178, 218, 255);
+  display: block;
+}
+span.run6 {
+  background-color: rgb(178, 220, 255);
+  display: block;
+}
+span.run7 {
+  background-color: rgb(178, 223, 255);
+  display: block;
+}
+span.run8 {
+  background-color: rgb(178, 225, 255);
+  display: block;
+}
+span.run9 {
+  background-color: rgb(178, 228, 255);
+  display: block;
+}
+span.run10 {
+  background-color: rgb(178, 232, 255);
+  display: block;
+}
+span.run11 {
+  background-color: rgb(178, 234, 255);
+  display: block;
+}
+span.run12 {
+  background-color: rgb(178, 237, 255);
+  display: block;
+}
+span.run13 {
+  background-color: rgb(178, 239, 255);
+  display: block;
+}
+span.run14 {
+  background-color: rgb(178, 242, 255);
+  display: block;
+}
+span.run15 {
+  background-color: rgb(178, 246, 255);
+  display: block;
+}
+span.run16 {
+  background-color: rgb(178, 248, 255);
+  display: block;
+}
+span.run17 {
+  background-color: rgb(178, 251, 255);
+  display: block;
+}
+span.run18 {
+  background-color: rgb(178, 253, 255);
+  display: block;
+}
+span.run19 {
+  background-color: rgb(178, 255, 253);
+  display: block;
+}
+span.run20 {
+  background-color: rgb(178, 255, 249);
+  display: block;
+}
+span.run21 {
+  background-color: rgb(178, 255, 247);
+  display: block;
+}
+span.run22 {
+  background-color: rgb(178, 255, 244);
+  display: block;
+}
+span.run23 {
+  background-color: rgb(178, 255, 242);
+  display: block;
+}
+span.run24 {
+  background-color: rgb(178, 255, 239);
+  display: block;
+}
+span.run25 {
+  background-color: rgb(178, 255, 235);
+  display: block;
+}
+span.run26 {
+  background-color: rgb(178, 255, 233);
+  display: block;
+}
+span.run27 {
+  background-color: rgb(178, 255, 230);
+  display: block;
+}
+span.run28 {
+  background-color: rgb(178, 255, 228);
+  display: block;
+}
+span.run29 {
+  background-color: rgb(178, 255, 225);
+  display: block;
+}
+span.run30 {
+  background-color: rgb(178, 255, 221);
+  display: block;
+}
+span.run31 {
+  background-color: rgb(178, 255, 219);
+  display: block;
+}
+span.run32 {
+  background-color: rgb(178, 255, 216);
+  display: block;
+}
+span.run33 {
+  background-color: rgb(178, 255, 214);
+  display: block;
+}
+span.run34 {
+  background-color: rgb(178, 255, 211);
+  display: block;
+}
+span.run35 {
+  background-color: rgb(178, 255, 207);
+  display: block;
+}
+span.run36 {
+  background-color: rgb(178, 255, 205);
+  display: block;
+}
+span.run37 {
+  background-color: rgb(178, 255, 202);
+  display: block;
+}
+span.run38 {
+  background-color: rgb(178, 255, 200);
+  display: block;
+}
+span.run39 {
+  background-color: rgb(178, 255, 197);
+  display: block;
+}
+span.run40 {
+  background-color: rgb(178, 255, 193);
+  display: block;
+}
+span.run41 {
+  background-color: rgb(178, 255, 191);
+  display: block;
+}
+span.run42 {
+  background-color: rgb(178, 255, 188);
+  display: block;
+}
+span.run43 {
+  background-color: rgb(178, 255, 186);
+  display: block;
+}
+span.run44 {
+  background-color: rgb(178, 255, 183);
+  display: block;
+}
+span.run45 {
+  background-color: rgb(178, 255, 179);
+  display: block;
+}
+span.run46 {
+  background-color: rgb(179, 255, 178);
+  display: block;
+}
+span.run47 {
+  background-color: rgb(182, 255, 178);
+  display: block;
+}
+span.run48 {
+  background-color: rgb(184, 255, 178);
+  display: block;
+}
+span.run49 {
+  background-color: rgb(187, 255, 178);
+  display: block;
+}
+span.run50 {
+  background-color: rgb(191, 255, 178);
+  display: block;
+}
+span.run51 {
+  background-color: rgb(193, 255, 178);
+  display: block;
+}
+span.run52 {
+  background-color: rgb(196, 255, 178);
+  display: block;
+}
+span.run53 {
+  background-color: rgb(198, 255, 178);
+  display: block;
+}
+span.run54 {
+  background-color: rgb(201, 255, 178);
+  display: block;
+}
+span.run55 {
+  background-color: rgb(205, 255, 178);
+  display: block;
+}
+span.run56 {
+  background-color: rgb(207, 255, 178);
+  display: block;
+}
+span.run57 {
+  background-color: rgb(210, 255, 178);
+  display: block;
+}
+span.run58 {
+  background-color: rgb(212, 255, 178);
+  display: block;
+}
+span.run59 {
+  background-color: rgb(215, 255, 178);
+  display: block;
+}
+span.run60 {
+  background-color: rgb(219, 255, 178);
+  display: block;
+}
+span.run61 {
+  background-color: rgb(221, 255, 178);
+  display: block;
+}
+span.run62 {
+  background-color: rgb(224, 255, 178);
+  display: block;
+}
+span.run63 {
+  background-color: rgb(226, 255, 178);
+  display: block;
+}
+span.run64 {
+  background-color: rgb(229, 255, 178);
+  display: block;
+}
+span.run65 {
+  background-color: rgb(233, 255, 178);
+  display: block;
+}
+span.run66 {
+  background-color: rgb(235, 255, 178);
+  display: block;
+}
+span.run67 {
+  background-color: rgb(238, 255, 178);
+  display: block;
+}
+span.run68 {
+  background-color: rgb(240, 255, 178);
+  display: block;
+}
+span.run69 {
+  background-color: rgb(243, 255, 178);
+  display: block;
+}
+span.run70 {
+  background-color: rgb(247, 255, 178);
+  display: block;
+}
+span.run71 {
+  background-color: rgb(249, 255, 178);
+  display: block;
+}
+span.run72 {
+  background-color: rgb(252, 255, 178);
+  display: block;
+}
+span.run73 {
+  background-color: rgb(255, 255, 178);
+  display: block;
+}
+span.run74 {
+  background-color: rgb(255, 252, 178);
+  display: block;
+}
+span.run75 {
+  background-color: rgb(255, 248, 178);
+  display: block;
+}
+span.run76 {
+  background-color: rgb(255, 246, 178);
+  display: block;
+}
+span.run77 {
+  background-color: rgb(255, 243, 178);
+  display: block;
+}
+span.run78 {
+  background-color: rgb(255, 240, 178);
+  display: block;
+}
+span.run79 {
+  background-color: rgb(255, 238, 178);
+  display: block;
+}
+span.run80 {
+  background-color: rgb(255, 234, 178);
+  display: block;
+}
+span.run81 {
+  background-color: rgb(255, 232, 178);
+  display: block;
+}
+span.run82 {
+  background-color: rgb(255, 229, 178);
+  display: block;
+}
+span.run83 {
+  background-color: rgb(255, 226, 178);
+  display: block;
+}
+span.run84 {
+  background-color: rgb(255, 224, 178);
+  display: block;
+}
+span.run85 {
+  background-color: rgb(255, 220, 178);
+  display: block;
+}
+span.run86 {
+  background-color: rgb(255, 218, 178);
+  display: block;
+}
+span.run87 {
+  background-color: rgb(255, 215, 178);
+  display: block;
+}
+span.run88 {
+  background-color: rgb(255, 212, 178);
+  display: block;
+}
+span.run89 {
+  background-color: rgb(255, 210, 178);
+  display: block;
+}
+span.run90 {
+  background-color: rgb(255, 206, 178);
+  display: block;
+}
+span.run91 {
+  background-color: rgb(255, 204, 178);
+  display: block;
+}
+span.run92 {
+  background-color: rgb(255, 201, 178);
+  display: block;
+}
+span.run93 {
+  background-color: rgb(255, 198, 178);
+  display: block;
+}
+span.run94 {
+  background-color: rgb(255, 196, 178);
+  display: block;
+}
+span.run95 {
+  background-color: rgb(255, 192, 178);
+  display: block;
+}
+span.run96 {
+  background-color: rgb(255, 189, 178);
+  display: block;
+}
+span.run97 {
+  background-color: rgb(255, 187, 178);
+  display: block;
+}
+span.run98 {
+  background-color: rgb(255, 184, 178);
+  display: block;
+}
+span.run99 {
+  background-color: rgb(255, 182, 178);
+  display: block;
+}
+span.run100 {
+  background-color: rgb(255, 178, 178);
+  display: block;
+}
+pre {
+    white-space: pre-wrap; /* CSS2.1 compliant */
+    white-space: -moz-pre-wrap; /* Mozilla-based browsers */
+    white-space: -o-pre-wrap; /* Opera 7+ */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/config/routes.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,4 @@
+RedmineApp::Application.routes.draw do
+  post "/projects/:id/embedded/upload", :to => "redmine_embedded#upload", :as => :upload_embedded
+  get 'projects/:id/embedded(/*request_path(.:format))', :to => "redmine_embedded#index", :as => :show_embedded
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/init.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,28 @@
+require 'redmine'
+require 'redmine_embedded'
+
+Redmine::Plugin.register :redmine_embedded do
+  name 'Redmine Embedded Documentation' 
+  author 'Jean-Philippe Lang, Reuben Mallaby'
+  description 'Embed various documentations in your projects'
+  version '0.0.2'
+  settings  :partial => 'settings/redmine_embedded',
+            :default => { 'path' => '/var/doc/{PROJECT}/html',
+                         'index' => 'main.html overview-summary.html index.html',
+                         'extensions' => 'html png gif',
+                         'template' => '',
+                         'encoding' => '',
+                         'menu' => 'Embedded' }
+  project_module :redmine_embedded do
+    permission :view_embedded_doc, {:redmine_embedded => :index}
+    permission :edit_embedded_doc, {:redmine_embedded => :upload}
+  end
+  menu :project_menu, :redmine_embedded,
+    { 
+      :controller => "redmine_embedded", 
+      :action => "index"
+    },
+    :caption => Proc.new { Setting.plugin_redmine_embedded['menu'] },
+    :if => Proc.new { !Setting.plugin_redmine_embedded['menu'].blank? }
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_embedded/lib/redmine_embedded.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,69 @@
+# Redmine - project management software
+# Copyright (C) 2008  Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+module Redmine
+  module Plugins
+    module RedmineEmbedded
+      class << self
+        
+        # Returns an Array of available templates
+        def available_templates
+          assets_by_template.keys.sort
+        end
+        
+        # Returns the assets for a given template
+        def assets(template)
+          assets_by_template.has_key?(template) ? assets_by_template[template] : []
+        end
+        
+        def detect_template_from_path(path)
+          t = path.to_s.split(%r{[/\\]}) & available_templates
+          t.empty? ? Setting.plugin_redmine_embedded['template'].to_s : t.first
+        end
+        
+        def valid_extension?(path)
+          extensions = Setting.plugin_redmine_embedded['extensions'].to_s.split.each(&:downcase)
+          extensions.include?(File.extname(path).downcase[1..-1])
+        end
+
+        private
+        
+        # A Hash of available assets by template
+        def assets_by_template
+          @@assets_by_template ||= scan_assets
+        end
+        
+        # Scans assets directory for templates
+        # and returns a Hash of available assets by template
+        def scan_assets
+          a = Hash.new {|h,k| h[k] = [] }
+          Dir.glob(File.join(File.dirname(__FILE__), '../assets/*/*.{css,js}')).each do |asset|
+            asset = File.basename(asset)
+            template = asset.gsub(%r{\.(js|css)$}, '')
+            a[template] << asset
+          end
+          a
+        end
+      end
+    end
+  end
+end
+
+class << RedmineApp::Application;self;end.class_eval do
+  define_method :clear!, lambda {}
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/COPYING	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,17 @@
+redmine_tags is a redMine plugin, that adds tagging support.
+
+Copyright (c) 2010 Eric Davis
+Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+
+redmine_tags is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+redmine_tags is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/Gemfile	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1 @@
+gem "acts-as-taggable-on", "2.3.3"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/HISTORY.md	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,4 @@
+## 2.0.1 (unreleased)
+
+- Added Simplified Chinese translation. Thanks to archonwang.
+- Added Bulgarian translation. Thanks to Ivan Cenov.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/LICENSE	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,619 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/README.md	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,41 @@
+Redmine Tags
+============
+
+Allows marking up different models in Redmine with tags.
+Inspired by original redmine\_tags of Eric Davis.
+
+
+Supported models
+----------------
+
+- Issues
+
+
+Requirements
+------------
+
+- Redmine `>= 2.1.0`
+- acts-as-taggable-on `= 2.3.3`
+
+
+Installation
+------------
+
+- Clone this repository into `redmine/plugins/redmine_tags`
+- Install dependencies and migrate database:
+
+        cd redmine/
+        bundle install
+        RAILS_ENV=production rails generate acts_as_taggable_on:migration
+        RAILS_ENV=production rake db:migrate
+        RAILS_ENV=production rake redmine:plugins:migrate
+
+- Restart your Redmine web server (e.g. mongrel, thin, mod\_rails)
+
+
+License
+-------
+
+This plugin is licensed under the terms of GNU/GPL v3+.
+See COPYING and LICENSE for details.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/README.rdoc	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,15 @@
+= Redmine Tags
+
+Allows marking up different models in Redmine with tags.
+Inspired by original redmine_tags of Eric Davis. But in
+comparison extended with some code that was already wrote
+as part of my own redmine taggable_issues branch.
+
+== Supported models
+
+* Issues
+
+== License
+
+This plugin is licensed under the GNU/GPL v3.
+See COPYING and LICENSE for details.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/helpers/filters_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,62 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+module FiltersHelper
+  # returns link to the page with issues filtered by specified filters
+  # === parameters
+  # * <i>title</i> = link title text
+  # * <i>filters</i> = filters to be applied (see <tt>link_to_filter_options</tt> for details)
+  # * <i>options</i> = (optional) base options of the link
+  # === example
+  # link_to_filter 'foobar', [[ :tags, '~', 'foobar' ]]
+  # link_to_filter 'foobar', [[ :tags, '~', 'foobar' ]], :project_id => project
+  def link_to_filter(title, filters, options = {})
+    options.merge! link_to_filter_options(filters)
+    link_to title, options
+  end
+
+
+  # returns hash suitable for passing it to the <tt>to_link</tt>
+  # === parameters
+  # * <i>filters</i> = array of arrays. each child array is an array of strings:
+  #                    name, operator and value
+  # === example
+  # link_to 'foobar', link_to_filter_options [[ :tags, '~', 'foobar' ]]
+  #
+  # filters = [[ :tags, '~', 'bazbaz' ], [:status_id, 'o']]
+  # link_to 'bazbaz', link_to_filter_options filters
+  def link_to_filter_options(filters)
+    options = {
+      :controller => 'issues',
+      :action => 'index',
+      :set_filter => 1,
+      :fields => [],
+      :values => {},
+      :operators => {}
+    }
+
+    filters.each do |f|
+      name, operator, value = f
+      options[:fields].push(name)
+      options[:operators][name] = operator
+      options[:values][name] = [value]
+    end
+
+    options
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/helpers/issues_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,42 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+module IssuesHelper
+  include TagsHelper
+
+  def sidebar_tags
+    unless @sidebar_tags
+      @sidebar_tags = []
+      if :none != RedmineTags.settings[:issues_sidebar].to_sym
+        @sidebar_tags = Issue.available_tags({
+          :project => @project,
+          :open_only => (RedmineTags.settings[:issues_open_only].to_i == 1)
+        })
+      end
+    end
+    @sidebar_tags
+  end
+
+  def render_sidebar_tags
+    render_tags_list(sidebar_tags, {
+      :show_count => (RedmineTags.settings[:issues_show_count].to_i == 1),
+      :open_only => (RedmineTags.settings[:issues_open_only].to_i == 1),
+      :style => RedmineTags.settings[:issues_sidebar].to_sym
+    })
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/helpers/tags_helper.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,113 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+module TagsHelper
+  include ActsAsTaggableOn::TagsHelper
+  include FiltersHelper
+
+
+  # Returns tag link
+  # === Parameters
+  # * <i>tag</i> = Instance of Tag
+  # * <i>options</i> = (optional) Options (override system settings)
+  #   * show_count  - Boolean. Whenever show tag counts
+  #   * open_only   - Boolean. Whenever link to the filter with "open" issues
+  #                   only limit.
+  def render_tag_link(tag, options = {})
+    filters = [[:tags, '=', tag.name]]
+    filters << [:status_id, 'o'] if options[:open_only]
+
+    content = link_to_filter tag.name, filters, :project_id => @project
+    if options[:show_count]
+      content << content_tag('span', "(#{tag.count})", :class => 'tag-count')
+    end
+
+    content_tag('span', content, :class => 'tag-label')
+  end
+
+  def render_project_tag_link(tag, options = {})
+    content = link_to tag.name, :controller => :projects, :action => :index, :tag_search => tag.name
+
+    if options[:show_count]
+      content << content_tag('span', "(#{tag.count})", :class => 'tag-count')
+    end
+    content_tag('span', content, :class => 'tag-label')
+  end
+
+
+  # Renders list of tags
+  # Clouds are rendered as block <tt>div</tt> with internal <tt>span</t> per tag.
+  # Lists are rendered as unordered lists <tt>ul</tt>. Lists are ordered by
+  # <tt>tag.count</tt> descending.
+  # === Parameters
+  # * <i>tags</i> = Array of Tag instances
+  # * <i>options</i> = (optional) Options (override system settings)
+  #   * show_count  - Boolean. Whenever show tag counts
+  #   * open_only   - Boolean. Whenever link to the filter with "open" issues
+  #                   only limit.
+  #   * style       - list, cloud
+  def render_tags_list(tags, options = {})
+    unless tags.nil? or tags.empty?
+      content, style = '', options.delete(:style)
+
+      # prevent ActsAsTaggableOn::TagsHelper from calling `all`
+      # otherwise we will need sort tags after `tag_cloud`
+      tags = tags.all if tags.respond_to?(:all)
+
+      case sorting = "#{RedmineTags.settings[:issues_sort_by]}:#{RedmineTags.settings[:issues_sort_order]}"
+        when "name:asc";    tags.sort! { |a,b| a.name <=> b.name }
+        when "name:desc";   tags.sort! { |a,b| b.name <=> a.name }
+        when "count:asc";   tags.sort! { |a,b| a.count <=> b.count }
+        when "count:desc";  tags.sort! { |a,b| b.count <=> a.count }
+        # Unknown sorting option. Fallback to default one
+        else
+          logger.warn "[redmine_tags] Unknown sorting option: <#{sorting}>"
+          tags.sort! { |a,b| a.name <=> b.name }
+      end
+
+      if :list == style
+        list_el, item_el = 'ul', 'li'
+      elsif :cloud == style
+        list_el, item_el = 'div', 'span'
+        tags = cloudify(tags)
+      else
+        raise "Unknown list style"
+      end
+
+      content = content.html_safe
+      tag_cloud tags, (1..8).to_a do |tag, weight|
+        content << " ".html_safe + content_tag(item_el, render_project_tag_link(tag, options), :class => "tag-nube-#{weight}") + " ".html_safe
+      end
+
+      content_tag(list_el, content, :class => 'tags')
+    end
+  end
+
+  private
+
+  # make snowball. first tags comes in th middle.
+  def cloudify(tags)
+    temp, tags, trigger = tags, [], true
+    temp.each do |tag|
+      tags.send((trigger ? 'push' : 'unshift'), tag)
+      trigger = !trigger
+    end
+    tags
+  end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/auto_completes/_search_tag_list.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,5 @@
+<ul>
+	<% @tags.each do |tag| -%>
+  		<%= content_tag 'li', h('%s (%d)' % [tag.name, tag.count]), :name => tag.name %>
+	<% end -%>
+</ul>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/auto_completes/_tag_list.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,4 @@
+<%= raw @tags.collect {|tag| 
+       tag.name
+    }.to_json
+%>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/auto_completes/_tag_list.json.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,4 @@
+<%= raw @tags.collect {|tag| 
+       tag.name
+    }.to_json
+%>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/issues/_bulk_tags_form.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1 @@
+<%= render :partial => "issues/tags_form", :locals => {:issue => Issue.new({:project => issues.first.project})} %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/issues/_tags.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,6 @@
+<% unless issue.tag_list.empty? %>
+  <tr>
+    <td><b><%=l(:tags)%>:</b></td>
+    <td><%= safe_join(issue.tag_counts.collect{ |t| render_tag_link(t, :show_count => false, :open_only => false) }, ', ') %></td>
+  </tr>
+<% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/issues/_tags_form.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,29 @@
+<div>
+  <p id="issue_tags">
+  <% issue = Issue.new({:project => issues.first.project}) if defined? issues %>
+  <% text_field_options = {:label => :tags, :size => 60, :class => 'hol'} %>
+  <% if defined? form %>
+    <%= form.text_field :tag_list, text_field_options %>
+  <% else %>
+    <%= label_tag :tags, nil, :for => :issue_tag_list  %>
+    <%= text_field_tag 'issue[tag_list]', "", :class => text_field_options[:class], :size => text_field_options[:size], :id => :issue_tag_list %>
+  <% end %>
+  </p>
+  <div id="issue_tag_candidates" class="autocomplete"></div>
+  <%= stylesheet_link_tag 'jquery.tagit.css', :plugin => 'redmine_tags' %>
+  <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %>
+  <%= javascript_include_tag 'tag-it', :plugin => 'redmine_tags' %>
+  <%= javascript_tag "$('#issue_tag_list').tagit({
+		tagSource: function(search, showChoices) {
+		  var that = this;
+		  $.ajax({
+			url: '#{url_for(:controller => 'auto_completes', :action => 'issue_tags', :project_id => issue.project.id)}',
+			data: {q: search.term},
+			success: function(choices) {
+			  showChoices(that._subtractArray(jQuery.parseJSON(choices), that.assignedTags()));
+			}
+		  });
+		},
+	});
+" %>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/issues/_tags_sidebar.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,6 @@
+<% unless sidebar_tags.empty? -%>
+  <%= stylesheet_link_tag 'jquery.tagit.css', :plugin => 'redmine_tags' %>
+  <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %>
+  <h3><%= l(:tags) %></h3>
+  <%= render_sidebar_tags %>
+<% end -%>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/projects/_filter_search_tags.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,34 @@
+<div>
+  <p id="project_tags">
+    <%= label_tag 'tag_search', l(:tags_search).html_safe -%>
+    <%= text_field_tag 'tag_search', params[:tag_search] -%>
+    <br />
+  </p>
+
+  <div id="project_tag_candidates" class="autocomplete" style="margin-top: 0;"></div>
+
+  <%= stylesheet_link_tag 'jquery.tagit.css', :plugin => 'redmine_tags' %>
+  <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %>
+  <%= javascript_include_tag 'tag-it', :plugin => 'redmine_tags' %>
+
+  <%= javascript_tag "$('#tag_search').tagit({
+            tagSource: function(search, showChoices) {
+            var that = this;
+            $.ajax({
+              url: '#{url_for(:controller => 'auto_completes', :action => 'project_tags')}',
+              data: {q: search.term},
+              success: function(choices) {
+              showChoices(that._subtractArray(jQuery.parseJSON(choices), that.assignedTags()));
+            }
+          });
+        },
+    });
+" -%>
+
+</div>
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/projects/_filter_tags.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,13 @@
+<p class='tag'>
+  <%= labelled_fields_for @project do |f| -%>
+    <div>
+      <p id="project_tags">
+        <%= f.text_field :tag_list, :label => :tags, :size => 60, :class => 'hol' %>
+      </p>
+      <div id="project_tag_candidates" class="autocomplete"></div>
+      <%= javascript_include_tag 'tags_input', :plugin => 'redmine_tags' %>
+
+      <%= javascript_tag "observeProjectTagsField('#{url_for(:controller => 'auto_completes', :action => 'project_tags')}')" %>
+    </div>
+  <% end -%>
+</p>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/projects/_filtered_projects.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,7 @@
+<% if @projects.empty? %>
+<p><b><%= l(:project_filter_no_results) %></b></p>
+<% else %>
+<%= render_project_table_with_filtering(@projects, @question) %>
+<% end %>
+
+<p class="pagination"><%= pagination_links_full @project_pages, @project_count %></p>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/projects/_my.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,1 @@
+<%= render_my_project_hierarchy_with_tags(@user_projects) %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/projects/_my_projects.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,17 @@
+<% if !@user_projects.empty? %>
+<% if @myproj_status=="true" %>
+<fieldset id="my_projects_fieldset" class="collapsible">
+    <legend onclick="toggleFieldsetWithState(this);"><h2><%= l(:label_my_project_plural) %></h2></legend>
+<% else %>
+<fieldset id="my_projects_fieldset" class="collapsible collapsed">
+    <legend onclick="toggleFieldsetWithState(this);"><h2><%= l(:label_my_project_plural) %></h2></legend>
+    <div style="display: none;">    
+<% end %>
+    <div>
+      <%= render_my_project_hierarchy_with_tags(@user_projects)%>
+    </div>
+  <% unless @myproj_status=="true" %>
+    </div>
+  <%- end -%>
+</fieldset>
+<% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/projects/_tagcloud.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,6 @@
+
+<div id="tags">
+<%= render_tags_list(Project.available_tags.select { |t| Project.tagged_with(t).count > 1 }, :style => :cloud) %>
+</div>
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/projects/_tags.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,7 @@
+<% unless @project.tag_list.empty? %>
+  <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %>
+  <dl class="tags">
+    <dt class="tags-title"><%=l(:tags)%></dt>
+    <dd class="tags"><%= raw @project.tag_counts.collect{ |t| render_project_tag_link(t) }.join(', ') %></dd>
+  </dl>
+<% end %>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/projects/_tags_form.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,26 @@
+<%= labelled_fields_for :project, project do |f| -%>
+<div>
+  <p id="project_tags"><%= f.text_field :tag_list, :label => :tags, :size => 60, :class => 'hol' %>
+    <em class="info"><%= l(:text_tags_search).html_safe %></em>
+  </p>
+  <div id="project_tag_candidates" class="autocomplete" style="margin-top: 0;"></div>
+  <%= stylesheet_link_tag 'jquery.tagit.css', :plugin => 'redmine_tags' %>
+  <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %>
+  <%= javascript_include_tag 'tag-it', :plugin => 'redmine_tags' %>
+
+  <%= javascript_tag "$('#project_tag_list').tagit({
+            tagSource: function(search, showChoices) {
+            var that = this;
+            $.ajax({
+              url: '#{url_for(:controller => 'auto_completes', :action => 'project_tags')}',
+              data: {q: search.term},
+              success: function(choices) {
+              showChoices(that._subtractArray(jQuery.parseJSON(choices), that.assignedTags()));
+            }
+          });
+        },
+    });
+" %>
+
+</div>
+<% end -%>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/projects/index.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,44 @@
+<% content_for :header_tags do %>
+    <%= auto_discovery_link_tag(:atom, {:action => 'index', :format => 'atom', :key => User.current.rss_key}) %>
+    <%= stylesheet_link_tag 'redmine_tags', :plugin => 'redmine_tags' %>
+<% end %>
+
+<%= javascript_include_tag 'projects_index', :plugin => 'redmine_tags' %>
+
+<div class="contextual">
+    <%= link_to l(:label_overall_activity), { :controller => 'activities', :action => 'index' }%>
+    <%= ('| ' + link_to(l(:label_project_new), {:controller => 'projects', :action => 'new'}, :class => 'icon icon-add')).html_safe if User.current.allowed_to?(:add_project, nil, :global => true) %>
+</div>
+
+<div style="clear:both;"></div>
+<h2>
+  <%= l("label_project_all") %>
+</h2>
+
+<div style="clear:both;"></div>
+
+<%= form_tag :projects, :method => :get do -%>
+  <div>
+    <p class='q'>
+      <%= label_tag 'search', l('project_filtering_q_label') %>
+      <%= text_field_tag 'search', params[:search] -%>
+    </p>
+
+    <div id='filter_tags'>
+      <%= render :partial => 'filter_search_tags' -%>
+    </div>
+
+    <%= submit_tag :search, :name => "Search" %>
+
+  </div>
+<%- end -%>
+
+<div id="projects">
+  <%= render :partial => 'filtered_projects' %>
+</div>
+
+<% other_formats_links do |f| %>
+	<%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
+<% end %>
+
+<% html_title(l(:label_project_plural)) -%>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/app/views/tags/_settings.html.erb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,19 @@
+<fieldset><legend><%= l(:setting_issue_tags) %></legend>
+  <p>
+    <label><%= l(:issues_sidebar) %></label>
+    <%= select_tag 'settings[issues_sidebar]', options_for_select(%w(none list cloud).collect{|v| [l("issue_tags_sidebar_#{v}"), v]}, @settings[:issues_sidebar]) %>
+  </p>
+  <p>
+    <label><%= l(:issues_show_count) %></label>
+    <%= check_box_tag 'settings[issues_show_count]', 1, 1 == @settings[:issues_show_count].to_i %>
+  </p>
+  <p>
+    <label><%= l(:issues_open_only) %></label>
+    <%= check_box_tag 'settings[issues_open_only]', 1, 1 == @settings[:issues_open_only].to_i %>
+  </p>
+  <p>
+    <label><%= l(:issues_sort_by) %></label>
+    <%= select_tag 'settings[issues_sort_by]', options_for_select(%w(name count).collect{|v| [l("issues_sort_by_#{v}"), v]}, @settings[:issues_sort_by]) %>
+    <%= select_tag 'settings[issues_sort_order]', options_for_select(%w(asc desc).collect{|v| [l("issues_sort_order_#{v}"), v]}, @settings[:issues_sort_order]) %>
+  </p>
+</fieldset>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/assets/javascripts/tag-it.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,538 @@
+/*
+* jQuery UI Tag-it!
+*
+* @version v2.0 (06/2011)
+*
+* Copyright 2011, Levy Carneiro Jr.
+* Released under the MIT license.
+* http://aehlke.github.com/tag-it/LICENSE
+*
+* Homepage:
+*   http://aehlke.github.com/tag-it/
+*
+* Authors:
+*   Levy Carneiro Jr.
+*   Martin Rehfeld
+*   Tobias Schmidt
+*   Skylar Challand
+*   Alex Ehlke
+*
+* Maintainer:
+*   Alex Ehlke - Twitter: @aehlke
+*
+* Dependencies:
+*   jQuery v1.4+
+*   jQuery UI v1.8+
+*/
+(function($) {
+
+    $.widget('ui.tagit', {
+        options: {
+            allowDuplicates   : false,
+            caseSensitive     : true,
+            fieldName         : 'tags',
+            placeholderText   : null,   // Sets `placeholder` attr on input field.
+            readOnly          : false,  // Disables editing.
+            removeConfirmation: false,  // Require confirmation to remove tags.
+            tagLimit          : null,   // Max number of tags allowed (null for unlimited).
+
+            // Used for autocomplete, unless you override `autocomplete.source`.
+            availableTags     : [],
+
+            // Use to override or add any options to the autocomplete widget.
+            //
+            // By default, autocomplete.source will map to availableTags,
+            // unless overridden.
+            autocomplete: {},
+
+            // Shows autocomplete before the user even types anything.
+            showAutocompleteOnFocus: false,
+
+            // When enabled, quotes are unneccesary for inputting multi-word tags.
+            allowSpaces: false,
+
+            // The below options are for using a single field instead of several
+            // for our form values.
+            //
+            // When enabled, will use a single hidden field for the form,
+            // rather than one per tag. It will delimit tags in the field
+            // with singleFieldDelimiter.
+            //
+            // The easiest way to use singleField is to just instantiate tag-it
+            // on an INPUT element, in which case singleField is automatically
+            // set to true, and singleFieldNode is set to that element. This
+            // way, you don't need to fiddle with these options.
+            singleField: false,
+
+            // This is just used when preloading data from the field, and for
+            // populating the field with delimited tags as the user adds them.
+            singleFieldDelimiter: ',',
+
+            // Set this to an input DOM node to use an existing form field.
+            // Any text in it will be erased on init. But it will be
+            // populated with the text of tags as they are created,
+            // delimited by singleFieldDelimiter.
+            //
+            // If this is not set, we create an input node for it,
+            // with the name given in settings.fieldName.
+            singleFieldNode: null,
+
+            // Whether to animate tag removals or not.
+            animate: true,
+
+            // Optionally set a tabindex attribute on the input that gets
+            // created for tag-it.
+            tabIndex: null,
+
+            // Event callbacks.
+            beforeTagAdded      : null,
+            afterTagAdded       : null,
+
+            beforeTagRemoved    : null,
+            afterTagRemoved     : null,
+
+            onTagClicked        : null,
+            onTagLimitExceeded  : null,
+
+
+            // DEPRECATED:
+            //
+            // /!\ These event callbacks are deprecated and WILL BE REMOVED at some
+            // point in the future. They're here for backwards-compatibility.
+            // Use the above before/after event callbacks instead.
+            onTagAdded  : null,
+            onTagRemoved: null,
+            // `autocomplete.source` is the replacement for tagSource.
+            tagSource: null
+            // Do not use the above deprecated options.
+        },
+
+        _create: function() {
+            // for handling static scoping inside callbacks
+            var that = this;
+
+            // There are 2 kinds of DOM nodes this widget can be instantiated on:
+            //     1. UL, OL, or some element containing either of these.
+            //     2. INPUT, in which case 'singleField' is overridden to true,
+            //        a UL is created and the INPUT is hidden.
+            if (this.element.is('input')) {
+                this.tagList = $('<ul></ul>').insertAfter(this.element);
+                this.options.singleField = true;
+                this.options.singleFieldNode = this.element;
+                this.element.css('display', 'none');
+            } else {
+                this.tagList = this.element.find('ul, ol').andSelf().last();
+            }
+
+            this.tagInput = $('<input type="text" />').addClass('ui-widget-content');
+
+            if (this.options.readOnly) this.tagInput.attr('disabled', 'disabled');
+
+            if (this.options.tabIndex) {
+                this.tagInput.attr('tabindex', this.options.tabIndex);
+            }
+
+            if (this.options.placeholderText) {
+                this.tagInput.attr('placeholder', this.options.placeholderText);
+            }
+
+            if (!this.options.autocomplete.source) {
+                this.options.autocomplete.source = function(search, showChoices) {
+                    var filter = search.term.toLowerCase();
+                    var choices = $.grep(this.options.availableTags, function(element) {
+                        // Only match autocomplete options that begin with the search term.
+                        // (Case insensitive.)
+                        return (element.toLowerCase().indexOf(filter) === 0);
+                    });
+                    showChoices(this._subtractArray(choices, this.assignedTags()));
+                };
+            }
+
+            if (this.options.showAutocompleteOnFocus) {
+                this.tagInput.focus(function(event, ui) {
+                    that._showAutocomplete();
+                });
+
+                if (typeof this.options.autocomplete.minLength === 'undefined') {
+                    this.options.autocomplete.minLength = 0;
+                }
+            }
+
+            // Bind autocomplete.source callback functions to this context.
+            if ($.isFunction(this.options.autocomplete.source)) {
+                this.options.autocomplete.source = $.proxy(this.options.autocomplete.source, this);
+            }
+
+            // DEPRECATED.
+            if ($.isFunction(this.options.tagSource)) {
+                this.options.tagSource = $.proxy(this.options.tagSource, this);
+            }
+
+            this.tagList
+                .addClass('tagit')
+                .addClass('ui-widget ui-widget-content ui-corner-all')
+                // Create the input field.
+                .append($('<li class="tagit-new"></li>').append(this.tagInput))
+                .click(function(e) {
+                    var target = $(e.target);
+                    if (target.hasClass('tagit-label')) {
+                        var tag = target.closest('.tagit-choice');
+                        if (!tag.hasClass('removed')) {
+                            that._trigger('onTagClicked', e, {tag: tag, tagLabel: that.tagLabel(tag)});
+                        }
+                    } else {
+                        // Sets the focus() to the input field, if the user
+                        // clicks anywhere inside the UL. This is needed
+                        // because the input field needs to be of a small size.
+                        that.tagInput.focus();
+                    }
+                });
+
+            // Single field support.
+            var addedExistingFromSingleFieldNode = false;
+            if (this.options.singleField) {
+                if (this.options.singleFieldNode) {
+                    // Add existing tags from the input field.
+                    var node = $(this.options.singleFieldNode);
+                    var tags = node.val().split(this.options.singleFieldDelimiter);
+                    node.val('');
+                    $.each(tags, function(index, tag) {
+                        that.createTag(tag, null, true);
+                        addedExistingFromSingleFieldNode = true;
+                    });
+                } else {
+                    // Create our single field input after our list.
+                    this.options.singleFieldNode = $('<input type="hidden" style="display:none;" value="" name="' + this.options.fieldName + '" />');
+                    this.tagList.after(this.options.singleFieldNode);
+                }
+            }
+
+            // Add existing tags from the list, if any.
+            if (!addedExistingFromSingleFieldNode) {
+                this.tagList.children('li').each(function() {
+                    if (!$(this).hasClass('tagit-new')) {
+                        that.createTag($(this).text(), $(this).attr('class'), true);
+                        $(this).remove();
+                    }
+                });
+            }
+
+            // Events.
+            this.tagInput
+                .keydown(function(event) {
+                    // Backspace is not detected within a keypress, so it must use keydown.
+                    if (event.which == $.ui.keyCode.BACKSPACE && that.tagInput.val() === '') {
+                        var tag = that._lastTag();
+                        if (!that.options.removeConfirmation || tag.hasClass('remove')) {
+                            // When backspace is pressed, the last tag is deleted.
+                            that.removeTag(tag);
+                        } else if (that.options.removeConfirmation) {
+                            tag.addClass('remove ui-state-highlight');
+                        }
+                    } else if (that.options.removeConfirmation) {
+                        that._lastTag().removeClass('remove ui-state-highlight');
+                    }
+
+                    // Comma/Space/Enter are all valid delimiters for new tags,
+                    // except when there is an open quote or if setting allowSpaces = true.
+                    // Tab will also create a tag, unless the tag input is empty,
+                    // in which case it isn't caught.
+                    if (
+                        event.which === $.ui.keyCode.COMMA ||
+                        event.which === $.ui.keyCode.ENTER ||
+                        (
+                            event.which == $.ui.keyCode.TAB &&
+                            that.tagInput.val() !== ''
+                        ) ||
+                        (
+                            event.which == $.ui.keyCode.SPACE &&
+                            that.options.allowSpaces !== true &&
+                            (
+                                $.trim(that.tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"' ||
+                                (
+                                    $.trim(that.tagInput.val()).charAt(0) == '"' &&
+                                    $.trim(that.tagInput.val()).charAt($.trim(that.tagInput.val()).length - 1) == '"' &&
+                                    $.trim(that.tagInput.val()).length - 1 !== 0
+                                )
+                            )
+                        )
+                    ) {
+                        // Enter submits the form if there's no text in the input.
+                        if (!(event.which === $.ui.keyCode.ENTER && that.tagInput.val() === '')) {
+                            event.preventDefault();
+                        }
+
+                        that.createTag(that._cleanedInput());
+
+                        // The autocomplete doesn't close automatically when TAB is pressed.
+                        // So let's ensure that it closes.
+                        that.tagInput.autocomplete('close');
+                    }
+                }).blur(function(e){
+                    // Create a tag when the element loses focus.
+                    // If autocomplete is enabled and suggestion was clicked, don't add it.
+                    if (!that.tagInput.data('autocomplete-open')) {
+                        that.createTag(that._cleanedInput());
+                    }
+                });
+
+            // Autocomplete.
+            if (this.options.availableTags || this.options.tagSource || this.options.autocomplete.source) {
+                var autocompleteOptions = {
+                    select: function(event, ui) {
+                        that.createTag(ui.item.value);
+                        // Preventing the tag input to be updated with the chosen value.
+                        return false;
+                    }
+                };
+                $.extend(autocompleteOptions, this.options.autocomplete);
+
+                // tagSource is deprecated, but takes precedence here since autocomplete.source is set by default,
+                // while tagSource is left null by default.
+                autocompleteOptions.source = this.options.tagSource || autocompleteOptions.source;
+
+                this.tagInput.autocomplete(autocompleteOptions).bind('autocompleteopen', function(event, ui) {
+                    that.tagInput.data('autocomplete-open', true);
+                }).bind('autocompleteclose', function(event, ui) {
+                    that.tagInput.data('autocomplete-open', false)
+                });
+            }
+        },
+
+        _cleanedInput: function() {
+            // Returns the contents of the tag input, cleaned and ready to be passed to createTag
+            return $.trim(this.tagInput.val().replace(/^"(.*)"$/, '$1'));
+        },
+
+        _lastTag: function() {
+            return this.tagList.find('.tagit-choice:last:not(.removed)');
+        },
+
+        _tags: function() {
+            return this.tagList.find('.tagit-choice:not(.removed)');
+        },
+
+        assignedTags: function() {
+            // Returns an array of tag string values
+            var that = this;
+            var tags = [];
+            if (this.options.singleField) {
+                tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter);
+                if (tags[0] === '') {
+                    tags = [];
+                }
+            } else {
+                this._tags().each(function() {
+                    tags.push(that.tagLabel(this));
+                });
+            }
+            return tags;
+        },
+
+        _updateSingleTagsField: function(tags) {
+            // Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter
+            $(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter)).trigger('change');
+        },
+
+        _subtractArray: function(a1, a2) {
+            var result = [];
+            for (var i = 0; i < a1.length; i++) {
+                if ($.inArray(a1[i], a2) == -1) {
+                    result.push(a1[i]);
+                }
+            }
+            return result;
+        },
+
+        tagLabel: function(tag) {
+            // Returns the tag's string label.
+            if (this.options.singleField) {
+                return $(tag).find('.tagit-label:first').text();
+            } else {
+                return $(tag).find('input:first').val();
+            }
+        },
+
+        _showAutocomplete: function() {
+            this.tagInput.autocomplete('search', '');
+        },
+
+        _findTagByLabel: function(name) {
+            var that = this;
+            var tag = null;
+            this._tags().each(function(i) {
+                if (that._formatStr(name) == that._formatStr(that.tagLabel(this))) {
+                    tag = $(this);
+                    return false;
+                }
+            });
+            return tag;
+        },
+
+        _isNew: function(name) {
+            return !this._findTagByLabel(name);
+        },
+
+        _formatStr: function(str) {
+            if (this.options.caseSensitive) {
+                return str;
+            }
+            return $.trim(str.toLowerCase());
+        },
+
+        _effectExists: function(name) {
+            return Boolean($.effects && ($.effects[name] || ($.effects.effect && $.effects.effect[name])));
+        },
+
+        createTag: function(value, additionalClass, duringInitialization) {
+            var that = this;
+
+            value = $.trim(value);
+
+            if(this.options.preprocessTag) {
+                value = this.options.preprocessTag(value);
+            }
+
+            if (value === '') {
+                return false;
+            }
+
+            if (!this.options.allowDuplicates && !this._isNew(value)) {
+                var existingTag = this._findTagByLabel(value);
+                if (this._trigger('onTagExists', null, {
+                    existingTag: existingTag,
+                    duringInitialization: duringInitialization
+                }) !== false) {
+                    if (this._effectExists('highlight')) {
+                        existingTag.effect('highlight');
+                    }
+                }
+                return false;
+            }
+
+            if (this.options.tagLimit && this._tags().length >= this.options.tagLimit) {
+                this._trigger('onTagLimitExceeded', null, {duringInitialization: duringInitialization});
+                return false;
+            }
+
+            var label = $(this.options.onTagClicked ? '<a class="tagit-label"></a>' : '<span class="tagit-label"></span>').text(value);
+
+            // Create tag.
+            var tag = $('<li></li>')
+                .addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
+                .addClass(additionalClass)
+                .append(label);
+
+            if (this.options.readOnly){
+                tag.addClass('tagit-choice-read-only');
+            } else {
+                tag.addClass('tagit-choice-editable');
+                // Button for removing the tag.
+                var removeTagIcon = $('<span></span>')
+                    .addClass('ui-icon ui-icon-close');
+                var removeTag = $('<a><span class="text-icon">\xd7</span></a>') // \xd7 is an X
+                    .addClass('tagit-close')
+                    .append(removeTagIcon)
+                    .click(function(e) {
+                        // Removes a tag when the little 'x' is clicked.
+                        that.removeTag(tag);
+                    });
+                tag.append(removeTag);
+            }
+
+            // Unless options.singleField is set, each tag has a hidden input field inline.
+            if (!this.options.singleField) {
+                var escapedValue = label.html();
+                tag.append('<input type="hidden" style="display:none;" value="' + escapedValue + '" name="' + this.options.fieldName + '" />');
+            }
+
+            if (this._trigger('beforeTagAdded', null, {
+                tag: tag,
+                tagLabel: this.tagLabel(tag),
+                duringInitialization: duringInitialization
+            }) === false) {
+                return;
+            }
+
+            if (this.options.singleField) {
+                var tags = this.assignedTags();
+                tags.push(value);
+                this._updateSingleTagsField(tags);
+            }
+
+            // DEPRECATED.
+            this._trigger('onTagAdded', null, tag);
+
+            this.tagInput.val('');
+
+            // Insert tag.
+            this.tagInput.parent().before(tag);
+
+            this._trigger('afterTagAdded', null, {
+                tag: tag,
+                tagLabel: this.tagLabel(tag),
+                duringInitialization: duringInitialization
+            });
+
+            if (this.options.showAutocompleteOnFocus && !duringInitialization) {
+                setTimeout(function () { that._showAutocomplete(); }, 0);
+            }
+        },
+
+        removeTag: function(tag, animate) {
+            animate = typeof animate === 'undefined' ? this.options.animate : animate;
+
+            tag = $(tag);
+
+            // DEPRECATED.
+            this._trigger('onTagRemoved', null, tag);
+
+            if (this._trigger('beforeTagRemoved', null, {tag: tag, tagLabel: this.tagLabel(tag)}) === false) {
+                return;
+            }
+
+            if (this.options.singleField) {
+                var tags = this.assignedTags();
+                var removedTagLabel = this.tagLabel(tag);
+                tags = $.grep(tags, function(el){
+                    return el != removedTagLabel;
+                });
+                this._updateSingleTagsField(tags);
+            }
+
+            if (animate) {
+                tag.addClass('removed'); // Excludes this tag from _tags.
+                var hide_args = this._effectExists('blind') ? ['blind', {direction: 'horizontal'}, 'fast'] : ['fast'];
+
+                var thisTag = this;
+                hide_args.push(function() {
+                    tag.remove();
+                    thisTag._trigger('afterTagRemoved', null, {tag: tag, tagLabel: thisTag.tagLabel(tag)});
+                });
+
+                tag.fadeOut('fast').hide.apply(tag, hide_args).dequeue();
+            } else {
+                tag.remove();
+                this._trigger('afterTagRemoved', null, {tag: tag, tagLabel: this.tagLabel(tag)});
+            }
+
+        },
+
+        removeTagByLabel: function(tagLabel, animate) {
+            var toRemove = this._findTagByLabel(tagLabel);
+            if (!toRemove) {
+                throw "No such tag exists with the name '" + tagLabel + "'";
+            }
+            this.removeTag(toRemove, animate);
+        },
+
+        removeAll: function() {
+            // Removes all tags.
+            var that = this;
+            this._tags().each(function(index, tag) {
+                that.removeTag(tag, false);
+            });
+        }
+
+    });
+})(jQuery);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/assets/stylesheets/jquery.tagit.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,54 @@
+ul.tagit {
+    padding: 1px 5px;
+    overflow: auto;
+    margin-left: inherit; /* usually we don't want the regular ul margins. */
+    margin-right: inherit;
+}
+ul.tagit li {
+    display: block;
+    float: left;
+    margin: 2px 5px 2px 0;
+}
+ul.tagit li.tagit-choice {
+    padding: .2em 18px .2em .5em;
+    position: relative;
+    line-height: inherit;
+}
+ul.tagit li.tagit-new {
+    padding: .25em 4px .25em 0;
+}
+
+ul.tagit li.tagit-choice a.tagit-label {
+    cursor: pointer;
+    text-decoration: none;
+}
+ul.tagit li.tagit-choice .tagit-close {
+    cursor: pointer;
+    position: absolute;
+    right: .1em;
+    top: 50%;
+    margin-top: -8px;
+}
+
+/* used for some custom themes that don't need image icons */
+ul.tagit li.tagit-choice .tagit-close .text-icon {
+    display: none;
+}
+
+ul.tagit li.tagit-choice input {
+    display: block;
+    float: left;
+    margin: 2px 5px 2px 0;
+}
+ul.tagit input[type="text"] {
+    -moz-box-sizing:    border-box;
+    -webkit-box-sizing: border-box;
+    box-sizing:         border-box;
+
+    border: none;
+    margin: 0;
+    padding: 0;
+    width: inherit;
+    background-color: inherit;
+    outline: none;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/assets/stylesheets/redmine_tags.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,62 @@
+/**
+ * This file is a part of redmine_tags
+ * redMine plugin, that adds tagging support.
+ *
+ * Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+ *
+ * redmine_tags is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * redmine_tags is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+ul.tags { list-style: none; padding: 0px; }
+ul.tags li { margin: .25em 0px; }
+
+div.tags { text-align: center; }
+div.tags h3 { text-align: left; }
+div.tags .tag-label { margin: .25em; }
+div.tags .tag-nube-1 { font-size: .8em; }
+div.tags .tag-nube-2 { font-size: .9em; }
+div.tags .tag-nube-3 { font-size: 1em; }
+div.tags .tag-nube-4 { font-size: 1.1em; }
+div.tags .tag-nube-5 { font-size: 1.2em; }
+div.tags .tag-nube-6 { font-size: 1.3em; }
+div.tags .tag-nube-7 { font-size: 1.4em; }
+div.tags .tag-nube-8 { font-size: 1.5em; }
+
+.tag-count { font-size: .75em; margin-left: .5em; }
+
+.tagit.ui-widget {
+	font-size: 1em;
+	margin: 0px;
+}
+
+ul.tagit li.tagit-choice {
+	color: #505050;
+	font-weight: normal;
+}
+
+ul.tagit li.tagit-choice:hover, ul.tagit li.tagit-choice.remove {
+	border-color: #6D95E0;
+}
+
+ul.tagit input[type="text"] {
+	background: transparent;
+}
+
+ul.projects .tags, ul.projects .no-tags { padding-left: 0.5em; color: #3e442c; font-size: 0.95em }
+table.projects th.tags { color: #3e442c; }
+
+dl.tags { margin-top: 1.5em; margin-left: 0; padding-bottom: 1em; overflow: hidden; margin-bottom: 1em; border-bottom: 1px dotted #bbbbbb; }
+dt.tags-title { float: left; font-weight: bold; color: #3e442c; }
+dd.tags { margin-left: .5em; float: left; color: #3e442c; }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/config/locales/bg.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,41 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Bulgarian translation for redmine_tags
+# by Ivan Cenov (jwalker_@Skype) i_cenov@botevgrad.com
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+bg:
+  tags: Маркери
+  field_tags: Маркери
+  field_tag_list: Маркери
+  setting_issue_tags: Маркери на задачите
+  issues_sidebar: Показване на страничния панел като
+  issues_show_count: Показване на броя на задачите
+  issues_open_only: Само отворените задачи
+  issues_sort_by: Подреждане на маркерите по
+
+  issue_tags_sidebar_none: Да не се показват
+  issue_tags_sidebar_list: Списък
+  issue_tags_sidebar_cloud: Облак
+
+  issues_sort_by_name: Наименование
+  issues_sort_by_count: Количество задачи
+  issues_sort_order_asc: Нарастване
+  issues_sort_order_desc: Намаляване
+
+  auto_complete_new_tag: Добавяне на нов...
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/config/locales/de.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,43 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# German translation for redmine_tags
+# by: Terence Miller AKA cforce, <cforce(at)gmx.de>,
+#     Jörg Jans
+#
+# Copyright (c) 2010 Terence Miller AKA cforce
+# Copyright (c) 2010 Jörg Jans
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+de:
+  tags: Tags
+  field_tags: Tags
+  field_tag_list: Tags
+  setting_issue_tags: Ticket Tags
+  issues_sidebar: Zeige die Tags auf der Sidebar
+  issues_show_count: Zeige die Ticketanzahl an
+  issues_open_only: Zeige nur noch offene Tickets
+  issues_sort_by: Sortiere Tags nach
+
+  issue_tags_sidebar_none: Keine
+  issue_tags_sidebar_list: Liste
+  issue_tags_sidebar_cloud: Cloud
+
+  issues_sort_by_name: Name
+  issues_sort_by_count: Anzahl tickets
+  issues_sort_order_asc: Aufsteigend
+  issues_sort_order_desc: Absteigend
+
+  auto_complete_new_tag: Hinzufügen...
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/config/locales/en.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,50 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# English translation for redmine_tags
+# by Aleksey V Zapparov AKA ixti
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+en:
+  tags: Tags
+  field_tags: Tags
+  field_tag_list: Tags
+  setting_issue_tags: Issues Tags
+  issues_sidebar: Display tags on sidebar as
+  issues_show_count: Display amount of issues
+  issues_open_only: Display open issues only
+  issues_sort_by: Sort tags by
+
+  issue_tags_sidebar_none: None
+  issue_tags_sidebar_list: List
+  issue_tags_sidebar_cloud: Cloud
+
+  issues_sort_by_name: Name
+  issues_sort_by_count: Issues amount
+  issues_sort_order_asc: Ascending
+  issues_sort_order_desc: Descending
+
+  auto_complete_new_tag: Add new...
+
+  project_filtering_q_label: "Filter by name:"
+  project_filter_no_results: "No matching projects found"
+  button_filter: "Filter"
+
+  tags_search: "Filter by tag"
+
+  text_tags_search: "A tag can be any text you like, but they're most useful if you choose tags that are already being used for the same thing by other projects (where possible). <br />Some tag examples are: library, plugin, paper, c++, mir, alpha, stable, bsd, android, ...<br />Tags help others find your work: please don't forget to tag your projects!"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/config/locales/fr.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,41 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# French translation for redmine_tags
+# by Stephane HANNEQUIN, <stephane.hannequin(at)aster-ingenierie.com>
+#
+# Copyright (c) 2010 Stephane HANNEQUIN
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+fr:
+  tags: Tags
+  field_tags: Tags
+  field_tag_list: Tags
+  setting_issue_tags: Tags des demandes
+  issues_sidebar: Afficher les Tags comme
+  issues_show_count: Afficher le nombre de demande
+  issues_open_only: N'afficher que les demandes ouvertes
+  issues_sort_by: (en) Sort tags by
+  
+  issue_tags_sidebar_none: Ne pas afficher
+  issue_tags_sidebar_list: Liste
+  issue_tags_sidebar_cloud: Nuage
+
+  issues_sort_by_name: (en) Name
+  issues_sort_by_count: (en) Issues amount
+  issues_sort_order_asc: (en) Ascending
+  issues_sort_order_desc: (en) Descending
+
+  auto_complete_new_tag: Nouveau...
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/config/locales/ru.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,41 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Russian translation for redmine_tags
+# by Aleksey V Zapparov AKA ixti
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+ru:
+  tags: Метки
+  field_tags: Метки
+  field_tag_list: Метки
+  setting_issue_tags: Метки задач
+  issues_sidebar: Боковую панель как
+  issues_show_count: Показать кол-во задач
+  issues_open_only: Только открытые задачи
+  issues_sort_by: Упорядочить метки по
+  
+  issue_tags_sidebar_none: Не показывать
+  issue_tags_sidebar_list: Список
+  issue_tags_sidebar_cloud: Облако
+
+  issues_sort_by_name: Названию
+  issues_sort_by_count: Кол-ву задач
+  issues_sort_order_asc: по возрастанию
+  issues_sort_order_desc: по убыванию
+
+  auto_complete_new_tag: Добавить...
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/config/locales/zh.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,41 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Simplified Chinese translation for redmine_tags
+# by archonwang (https://github.com/archonwang)
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+zh:
+  tags: 标签
+  field_tags: 标签
+  field_tag_list: 标签
+  setting_issue_tags: 问题标签
+  issues_sidebar: 在侧边栏显示标签
+  issues_show_count: 显示问题计数
+  issues_open_only: 仅显示打开的问题
+  issues_sort_by: 标签按何种方式排序
+
+  issue_tags_sidebar_none: 无
+  issue_tags_sidebar_list: 列表显示
+  issue_tags_sidebar_cloud: 云显示
+
+  issues_sort_by_name: 名称
+  issues_sort_by_count: 问题计数
+  issues_sort_order_asc: 顺序
+  issues_sort_order_desc: 倒序
+
+  auto_complete_new_tag: 添加新标签 ... ...
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/config/routes.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,6 @@
+RedmineApp::Application.routes.draw do
+  match '/issue_tags/auto_complete/:project_id', :to => 'auto_completes#issue_tags', :via => :get, :as => 'auto_complete_issue_tags'
+  match '/auto_completes/project_search_tags', :to => 'auto_completes#project_search_tags'
+  match '/auto_completes/project_tags', :to => 'auto_completes#project_tags'
+  match 'projects/set_fieldset_status' => 'projects#set_fieldset_status', :constraints => {:method => :post}
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/db/migrate/001_acts_as_taggable_on_migration.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,29 @@
+class ActsAsTaggableOnMigration < ActiveRecord::Migration
+  def self.up
+    create_table :tags do |t|
+      t.column :name, :string
+    end
+    
+    create_table :taggings do |t|
+      t.column :tag_id, :integer
+      t.column :taggable_id, :integer
+      t.column :tagger_id, :integer
+      t.column :tagger_type, :string
+      
+      # You should make sure that the column created is
+      # long enough to store the required class names.
+      t.column :taggable_type, :string
+      t.column :context, :string
+      
+      t.column :created_at, :datetime
+    end
+    
+    add_index :taggings, :tag_id
+    add_index :taggings, [:taggable_id, :taggable_type, :context]
+  end
+  
+  def self.down
+    drop_table :taggings
+    drop_table :tags
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/init.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,83 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+require 'redmine'
+require 'redmine_tags'
+
+
+Redmine::Plugin.register :redmine_tags do
+  name        'redmine_tags'
+  author      'Aleksey V Zapparov AKA "ixti"'
+  description 'redMine tagging support'
+  version     '2.0.1-dev'
+  url         'https://github.com/ixti/redmine_tags/'
+  author_url  'http://www.ixti.net/'
+
+  requires_redmine :version_or_higher => '1.2.0'
+
+  settings :default => {
+    :issues_sidebar => 'none',
+    :issues_show_count => 0,
+    :issues_open_only => 0,
+    :issues_sort_by => 'name',
+    :issues_sort_order => 'asc'
+  }, :partial => 'tags/settings'
+end
+
+
+ActionDispatch::Callbacks.to_prepare do
+  require_dependency 'redmine_project_filtering'
+
+  unless Project.included_modules.include?(RedmineTags::Patches::ProjectPatch)
+    Project.send(:include, RedmineTags::Patches::ProjectPatch)
+  end
+
+  unless ProjectsHelper.included_modules.include?(RedmineTags::Patches::ProjectsHelperPatch)
+    ProjectsHelper.send(:include, RedmineTags::Patches::ProjectsHelperPatch)
+  end
+
+  unless Issue.included_modules.include?(RedmineTags::Patches::IssuePatch)
+    Issue.send(:include, RedmineTags::Patches::IssuePatch)
+  end
+
+  unless IssuesHelper.included_modules.include?(RedmineTags::Patches::IssuesHelperPatch)
+    IssuesHelper.send(:include, RedmineTags::Patches::IssuesHelperPatch)
+  end
+
+  unless ProjectsController.included_modules.include?(RedmineTags::Patches::ProjectsControllerPatch)
+    ProjectsController.send(:include, RedmineTags::Patches::ProjectsControllerPatch)
+  end
+
+  unless AutoCompletesController.included_modules.include?(RedmineTags::Patches::AutoCompletesControllerPatch)
+    AutoCompletesController.send(:include, RedmineTags::Patches::AutoCompletesControllerPatch)
+  end
+
+  unless Query.included_modules.include?(RedmineTags::Patches::QueryPatch)
+    Query.send(:include, RedmineTags::Patches::QueryPatch)
+  end
+
+  unless QueriesHelper.included_modules.include?(RedmineTags::Patches::QueriesHelperPatch)
+    QueriesHelper.send(:include, RedmineTags::Patches::QueriesHelperPatch)
+  end
+end
+
+
+require 'redmine_tags/hooks/model_issue_hook'
+require 'redmine_tags/hooks/views_issues_hook'
+require 'redmine_tags/hooks/views_projects_hook'
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_project_filtering.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,15 @@
+module RedmineProjectFiltering
+
+  # transforms a question and a list of custom fields into something that Project.search can process
+  def self.calculate_tokens(question, custom_fields=nil)
+    list = []
+    list << question if question.present?
+
+    tokens = list.join(' ').scan(%r{((\s|^)"[\s\w]+"(\s|$)|\S+)})
+    tokens = tokens.collect{ |m| m.first.gsub(%r{(^\s*"\s*|\s*"\s*$)}, '') }
+    
+    # tokens must be at least 2 characters long
+    tokens.select {|w| w.length > 1 }
+  end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_tags.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,21 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+module RedmineTags
+  def self.settings() Setting[:plugin_redmine_tags] end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_tags/hooks/model_issue_hook.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,58 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Copyright (c) 2010 Eric Davis
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+module RedmineTags
+  module Hooks
+    class ModelIssueHook < Redmine::Hook::ViewListener
+      def controller_issues_edit_before_save(context={})
+        save_tags_to_issue(context, true)
+      end
+
+      def controller_issues_bulk_edit_before_save(context={})
+        save_tags_to_issue(context, true)
+      end
+
+      # Issue has an after_save method that calls reload (update_nested_set_attributes)
+      # This makes it impossible for a new record to get a tag_list, it's
+      # cleared on reload. So instead, hook in after the Issue#save to update
+      # this issue's tag_list and call #save ourselves.
+      def controller_issues_new_after_save(context={})
+        save_tags_to_issue(context, false)
+        context[:issue].save
+      end
+
+      def save_tags_to_issue(context, create_journal)
+        params = context[:params]
+
+        if params && params[:issue] && !params[:issue][:tag_list].nil?
+          old_tags = context[:issue].tag_list.to_s
+          context[:issue].tag_list = params[:issue][:tag_list]
+          new_tags = context[:issue].tag_list.to_s
+
+          if create_journal and not (old_tags == new_tags || context[:issue].current_journal.blank?)
+            context[:issue].current_journal.details << JournalDetail.new(:property => 'attr',
+                                                                         :prop_key => 'tag_list',
+                                                                         :old_value => old_tags,
+                                                                         :value => new_tags)
+          end
+        end
+      end
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_tags/hooks/views_issues_hook.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,37 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+module RedmineTags
+  module Hooks
+    class ViewsIssuesHook < Redmine::Hook::ViewListener
+
+        # TODO: temporary hack to disable tags on issues
+        ## BEGIN     ~lf 20130712
+
+        # render_on :view_issues_show_details_bottom, :partial => '# issues/tags'
+        # render_on :view_issues_form_details_bottom, :partial => '# issues/tags_form'
+        # render_on :view_issues_sidebar_planning_bottom, :partial => '# issues/tags_sidebar'
+        # render_on :view_issues_bulk_edit_details_bottom, :partial => 'issues/tags_form'
+
+        ## END     ~lf 20130712
+
+
+    end
+  end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_tags/hooks/views_projects_hook.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,10 @@
+module RedmineTags
+  module Hooks
+    class ViewsProjectsHook < Redmine::Hook::ViewListener
+      render_on :view_projects_form, :partial => 'projects/tags_form'
+      render_on :view_projects_show_sidebar_top, :partial => 'projects/tags'
+#      render_on :view_issues_sidebar_planning_bottom, :partial => 'issues/tags_sidebar'
+    end
+  end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_tags/patches/auto_completes_controller_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,57 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+require_dependency 'auto_completes_controller'
+
+module RedmineTags
+  module Patches
+    module AutoCompletesControllerPatch
+      def self.included(base)
+        base.send(:include, InstanceMethods)
+
+        base.class_eval do
+          unloadable
+        end
+      end
+
+
+      module InstanceMethods
+        def issue_tags
+          @name = params[:q].to_s
+          @tags = Issue.available_tags({
+            :project_id => @project,
+            :name_like => @name
+          })
+          render :layout => false, :partial => 'tag_list'
+        end
+
+        def project_tags
+          @name = params[:q].to_s
+          @tags = Project.available_tags :name_like => @name
+          render :layout => false, :partial => 'tag_list'
+        end
+        
+        def project_search_tags
+          @name = params[:q].to_s
+          @tags = Project.available_tags :name_like => @name
+          render :layout => false, :partial => 'search_tag_list'
+        end
+      end
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_tags/patches/issue_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,76 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+require_dependency 'issue'
+
+module RedmineTags
+  module Patches
+    module IssuePatch
+      def self.included(base)
+        base.extend(ClassMethods)
+
+        base.class_eval do
+          unloadable
+          acts_as_taggable
+
+          scope :on_project, lambda { |project|
+            project = project.id if project.is_a? Project
+            { :conditions => ["#{Project.table_name}.id=?", project] }
+          }
+
+          Issue.safe_attributes 'tag_list'
+        end
+      end
+
+      module ClassMethods
+        TAGGING_IDS_LIMIT_SQL = <<-SQL
+          tag_id IN (
+            SELECT #{ActsAsTaggableOn::Tagging.table_name}.tag_id
+              FROM #{ActsAsTaggableOn::Tagging.table_name}
+             WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN (?)
+          )
+        SQL
+
+        # Returns available issue tags
+        # === Parameters
+        # * <i>options</i> = (optional) Options hash of
+        #   * project   - Project to search in.
+        #   * open_only - Boolean. Whenever search within open issues only.
+        #   * name_like - String. Substring to filter found tags.
+        def available_tags(options = {})
+          ids_scope = Issue.visible
+          ids_scope = ids_scope.on_project(options[:project]) if options[:project]
+          ids_scope = ids_scope.open if options[:open_only]
+
+          conditions = [""]
+
+          # limit to the tags matching given %name_like%
+          if options[:name_like]
+            conditions[0] << "#{ActsAsTaggableOn::Tag.table_name}.name LIKE ? AND "
+            conditions << "%#{options[:name_like].downcase}%"
+          end
+
+          conditions[0] << TAGGING_IDS_LIMIT_SQL
+          conditions << ids_scope.map{ |issue| issue.id }.push(-1)
+
+          self.all_tag_counts(:conditions => conditions)
+        end
+      end
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_tags/patches/issues_helper_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,37 @@
+
+require_dependency 'issues_helper'
+
+module RedmineTags
+  module Patches
+    module IssuesHelperPatch
+      def self.included(base)
+        base.send(:include, InstanceMethods)
+      end
+
+      module InstanceMethods
+        include TagsHelper
+
+        def sidebar_tags
+          unless @sidebar_tags
+            @sidebar_tags = []
+            if :none != RedmineTags.settings[:issues_sidebar].to_sym
+              @sidebar_tags = Issue.available_tags({
+                :project => @project,
+                :open_only => (RedmineTags.settings[:issues_open_only].to_i == 1)
+              })
+            end
+          end
+          @sidebar_tags
+        end
+
+        def render_sidebar_tags
+          render_tags_list(sidebar_tags, {
+            :show_count => (RedmineTags.settings[:issues_show_count].to_i == 1),
+            :open_only => (RedmineTags.settings[:issues_open_only].to_i == 1),
+            :style => RedmineTags.settings[:issues_sidebar].to_sym
+          })
+        end
+      end
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_tags/patches/project_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,72 @@
+# C4DM
+
+require_dependency 'project'
+
+module RedmineTags
+  module Patches
+    module ProjectPatch
+      def self.included(base) # :nodoc:
+        base.extend(ClassMethods)
+        base.send(:include, InstanceMethods)
+
+        base.class_eval do
+          unloadable
+          acts_as_taggable
+
+          Project.safe_attributes 'tag_list'
+
+          # TODO: review need for this callback (uneeded on update) ~lf.03042013
+          after_create :save_tags
+        end
+      end
+
+      module InstanceMethods
+        def save_tags
+          self.tags = Tag.transaction do
+            @tag_list.each(&:save)
+          end
+        end
+      end
+
+      module ClassMethods
+        TAGGING_IDS_LIMIT_SQL = <<-SQL
+            tag_id IN (
+                SELECT #{ActsAsTaggableOn::Tagging.table_name}.tag_id
+                FROM #{ActsAsTaggableOn::Tagging.table_name}
+                WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN (?)
+            )
+        SQL
+
+        def search_by_question(question)
+          if question.length > 1
+            search(RedmineProjectFiltering.calculate_tokens(question), nil, :all_words => true).first.sort_by(&:lft)
+          else
+            all(:order => 'lft')
+          end
+        end
+
+        # Returns available project tags
+        # Does not return tags from private projects
+        # === Parameters
+        # * <i>options</i> = (optional) Options hash of
+        #   * name_like - String. Substring to filter found tags.
+        def available_tags( options = {} )
+          ids_scope = Project.visible
+
+          conditions = [""]
+
+          # limit to the tags matching given %name_like%
+          if options[:name_like]
+            conditions[0] << "#{ActsAsTaggableOn::Tag.table_name}.name LIKE ? AND "
+            conditions << "%#{options[:name_like].downcase}%"
+          end
+
+          conditions[0] << TAGGING_IDS_LIMIT_SQL
+          conditions << ids_scope.map{ |issue| issue.id }.push(-1)
+
+          self.all_tag_counts(:conditions => conditions)
+        end
+      end
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_tags/patches/projects_controller_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+require_dependency 'projects_controller'
+
+module RedmineTags
+  module Patches
+    module ProjectsControllerPatch
+      def self.included(base)
+        base.send(:include, InstanceMethods)
+        base.class_eval do
+          unloadable
+          # skip_before_filter :authorize, :only => [:set_fieldset_status]
+          # skip_before_filter :find_project, :only => [:set_fieldset_status]
+
+          alias :index filtered_index
+        end
+      end
+
+      module InstanceMethods
+        def paginate_projects
+          sort_init 'name'
+          sort_update %w(name lft created_on updated_on)
+          @limit = per_page_option
+
+          # Only top-level visible projects are counted --lf.10Jan2013
+          top_level_visible_projects = @projects.visible_roots
+          @project_count = top_level_visible_projects.count
+
+          # Project.visible_roots.find(@projects).count
+
+          @project_pages = Redmine::Pagination::Paginator.new @project_count, @limit, params['page']
+          @offset ||= @project_pages.current.offset
+        end
+
+        # Lists visible projects. Paginator is for top-level projects only
+        # (subprojects belong to them)
+        def filtered_index
+          @project = Project.new
+          filter_projects
+          # get_fieldset_statuses
+
+          sort_clause = "name"
+
+          respond_to do |format|
+            format.html {
+              paginate_projects
+
+              # todo: check ordering ~luisf.14/Jan/2013
+              @projects = @projects[@offset, @limit]
+
+              render :template => 'projects/index', :layout => !request.xhr?
+            }
+            format.api {
+              @offset, @limit = api_offset_and_limit
+              @project_count = Project.visible.count
+              @projects = Project.visible.find(@projects, :offset => @offset, :limit => @limit, :order => 'lft')
+            }
+            format.atom {
+              projects = Project.visible.find(:all, :order => 'created_on DESC', :limit => Setting.feeds_limit.to_i)
+              render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
+            }
+            format.js {
+              paginate_projects
+              @projects = Project.visible_roots.find(@projects, :offset => @offset, :limit => @limit, :order => sort_clause)
+              render :update do |page|
+                page.replace_html 'projects', :partial => 'filtered_projects'
+              end
+            }
+          end
+        end
+
+        private
+
+        def filter_projects
+          # find projects like question
+          @question = (params[:search] || "").strip
+
+          if @question.empty?
+            projects = Project.visible
+          else
+            projects = Project.visible.like(@question)
+          end
+
+          # search for tags
+          if params.has_key?(:tag_search)
+             tag_list = (params[:tag_search] || "").strip.split(",")
+          else
+             tag_list = ""
+          end
+
+          unless tag_list.empty?
+            projects = projects.tagged_with(tag_list)
+          end
+
+          ## TODO: luisf-10Apr2013 should I only return the visible_roots?
+          @projects = projects
+
+        end
+      end
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_tags/patches/projects_helper_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,268 @@
+module RedmineTags
+  module Patches
+    module ProjectsHelperPatch
+
+      def self.included(base) # :nodoc:
+        base.send(:include, InstanceMethods)
+        base.send(:include, TagsHelper)
+
+        base.class_eval do
+          unloadable
+        end
+      end
+
+      module InstanceMethods
+        # Renders a tree of projects that the current user does not belong
+        # to, or of all projects if the current user is not logged in.  The
+        # given collection may be a subset of the whole project tree
+        # (eg. some intermediate nodes are private and can not be seen).  We
+        # are potentially interested in various things: the project name,
+        # description, manager(s), creation date, last activity date,
+        # general activity level, whether there is anything actually hosted
+        # here for the project, etc.
+        def render_project_table_with_filtering(projects, question)
+          custom_fields = ""
+          s = ""
+          if projects.any?
+            tokens = RedmineProjectFiltering.calculate_tokens(question, custom_fields)
+
+            s << "<div class='autoscroll'>"
+            s << "<table class='list projects'>"
+            s << "<thead><tr>"
+
+            s << sort_header_tag('name', :caption => l("field_name"))
+            s << "<th class='tags'>" << l("tags") << "</th>"
+            s << "<th class='managers'>" << l("label_managers") << "</th>"
+            s << sort_header_tag('created_on', :default_order => 'desc')
+            s << sort_header_tag('updated_on', :default_order => 'desc')
+
+            s << "</tr></thead><tbody>"
+
+            original_project = @project
+
+            projects.each do |project|
+              s << render_project_in_table_with_filtering(project, cycle('odd', 'even'), 0, tokens)
+            end
+
+            s << "</table>"
+          else
+            s << "\n"
+          end
+          @project = original_project
+
+          s.html_safe
+        end
+
+        def render_project_in_table_with_filtering(project, oddeven, level, tokens)
+          # set the project environment to please macros.
+          @project = project
+
+          classes = (level == 0 ? 'root' : 'child')
+
+          s = ""
+
+          s << "<tr class='#{oddeven} #{classes} level#{level}'>"
+          s << "<td class='firstcol' align=top><div class='name hosted_here"
+          s << " no_description" if project.description.blank?
+          s << "'>" << link_to( highlight_tokens(project.name, tokens), {:controller => 'projects', :action => 'show', :id => project}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
+          s << "</div>"
+          s << highlight_tokens(render_project_short_description(project), tokens)
+          s << "</td>"
+
+          # taglist
+          s << "<td class='tags' align=top>" << project.tag_counts.collect{ |t| render_project_tag_link(t) }.join(', ') << "</td>"
+
+          s << "<td class='managers' align=top>"
+
+          u = project.users_by_role
+          if u
+            u.keys.each do |r|
+              if r.allowed_to?(:edit_project)
+                mgrs = []
+                u[r].sort.each do |m|
+                  mgrs << link_to_user(m)
+                end
+                if mgrs.size < 3
+                  s << '<nobr>' << mgrs.join(', ') << '</nobr>'
+                else
+                  s << mgrs.join(', ')
+                end
+              end
+            end
+          end
+
+          s << "</td>"
+
+          s << "<td class='created_on' align=top>" << format_date(project.created_on) << "</td>"
+          s << "<td class='updated_on' align=top>" << format_date(project.updated_on) << "</td>"
+
+          s << "</tr>"
+
+          project.children.each do |child|
+            if child.is_public? or User.current.member_of?(child)
+              s << render_project_in_table_with_filtering(child, oddeven, level + 1, tokens)
+            end
+          end
+
+          s.html_safe
+        end
+
+
+
+        # Renders a tree of projects as a nested set of unordered lists
+        # The given collection may be a subset of the whole project tree
+        # (eg. some intermediate nodes are private and can not be seen)
+        def render_project_hierarchy_with_filtering(projects,custom_fields,question)
+          s = []
+          if projects.any?
+            tokens = RedmineProjectFiltering.calculate_tokens(question, custom_fields)
+
+            ancestors = []
+            original_project = @project
+            projects.each do |project|
+              # set the project environment to please macros.
+              @project = project
+              if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
+                s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>"
+              else
+                ancestors.pop
+                s << "</li>"
+                while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
+                  ancestors.pop
+                  s << "</ul></li>"
+                end
+              end
+              classes = (ancestors.empty? ? 'root' : 'child')
+              s << "<li class='#{classes}'><div class='#{classes}'>" +
+                link_to( highlight_tokens(project.name, tokens),
+                  {:controller => 'projects', :action => 'show', :id => project},
+                  :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}"
+                )
+              s << "<ul class='filter_fields'>"
+
+           #  CustomField.usable_for_project_filtering.each do |field|
+           #    value_model = project.custom_value_for(field.id)
+           #    value = value_model.present? ? value_model.value : nil
+           #    s << "<li><b>#{field.name.humanize}:</b> #{highlight_tokens(value, tokens)}</li>" if value.present?
+           #  end
+
+              s << "</ul>"
+              s << "<div class='clear'></div>"
+              unless project.description.blank?
+                s << "<div class='wiki description'>"
+                s << "<b>#{ t(:field_description) }:</b>"
+                s << highlight_tokens(textilizable(project.short_description, :project => project), tokens)
+                s << "\n</div>"
+              end
+              s << "</div>"
+              ancestors << project
+            end
+            ancestors.size.times{ s << "</li></ul>" }
+            @project = original_project
+          end
+          (s.join "\n").html_safe
+        end
+
+        # Renders a tree of projects where the current user belongs
+        # as a nested set of unordered lists
+        # The given collection may be a subset of the whole project tree
+        # (eg. some intermediate nodes are private and can not be seen)
+        def render_my_project_hierarchy_with_tags(projects)
+
+          s = ''
+
+          original_project = @project
+
+          projects.each do |project|
+            if project.root? || !projects.include?(project.parent)
+              s << render_my_project_in_hierarchy_with_tags(project)
+            end
+          end
+
+          @project = original_project
+
+          if s != ''
+            a = ''
+            a << "<ul class='projects root'>\n"
+            a << s
+            a << "</ul>\n"
+            s = a
+          end
+
+          s.html_safe
+
+        end
+
+
+
+
+        def render_my_project_in_hierarchy_with_tags(project)
+
+          s = ''
+
+          if User.current.member_of?(project)
+
+            # set the project environment to please macros.
+            @project = project
+
+            classes = (project.root? ? 'root' : 'child')
+
+            s << "<li class='#{classes}'><div class='#{classes}'>" +
+              link_to_project(project, {}, :class => "project my-project")
+            if project.is_public?
+              s << " <span class='public'>" << l(:field_is_public) << "</span>"
+            else
+              s << " <span class='private'>" << l(:field_is_private) << "</span>"
+            end
+
+            tc = project.tag_counts
+            if tc.empty?
+              s << " <span class='no-tags'>" << l(:field_no_tags) << "</span>"
+            else
+              s << " <span class='tags'>" << tc.collect{ |t| render_project_tag_link(t) }.join(', ') << "</span>"
+            end
+
+            s << render_project_short_description(project)
+
+            s << "</div>\n"
+
+            cs = ''
+            project.children.each do |child|
+              cs << render_my_project_in_hierarchy_with_tags(child)
+            end
+
+            if cs != ''
+              s << "<ul class='projects'>\n" << cs << "</ul>\n";
+            end
+
+          end
+
+          s.html_safe
+
+        end
+
+        private
+
+        # copied from search_helper. This one doesn't escape html or limit the text length
+        def highlight_tokens(text, tokens)
+          return text unless text && tokens && !tokens.empty?
+          re_tokens = tokens.collect {|t| Regexp.escape(t)}
+          regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE
+          result = ''
+          text.split(regexp).each_with_index do |words, i|
+            words = words.mb_chars
+            if i.even?
+              result << words
+            else
+              t = (tokens.index(words.downcase) || 0) % 4
+              result << content_tag('span', words, :class => "highlight token-#{t}")
+            end
+          end
+          result.html_safe
+        end
+
+      end
+    end
+  end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_tags/patches/queries_helper_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,49 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+require_dependency 'queries_helper'
+
+module RedmineTags
+  module Patches
+    module QueriesHelperPatch
+      def self.included(base)
+        base.send(:include, InstanceMethods)
+
+        base.class_eval do
+          unloadable
+          alias_method :column_content_original, :column_content
+          alias_method :column_content, :column_content_extended
+        end
+      end
+
+
+      module InstanceMethods
+        include TagsHelper
+
+
+        def column_content_extended(column, issue)
+          if column.name.eql? :tags
+            column.value(issue).collect{ |t| render_tag_link(t) }.join(', ')
+          else
+            column_content_original(column, issue)
+          end
+        end
+      end
+    end
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/redmine_tags/lib/redmine_tags/patches/query_patch.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,85 @@
+# This file is a part of redmine_tags
+# redMine plugin, that adds tagging support.
+#
+# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
+#
+# redmine_tags is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# redmine_tags is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
+
+require_dependency 'query'
+
+module RedmineTags
+  module Patches
+    module QueryPatch
+      def self.included(base)
+        base.send(:include, InstanceMethods)
+
+        base.class_eval do
+          unloadable
+
+          alias_method :statement_original, :statement
+          alias_method :statement, :statement_extended
+
+          alias_method :available_filters_original, :available_filters
+          alias_method :available_filters, :available_filters_extended
+
+          base.add_available_column(QueryColumn.new(:tags))
+        end
+      end
+
+
+      module InstanceMethods
+        def statement_extended
+          filter  = filters.delete 'tags'
+          clauses = statement_original || ""
+
+          if filter
+            filters.merge!( 'tags' => filter )
+
+            op = operator_for('tags')
+            case op
+            when '=', '!'
+              issues = Issue.tagged_with(values_for('tags').clone)
+            when '!*'
+              issues = Issue.tagged_with(ActsAsTaggableOn::Tag.all.map(&:to_s), :exclude => true)
+            else
+              issues = Issue.tagged_with(ActsAsTaggableOn::Tag.all.map(&:to_s), :any => true)
+            end
+
+            compare   = op.eql?('!') ? 'NOT IN' : 'IN'
+            ids_list  = issues.collect{ |issue| issue.id }.push(0).join(',')
+
+            clauses << " AND " unless clauses.empty?
+            clauses << "( #{Issue.table_name}.id #{compare} (#{ids_list}) ) "
+          end
+
+          clauses
+        end
+
+
+        def available_filters_extended
+          unless @available_filters 
+            available_filters_original.merge!({ 'tags' => {
+              :name   => l(:tags),
+              :type   => :list_optional,
+              :order  => 6,
+              :values => Issue.available_tags(:project => project).collect{ |t| [t.name, t.name] }
+            }})
+          end
+          @available_filters
+        end
+      end
+    end
+  end
+end
+
Binary file public/favicon.ico has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/public/javascripts/repository.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,8 @@
+function toggle_ext_url() {
+    if (document.getElementById('repository_is_external').checked) {
+        document.getElementById('repository_external_url').disabled = false;
+    } else {
+	document.getElementById('repository_external_url').disabled = true;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/public/javascripts/ssamr_institutions.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,31 @@
+
+
+/* SSAMR specific functions */
+
+/* institution related functions */
+$(document).ready(function(){
+	$('#ssamr_user_details_institution_type_true').click(function() {
+        $('#ssamr_user_details_other_institution').attr('disabled', 'disabled');
+        $('#ssamr_user_details_institution_id').removeAttr('disabled');
+	});
+
+	$('#ssamr_user_details_institution_type_false').click(function() {
+        $('#ssamr_user_details_other_institution').removeAttr('disabled');
+        $('#ssamr_user_details_institution_id').attr('disabled', 'disabled');
+	});
+
+    if($('#ssamr_user_details_institution_type_true').checked)
+        $('#ssamr_user_details_other_institution').attr('disabled', 'disabled');
+    else if($('ssamr_user_details_institution_type_false').checked)
+        $('#ssamr_user_details_institution_id').attr('disabled', 'disabled');
+	else {
+		$('#ssamr_user_details_other_institution').attr('disabled', 'disabled');
+        $('#ssamr_user_details_institution_id').removeAttr('disabled');
+        $('#ssamr_user_details_institution_type_true').checked = true;
+        $('#ssamr_user_details_institution_type_false').checked = false;
+	}
+}
+);
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/public/javascripts/ssamr_registration.js	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,6 @@
+/* SSAMR specific functions */
+
+/* initialisation: disabling  the other institution text box */
+$(document).ready(function(){
+        $('#ssamr_user_details_other_institution').attr('disabled', 'disabled');
+    });
\ No newline at end of file
--- a/public/stylesheets/application.css	Tue Sep 09 09:29:00 2014 +0100
+++ b/public/stylesheets/application.css	Tue Sep 09 10:02:18 2014 +0100
@@ -5,7 +5,12 @@
 #content h1, h2, h3, h4 {color: #555;}
 h2, .wiki h1 {font-size: 20px;}
 h3, .wiki h2 {font-size: 16px;}
-h4, .wiki h3 {font-size: 13px;}
+h4, .wiki h4, .wiki h5, .wiki h6 {font-size: 14px;}
+.wiki h3 {font-size: 15px;}
+.wiki h5 {font-weight: normal; font-style: italic; }
+.wiki h6 {font-weight: normal; font-style: normal; }
+
+
 h4 {border-bottom: 1px dotted #bbb;}
 
 /***** Layout *****/
@@ -25,12 +30,14 @@
 
 #account {float:right;}
 
-#header {min-height:5.3em;margin:0;background-color:#628DB6;color:#f8f8f8; padding: 4px 8px 20px 6px; position:relative;}
+#header {height:68px;margin:0;background-color:#628DB6;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
 #header a {color:#f8f8f8;}
 #header h1 a.ancestor { font-size: 80%; }
-#quick-search {float:right;}
 
-#main-menu {position: absolute;  bottom: 0px;  left:6px; margin-right: -500px;}
+#project-search-jump {float:right; }
+
+
+#main-menu {position: absolute;  bottom: 0px;  left:8px; margin-right: -500px;}
 #main-menu ul {margin: 0;  padding: 0;}
 #main-menu li {
   float:left;
@@ -190,7 +197,8 @@
 tr.changeset td.committed_on { text-align: center; width: 15%; white-space:nowrap;}
 
 table.files tbody th {text-align:left;}
-table.files tr.file td.filename { text-align: left; padding-left: 24px; }
+table.files tr.file td { text-align: center; }
+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%; }
@@ -270,9 +278,12 @@
 h3.version { background: url(../images/package.png) no-repeat 0% 50%; padding-left: 20px; }
 
 div.issues h3 { background: url(../images/ticket.png) no-repeat 0% 50%; padding-left: 20px; }
+div.activity h3 { background: url(../images/ticket.png) no-repeat 0% 50%; padding-left: 20px; }
 div.members h3 { background: url(../images/group.png) no-repeat 0% 50%; padding-left: 20px; }
 div.news h3 { background: url(../images/news.png) no-repeat 0% 50%; padding-left: 20px; }
 div.projects h3 { background: url(../images/projects.png) no-repeat 0% 50%; padding-left: 20px; }
+div.tags h3 { background: url(../images/ticket_note.png) no-repeat 0% 50%; padding-left: 20px; }
+div.institutions h3 { background: url(../images/group.png) no-repeat 0% 50%; padding-left: 20px; }
 
 #watchers select {width: 95%; display: block;}
 #watchers a.delete {opacity: 0.4; vertical-align: middle;}
@@ -313,6 +324,11 @@
 .splitcontent {overflow:auto;}
 .splitcontentleft{float:left; width:49%;}
 .splitcontentright{float:right; width:49%;}
+
+.threecolumnleft{float:left; clear:left; width: 32%;}
+.threecolumnright{float:right; clear:right; width: 32%;}
+.threecolumnmid{margin-left: 33%; margin-right: 33%;}
+
 form {display: inline;}
 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
 fieldset {border: 1px solid #e4e4e4; margin:0;}
@@ -369,22 +385,52 @@
 div.journal {overflow:auto;}
 div.journal.private-notes {border-left:2px solid #d22; padding-left:4px; margin-left:-6px;}
 
-div#activity dl, #search-results { margin-left: 2em; }
-div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
-div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
-div#activity dt.me .time { border-bottom: 1px solid #999; }
-div#activity dt .time { color: #777; font-size: 80%; }
+div#activity dl, div#news dl, #search-results { margin-left: 2em; }
+div#activity .box dl { margin-left: 0; }
+div#activity dd, div#news dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
+div#activity dt, div#news dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
+div#activity dt.me .time, div#news dt.me .time { border-bottom: 1px solid #999; }
+div#activity dt .time, div#news dt .time, .projects .latest .time, .projects .busy .managers { color: #777; font-size: 80%; }
 div#activity dd .description, #search-results dd .description { font-style: italic; }
-div#activity span.project:after, #search-results span.project:after { content: " -"; }
+div#activity span.project:after, div#news span.project:after, #search-results span.project:after { content: " -"; }
 div#activity dd span.description, #search-results dd span.description { display:block; color: #808080; }
 div#activity dt.grouped {margin-left:5em;}
 div#activity dd.grouped {margin-left:9em;}
 
+div#members dl { margin-left: 2em; }
+div#members dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
+div#members dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
+div#members dt.me .email { border-bottom: 1px solid #999; }
+div#members dt .email { color: #777; font-size: 80%; }
+div#members dd .roles { font-style: italic; }
+
+div#memberbox h3 { 
+  background: url(../images/group.png) no-repeat 0% 50%; 
+  padding-left: 20px;
+}
+div#memberbox p { 
+  padding-left: 20px;
+  margin-left: 0;
+}
+
+div.issues ul {
+  padding-left: 20px;
+  margin-left: 0;
+  list-style-type: none;
+}
+div.issues p {
+  padding-left: 20px;
+  margin-left: 0;
+}
+
+.projects .latest .title, .projects .busy .title { margin-right: 0.5em; }
+.tipoftheday .tip { margin-left: 2em; margin-top: 0.5em; }
+
 #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; }
 
-div#search-results-counts {float:right;}
-div#search-results-counts ul { margin-top: 0.5em; }
-div#search-results-counts  li { list-style-type:none; float: left; margin-left: 1em; }
+div#search-results-counts { display: block; padding-left: 0; margin-left: 0; }
+div#search-results-counts ul { margin-top: 0.5em; padding-left: 0; margin-left: 0; }
+div#search-results-counts li { display: inline; list-style-type: none; margin-right: 1em; }
 
 dt.issue { background-image: url(../images/ticket.png); }
 dt.issue-edit { background-image: url(../images/ticket_edit.png); }
@@ -427,16 +473,20 @@
 input#issue_subject { width: 99%; }
 select#issue_done_ratio { width: 95px; }
 
-ul.projects {margin:0; padding-left:1em;}
-ul.projects ul {padding-left:1.6em;}
-ul.projects.root {margin:0; padding:0;}
-ul.projects li {list-style-type:none;}
+ul.projects { margin: 0; padding-left: 1em; }
+ul.projects.root { margin: 0;  padding: 0; }
+/*ul.projects ul.projects { border-left: 3px solid #e0e0e0; } */
+ul.projects li.root { list-style-type:none; margin-bottom: 1em; }
+ul.projects li.child { list-style-type:none; }
+ul.projects div.root a.project { font-weight: bold; }
 
+li.latest, li.busy { margin-bottom: 0.5em; }
 #projects-index ul.projects ul.projects { border-left: 3px solid #e0e0e0; padding-left:1em;}
 #projects-index ul.projects li.root {margin-bottom: 1em;}
 #projects-index ul.projects li.child {margin-top: 1em;}
-#projects-index ul.projects div.root a.project { font-family: "Trebuchet MS", Verdana, sans-serif; font-weight: bold; font-size: 16px; margin: 0 0 10px 0; }
-.my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; }
+#projects-index ul.projects div.root a.project { font-weight: bold; font-size: 16px; margin: 0 0 10px 0; }
+/* .my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; } */
+
 
 #notified-projects>ul, #tracker_project_ids>ul, #custom_field_project_ids>ul {max-height:250px; overflow-y:auto;}
 
@@ -473,6 +523,9 @@
   min-height: 1.8em;
   clear:left;
 }
+.tabular ul{
+margin-top: -4px;
+}
 
 html>body .tabular p {overflow:hidden;}
 
@@ -490,6 +543,15 @@
   width: 175px;
 }
 
+.tabular .splitcontentleft .box p, .tabular .splitcontentright .box p, .splitcontentleft .tabular p, .splitcontentright .tabular p, .tabular .splitcontentleft .box ul, .tabular .splitcontentright .box ul, .splitcontentleft .tabular ul, .splitcontentright .tabular ul {
+padding-left: 120px;
+}
+
+.tabular .splitcontentleft .box label, .tabular .splitcontentright .box label, .splitcontentleft .tabular label, .splitcontentright .tabular label {
+margin-left: -120px;
+width: 115px;
+}
+
 .tabular label.floating{
   font-weight: normal;
   margin-left: 0px;
@@ -564,6 +626,9 @@
 #attachments_fields .ajax-waiting input.filename {background:url(../images/hourglass.png) no-repeat 0px 50%;}
 #attachments_fields .ajax-loading input.filename {background:url(../images/loading.gif) no-repeat 0px 50%;}
 #attachments_fields div.ui-progressbar { width: 100px; height:14px; margin: 2px 0 -5px 8px; display: inline-block; }
+#attachments_fields input[type=text] {margin-left: 8px; }
+#attachments_fields img {vertical-align: middle;}
+
 a.remove-upload {background: url(../images/delete.png) no-repeat 1px 50%; width:1px; display:inline-block; padding-left:16px;}
 a.remove-upload:hover {text-decoration:none !important;}
 
@@ -572,7 +637,8 @@
 div.attachments { margin-top: 12px; }
 div.attachments p { margin:4px 0 2px 0; }
 div.attachments img { vertical-align: middle; }
-div.attachments span.author { font-size: 0.9em; color: #888; }
+div.attachments span.author { font-size: 0.9em; color: #888; font-style: italic; padding-left: 4px }
+div.attachments span.size_and_count { font-size: 0.9em; color: #888; padding-left: 4px; }
 
 div.thumbnails {margin-top:0.6em;}
 div.thumbnails div {background:#fff;border:2px solid #ddd;display:inline-block;margin-right:2px;}
@@ -744,9 +810,9 @@
 
 #roadmap table.progress td { height: 1.2em; }
 /***** Tabs *****/
-#content .tabs {height: 2.6em; margin-bottom:1.2em; position:relative; overflow:hidden;}
-#content .tabs ul {margin:0; position:absolute; bottom:0; padding-left:0.5em; width: 2000px; border-bottom: 1px solid #bbbbbb;}
-#content .tabs ul li {
+#content .tabs, #content .tabs2 {height: 2.6em; margin-bottom:1.2em; position:relative; overflow:hidden;}
+#content .tabs ul, #content .tabs2 ul {margin:0; position:absolute; bottom:0; padding-left:0.5em; width: 2000px; border-bottom: 1px solid #bbbbbb;}
+#content .tabs ul li, #content .tabs2 ul li {
   float:left;
   list-style-type:none;
   white-space:nowrap;
@@ -755,7 +821,7 @@
   position:relative;
   margin-bottom:-1px;
 }
-#content .tabs ul li a{
+#content .tabs ul li a, #content .tabs2 ul li a {
   display:block;
   font-size: 0.9em;
   text-decoration:none;
@@ -770,19 +836,19 @@
   border-top-right-radius:3px;
 }
 
-#content .tabs ul li a:hover {
+#content .tabs ul li a:hover, #content .tabs2 ul li a:hover {
   background-color: #ffffdd;
   text-decoration:none;
 }
 
-#content .tabs ul li a.selected {
+#content .tabs ul li a.selected, #content .tabs2 ul li a.selected {
   background-color: #fff;
   border: 1px solid #bbbbbb;
   border-bottom: 1px solid #fff;
   color:#444;
 }
 
-#content .tabs ul li a.selected:hover {background-color: #fff;}
+#content .tabs ul li a.selected:hover, #content .tabs2 ul li a.selected:hover {background-color: #fff;}
 
 div.tabs-buttons { position:absolute; right: 0; width: 48px; height: 24px; background: white; bottom: 0; border-bottom: 1px solid #bbbbbb; }
 
@@ -1065,7 +1131,31 @@
 
 #activity dt, .journal {clear: left;}
 
-.journal-link {float: right;}
+td.username img.gravatar {
+    margin: 0 0.5em 0 0;
+    vertical-align: top;
+}
+
+#activity dt img.gravatar, #members dt img.gravatar, #active-colleagues img.gravatar {
+    float: left;
+    clear: left;
+    margin: 0.2em 1em 0.4em 0;
+}
+
+/* Used on 12px Gravatar img tags without the icon background */
+.icon-gravatar {
+  float: left;
+  margin-right: 4px;
+}
+
+#activity dt,
+.journal {
+    clear: left;
+}
+
+.journal-link {
+    float: right;
+}
 
 h2 img { vertical-align:middle; }
 
--- a/public/stylesheets/scm.css	Tue Sep 09 09:29:00 2014 +0100
+++ b/public/stylesheets/scm.css	Tue Sep 09 10:02:18 2014 +0100
@@ -35,7 +35,7 @@
 #changes-legend li { float: left; background-position: 5px 0; }
 
 table.filecontent { border: 1px solid #e2e2e2;  border-collapse: collapse; width:98%; background-color: #fafafa; }
-table.filecontent tbody {font-family:"Liberation Mono", Courier, monospace; font-size:12px;}
+table.filecontent tbody {font-family:"SourceCodePro-Regular","Liberation Mono", Courier, monospace; font-size:12px;}
 table.filecontent th { border: 1px solid #e2e2e2; background-color: #eee; }
 table.filecontent th.filename { background-color: #e4e4d4; text-align: left; padding:5px;}
 table.filecontent tr.spacing th { text-align:center; }
@@ -61,7 +61,7 @@
 table.filecontent td.line-code pre {
     margin: 0px;
     white-space: pre-wrap;
-    font-family:"Liberation Mono", Courier, monospace; font-size:12px;
+    font-family:"SourceCodePro-Regular","Liberation Mono", Courier, monospace; font-size:12px;
 }
 
 table.filecontent tr:target th.line-num { background-color:#E0E0E0; color: #777; }
Binary file public/themes/soundsoftware/images/home.png has changed
Binary file public/themes/soundsoftware/images/wrench.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/public/themes/soundsoftware/stylesheets/application.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,306 @@
+@import url(../../../stylesheets/application.css);
+
+/* Colours:
+
+   #be5700 link (orange)
+
+   #3e442c text (dark green)
+   (OK for headings, but small text is easier to read in black)
+
+   #ffa801 logo (yellow)
+
+   #525a38 logotype (olive)
+
+   #fdfaf0 background (cream)
+
+   #fdfbf5 alternate background (lighter cream)
+
+   #ffe69b highlight (light yellow)
+
+   #a9b680 detail (light green)
+*/
+
+body {
+  background: #fdfbf5;
+  color: #000;
+  margin: 0;
+  margin-bottom: 40px;
+  min-width: 620px;
+}
+
+h1 {
+  color: #3e442c;
+}
+
+h2,h3,h4,.wiki h1 {
+  color: #3e442c;
+}
+
+.wiki h2,.wiki h3,.wiki h4 {
+  color: #000;
+}
+
+h2,.wiki h1 {
+  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;
+    background-color: #fdfaf0;
+    color: #3e442c;
+    line-height: 1.5em;
+    border: 1px solid #a9b680;
+    border-left: 4px solid #a9b680;
+}
+
+.box h4 {
+    margin-top: 0;
+    padding-top: 0;
+}
+
+.odd {background-color:#fdf7e4;}
+.even {background-color: #fdfaf0;}
+
+.box .more { margin-left: 40px; }
+
+.box .institution { font-size: 0.95em; }
+
+#content .tabs { margin-bottom: 0; }
+
+table.list th {  background-color: #fdfaf0; border-bottom: 1px solid #a9b680; }
+table.list { border: 1px solid #a9b680; /* border-left: 4px solid #a9b680; */ }
+tr.entry { border-left: 1px solid #a9b680; border-right: 1px solid #a9b680; }
+tr.entry:last-child { border-bottom: 1px solid #a9b680; }
+
+table.projects th { text-align: left; }
+table.projects th.managers { color: #3e442c; }
+table.projects .root .name { font-size: 1.2em; padding-top: 0.3em; }
+table.projects .description { padding-bottom: 0.5em; }
+table.projects .no_description { padding-bottom: 0.5em; }
+table.projects .hosted_here { font-weight: bold; }
+table.projects .child .name { font-weight: normal; }
+table.projects .child .description { font-size: 0.95em; }
+table.projects .child .firstcol { padding-left: 1em }
+table.projects .level2 .firstcol { padding-left: 2em; }
+table.projects .level3 .firstcol { padding-left: 3em; }
+
+ul.projects .public, ul.projects .private { padding-left: 0.5em; color: #3e442c; font-size: 0.9em }
+
+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; }
+#header {  position: absolute; z-index: 0; top: 0; width: 100%; background: #fdfbf5; border-bottom: 2px solid #a9b680; /* height:80px; */ padding: 20px 0 0.5em 0; margin-bottom: 0; }
+#header a { color: #be5700; }
+#header h1 { color: #525a38; margin-top: 25px; font-size: 3em; font-weight: normal; margin-left: 10px; }
+#header h1#project-title.long-title { font-size: 2em; line-height: 0.95em }
+#header #project-title a, #header #project-title a:hover { color: #525a38; text-decoration: none; }
+.header-general h1 {
+  background: url('soundsoftware-logo-code-title-only-transparent.png') no-repeat 0 0;
+  text-indent: -9999px;
+  width: 537px;
+  height: 34px;
+ }
+
+#quick-search { margin-right: 6px; margin-top: 1em; color: #000; }
+#project-jump-box { float: right;  margin-right: 6px; margin-top: 5px; color: #000; }
+#project-ancestors-title {
+    margin-bottom: -6px;
+    margin-left: 12px;
+    margin-top: 6px;
+}
+
+#main-menu { position: absolute; top: 100px; /* background-color: #be5700; */ left: 2px; border-top: 0; width: 100%;/* height: 1.82em; */ padding: 0; margin: 0; border: 0; }
+#main-menu li { margin: 0; padding: 0; }
+#main-menu li a { background-color: #fdfbf5; color: #be5700; border-right: 1px solid #a9b680; font-size: 97%; padding: 0em 8px 0em 10px; font-weight: normal; }
+#main-menu li:last-child a { border-right: 0; }
+#main-menu li a:hover { background-color: #fdfbf5; color: #be5700; text-decoration: underline; }
+#main-menu li a.selected, #main-menu li a.selected:hover { background-color: #fdfbf5; color: #3e442c; }
+
+#footer { background-color: #fdfbf5; border: 0; border-top: 2px solid #a9b680; color: #3e442c; text-align: right; }
+#footer a { color: #be5700; font-weight: bold; }
+
+p.statistics { text-align: right; font-size:0.9em; color: #666; }
+
+#main { margin-top: 135px; font:95%; background: #fdfaf0; }
+#main a { font-weight: medium; color: #be5700;}
+#main a:hover { color: #be5700; text-decoration: underline; }
+#content { background: #fdfbf5; }
+/*#content .tabs ul { bottom:-1px; }
+*/
+h2, h3, h4, .wiki h1, .wiki h2, .wiki h3 { border-bottom: 0px; }
+/*h2, .wiki h1 { letter-spacing:-1px; }
+*/
+/* h4 { border-bottom: dotted 1px #c0c0c0; } */
+
+.wiki p, .wiki li { margin-left: 30px; margin-right: 3em; }
+
+.overviewfields h4 { margin-left: 30px; margin-bottom: 0; }
+.overviewfields ul { margin-top: 0; }
+
+.repository-info .wiki p { margin-left: 0 }
+
+div.issue { background: #fdfaf0; border: 1px solid #a9b680; border-left: 4px solid #a9b680; }
+
+#top-menu a.home, #top-menu a.my-page, #top-menu a.projects, #top-menu a.administration, #top-menu a.help { 
+    background-position: 0% 40%;
+    background-repeat: no-repeat;
+    padding-left: 20px;
+    padding-top: 2px;
+    padding-bottom: 3px;
+}
+
+#top-menu a.home { background-image: url(../images/home.png); }
+#top-menu a.my-page { background-image: url(../../../images/user.png); }
+#top-menu a.projects { background-image: url(../../../images/projects.png); }
+#top-menu a.administration { background-image: url(../images/wrench.png); }
+#top-menu a.help { background-image: url(../../../images/help.png); }
+
+#welcomepage {
+    width: 80%;
+    margin-left: 71px;
+    margin-top: 10px;
+}
+#welcomepage blockquote {
+    border: 0;
+    margin-top: 2em;
+    padding-left: 0;
+    margin-left: 0;
+}
+#welcomepage h2 {
+    font-size: 2em;
+    margin-bottom: 0.8em;
+}
+#welcomepage p, #welcomepage li {
+    font-size: 1.25em;
+    color: #3e442c;
+}
+#welcomepage p {
+    margin-left: 2em;
+    margin-top: 1em;
+    margin-bottom: 0.7em;
+}
+#welcomepage a {
+    white-space: nowrap;
+}
+#welcomepagenews {
+    float: right;
+    width: 27%;
+    margin-right: 3%;
+    margin-top: 1em;
+    margin-left: 2em;
+    margin-bottom: 2em;
+}
+
+div.flash.newsalert {
+  float: right;
+  width: 25%;
+  padding: 0px 10px 2px 24px;
+  margin: 1.2em 1em 1em 2em;
+  border-color: #dd0000;
+}
+
+/* for Javadoc in Embedded context: */
+
+.TableHeadingColor { background-color: #fdf7e4; color: #3e442c; border: 0px solid #fff; }
+.TableHeadingColor th { background-color: #fdf7e4; color: #3e442c; border: 1px solid #a9b680; }
+.TableHeadingColor th font { font-size: 1.4em; color: #3e442c; }
+.TableSubHeadingColor { background-color: #fdfaf0; color: #3e442c; border: 0px solid #fff; }
+.TableSubHeadingColor th { background-color: #fdfaf0; color: #3e442c; border: 1px solid #a9b680; }
+.TableRowColor { background-color: #fdfbf5; color: #000000; border: 0; }
+.TableRowColor td { background-color: #fdfbf5; color: #000000; border: 0; }
+.NavBarCell1 { background-color: #ffe69b; color:#000000 }
+
+.controller-redmine_embedded table { border: 0px solid #fff; }
+.controller-redmine_embedded h3 { margin-top: 0.5em; }
+.controller-redmine_embedded hr { color: #a9b680; background-color: #a9b680 }
+.controller-redmine_embedded center { text-align: left; } /* haha */
+.controller-redmine_embedded th { font-weight: normal; text-align: left; }
+.controller-redmine_embedded caption { text-align: left; font-weight: bold; font-size: 14px; margin-top: 1em; }
+.controller-redmine_embedded li.blockList { list-style-type: none; }
+
+.controller-redmine_embedded .summary { font-style: normal; margin-bottom: 1.5em; }
+
+/* Special hack to hide the FRAMES | NO FRAMES links -- they don't
+   work in this context -- and right-align the All Classes and Detail links */
+.controller-redmine_embedded .NavBarCell2 a[target=_top] { width: 0px; visibility: hidden; }
+.controller-redmine_embedded .NavBarCell2 + .NavBarCell2 { text-align: right; }
+.controller-redmine_embedded .NavBarCell3 + .NavBarCell3 { text-align: right; }
+
+.topNav, .bottomNav { position: relative; overflow: hidden; }
+.topNav ul li, .bottomNav ul li { float: left; list-style-type: none; margin-right: 14px; position: relative; font-weight: bold; }
+.controller-redmine_embedded .subNav .navList { display: none; }
+
+/* For Doxygen in Embedded context (though note some of the Javadoc
+   rules will also apply to Doxygen): */
+
+.memItemLeft,
+.memItemRight,
+.memTemplParams,
+.memTemplItemLeft,
+.memTemplItemRight,
+.indexkey,
+.memproto,
+.memproto td,
+.memdoc a { font-family: 'SourceCodePro-Regular', monospace; font-size: 0.9em; font-weight: normal; }
+
+.controller-redmine_embedded .memTemplParams { font-style: italic; }
+
+.controller-redmine_embedded #projectname,
+.controller-redmine_embedded .title { font-weight: bold; font-size: 14px; }
+
+.controller-redmine_embedded h2 { font-size: 16px; } 
+
+.controller-redmine_embedded .memitem { border-bottom: 1px solid #a9b680; padding-top: 0.5em; } 
+.controller-redmine_embedded .memitem:last-child { border-bottom: 0px; } 
+
+.controller-redmine_embedded .contents { margin-top: 0.5em; }
+.controller-redmine_embedded .contents td { padding: 0px; }
+
+.controller-redmine_embedded .contents h1,
+.controller-redmine_embedded .contents h2,
+.controller-redmine_embedded .navigation h1,
+.controller-redmine_embedded .navigation h2 { padding-top: 1em; padding-bottom: 0; }
+
+.controller-redmine_embedded .contents .center { text-align: center; } /* undo javadoc hack above */
+
+/* For MATLAB documentation */
+
+.controller-redmine_embedded #matlabdoc th { text-align: left; }
+
+/* autocomplete positioning fix */
+div.autocomplete {
+	margin-top:136px;
+}
+
+#my_projects_fieldset.collapsible {
+	font-size: 1.0em;
+	font-color: green;
+}
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/public/themes/soundsoftware/stylesheets/fonts-generic.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,31 @@
+@import url(fonts.css?3);
+
+h1, #project-ancestors-title, #top-menu a {
+  font-family: Insider, 'Gill Sans', Tahoma, sans-serif;
+  font-weight: bold;
+}    
+
+#top-menu div, #top-menu li {
+  font-size: 12px;
+}
+
+body,p,li,table { 
+  font-family: Insider, DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; 
+  font-size: 14px;
+  line-height: 1.34;
+  font-weight: normal;
+}
+
+h2,h3,h4,.wiki h1,.embedded h1 { 
+  font-family: Insider, DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; 
+  font-weight: bold;
+  line-height: 1.34;
+}
+
+code,pre,.code,.line-code,.embedded a.el {
+  font-family: 'SourceCodePro-Regular', monospace;
+  font-weight: normal;
+  font-size: 0.9em;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/public/themes/soundsoftware/stylesheets/fonts-mac.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,30 @@
+@import url(fonts.css?3);
+
+h1, #project-ancestors-title, #top-menu a {
+  font-family: Insider, "Lucida Grande", sans-serif;
+  font-weight: bold;
+}    
+
+#top-menu div, #top-menu li {
+  font-size: 12px;
+}
+
+body,p,li,table { 
+  font-family: Insider, "Lucida Grande", sans-serif;
+  font-size: 14px;
+  line-height: 1.34;
+  font-weight: normal;
+}
+
+h2,h3,h4,.wiki h1,.embedded h1 { 
+  font-family: Insider, "Lucida Grande", sans-serif;
+  font-weight: bold;
+  line-height: 1.34;
+}
+
+code,pre,.code,.line-code,.embedded a.el {
+  font-family: 'SourceCodePro-Regular', monospace;
+  font-weight: normal;
+  font-size: 0.9em;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/public/themes/soundsoftware/stylesheets/fonts-ms.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,33 @@
+@import url(fonts.css?3);
+
+/* IE likes us to separate out normal & bold into different fonts
+   rather than use the selectors on the same font */
+
+h1, #project-ancestors-title, #top-menu a {
+  font-family: Insider-Medium, Tahoma, sans-serif;
+  font-weight: normal;
+}    
+
+#top-menu div, #top-menu li {
+  font-size: 12px;
+}
+
+body,p,li,table { 
+  font-family: Insider-Regular, tahoma, verdana, sans-serif; 
+  font-size: 14px;
+  line-height: 1.34;
+  font-weight: normal;
+}
+
+h2,h3,h4,.wiki h1,.embedded h1 { 
+  font-family: Insider-Medium, tahoma, verdana, sans-serif; 
+  font-weight: normal;
+  line-height: 1.34;
+}
+
+code,pre,.code,.line-code,.embedded a.el {
+  font-family: 'SourceCodePro-Regular', monospace;
+  font-weight: normal;
+  font-size: 0.9em;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/public/themes/soundsoftware/stylesheets/fonts.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,48 @@
+
+@font-face {
+    font-family: 'Insider';
+    font-weight: bold;
+    font-style: normal;
+    src: url('fonts/24BC0E_0_0.eot');
+    src: url('fonts/24BC0E_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/24BC0E_0_0.woff') format('woff'), url('fonts/24BC0E_0_0.ttf') format('truetype');
+}
+
+@font-face {
+    font-family: 'Insider';
+    font-weight: normal;
+    font-style: italic;
+    src: url('fonts/276F6F_0_0.eot');
+    src: url('fonts/276F6F_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/276F6F_0_0.woff') format('woff'), url('fonts/276F6F_0_0.ttf') format('truetype');
+}
+
+@font-face {
+    font-family: 'Insider';
+    font-weight: normal;
+    font-style: normal;
+    src: url('fonts/24BC35_0_0.eot');
+    src: url('fonts/24BC35_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/24BC35_0_0.woff') format('woff'), url('fonts/24BC35_0_0.ttf') format('truetype');
+}
+
+@font-face {
+    font-family: 'Insider-Medium';
+    font-weight: normal;
+    font-style: normal;
+    src: url('fonts/24BC0E_0_0.eot');
+    src: url('fonts/24BC0E_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/24BC0E_0_0.woff') format('woff'), url('fonts/24BC0E_0_0.ttf') format('truetype');
+}
+ 
+@font-face {
+    font-family: 'Insider-Regular';
+    font-weight: normal;
+    font-style: normal;
+    src: url('fonts/24BC35_0_0.eot');
+    src: url('fonts/24BC35_0_0.eot?#iefix') format('embedded-opentype'), url('fonts/24BC35_0_0.woff') format('woff'), url('fonts/24BC35_0_0.ttf') format('truetype');
+}
+
+@font-face {
+    font-family: 'SourceCodePro-Regular';
+    font-weight: normal;
+    font-style: normal;
+    src: url('fonts/SourceCodePro-Regular-webfont.eot');
+    src: url('fonts/SourceCodePro-Regular-webfont.eot?#iefix') format('embedded-opentype'), url('fonts/SourceCodePro-Regular-webfont.woff') format('woff'), url('fonts/SourceCodePro-Regular-webfont.ttf') format('truetype');
+}
Binary file public/themes/soundsoftware/stylesheets/soundsoftware-logo-code-title-only-transparent.png has changed
Binary file public/themes/soundsoftware/stylesheets/soundsoftware-logo-code-title-only-transparent.xcf has changed
Binary file public/themes/soundsoftware/stylesheets/soundsoftware-logo-title-only-transparent-beta.png has changed
Binary file public/themes/soundsoftware/stylesheets/soundsoftware-logo-title-only-transparent-beta.xcf has changed
Binary file public/themes/soundsoftware/stylesheets/soundsoftware-logo-title-only-transparent.png has changed
Binary file public/themes/soundsoftware/stylesheets/soundsoftware-logo-title-only.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/public/themes/ssamr/stylesheets/application.css	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,97 @@
+@import url(../../../stylesheets/application.css);
+
+@font-face
+{
+  /* A font by Jos Buivenga (exljbris) -> www.exljbris.com */
+  font-family: Tallys;
+  src: url('Tallys_15.otf');
+}
+
+@font-face
+{
+  font-family: Droid;
+  src: url('DroidSans.ttf');
+}
+
+@font-face
+{
+  font-family: Droid;
+  font-weight: bold;
+  src: url('DroidSans-Bold.ttf');
+}
+
+@font-face
+{
+  font-family: Gillius;
+  src: url('GilliusADFNo2-Regular.otf');
+}
+
+@font-face
+{
+  font-family: Gillius; 
+  font-weight: bold;
+  src: url('GilliusADFNo2-Bold.otf');
+}
+
+body {
+  background: #ffffff;
+  color: #404040;
+  margin: 0;
+  margin-bottom: 40px;
+/*  font-size: 95%; */
+}
+
+body,p,h2,h3,h4,li,table { 
+  font-family: Droid, tahoma, verdana, sans-serif;
+}
+
+h1 {
+  font-family: Tallys, Cambria, Georgia, "Times New Roman", Times, serif;
+  font-weight: normal;
+}    
+
+h2,h3,h4 {
+  color: #000000;
+}
+
+h2 { font-size: 2em; }
+
+#top-menu { position: absolute; top: 0; z-index: 1; left: 0px; width: 100%; font-size: 80%; height: 2em; padding-top: 0.5em; background-color: #000000; }
+#top-menu a { font-weight: bold; }
+#header {  position: absolute; z-index: 0; top: 0; width: 100%; background: #ffffff; border-bottom: 2px solid #000; height:80px; padding: 10px 0 0 0; margin-bottom: 0; }
+#header a { color: #000; }
+#header h1 { color: #000; text-shadow: #ccc 1px 1px; margin-top: 30px; font-size: 3em; font-weight: normal; margin-left: 0.3em; }
+#quick-search { margin-right: 6px; margin-top: 2em; color: #000; }
+#main-menu { position: absolute; top: 90px; background-color: #000000; left: 0; border-top: 0; width: 100%; height: 2em; }
+#main-menu li { margin: 0; padding: 0; padding-top: 1px; }
+#main-menu li a { background-color: #000; border-right: 1px solid #fff; font-size: 90%; padding: 4px 8px 4px 8px; font-weight: bold; }
+#main-menu li a:hover { background-color: #80b0da; color: #ffffff; }
+#main-menu li a.selected, #main-menu li a.selected:hover { background-color: #80b0da; color: #ffffff; }
+
+#footer { background-color: #000; border: 0; color: #fff;}
+#footer a { color: #fff; font-weight: bold; }
+
+#main { margin-top: 135px; font:95%; background: #e8eaec; }
+#main a { font-weight: medium; color: #800;}
+#main a:hover { color: #800; text-decoration: underline; }
+#content { background: #fff; }
+/*#content .tabs ul { bottom:-1px; }
+*/
+h2, h3, h4, .wiki h1, .wiki h2, .wiki h3 { border-bottom: 0px; }
+/*h2, .wiki h1 { letter-spacing:-1px; }
+*/
+h4 { border-bottom: dotted 1px #c0c0c0; }
+
+#top-menu a.home, #top-menu a.my-page, #top-menu a.projects, #top-menu a.administration, #top-menu a.help { 
+    background-position: 0% 40%;
+    background-repeat: no-repeat;
+    padding-left: 20px;
+    padding-top: 2px;
+    padding-bottom: 3px;
+}
+
+#top-menu a.home { background-image: url(../images/home.png); }
+#top-menu a.my-page { background-image: url(../../../images/user.png); }
+#top-menu a.projects { background-image: url(../../../images/projects.png); }
+#top-menu a.administration { background-image: url(../images/wrench.png); }
+#top-menu a.help { background-image: url(../../../images/help.png); }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/fixtures/institutions.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,7 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+one:
+  name: MyString
+
+two:
+  name: MyString
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/fixtures/ssamr_user_details.yml	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,7 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+# one:
+#   column: value
+#
+# two:
+#   column: value
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/unit/institution_test.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class InstitutionTest < ActiveSupport::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/unit/ssamr_user_details_test.rb	Tue Sep 09 10:02:18 2014 +0100
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class SsamrUserDetailsTest < ActiveSupport::TestCase
+  # Replace this with your real tests.
+  test "User Id should be correct" do
+    assert_equal user_id, user.id 
+  end
+end