# HG changeset patch
# User Chris Cannam
# Date 1310636256 -3600
# Node ID 851510f1b5359b959afc5dddc9088831850f91f6
# Parent 1551c61843d22ccb21abef81fce953791f75aea4# Parent 0c939c159af4c00f6d285ef7a10d390400452671
Merge from branch "redmine-1.2"
diff -r 0c939c159af4 -r 851510f1b535 .hgignore
--- a/.hgignore Thu Jul 14 10:32:19 2011 +0100
+++ b/.hgignore Thu Jul 14 10:37:36 2011 +0100
@@ -26,3 +26,5 @@
*.rbc
.svn/
.git/
+*~
+
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/account_controller.rb
--- a/app/controllers/account_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/account_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -77,13 +77,19 @@
# 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 => Setting.default_language)
+
+ @ssamr_user_details = SsamrUserDetail.new
+
else
@user = User.new(params[:user])
@user.admin = false
+
@user.register
+
if session[:auth_source_registration]
@user.activate
@user.login = session[:auth_source_registration][:login]
@@ -98,6 +104,13 @@
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
+ @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)
@@ -269,6 +282,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.deliver_account_activation_request(user)
account_pending
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/application_controller.rb
--- a/app/controllers/application_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/application_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -270,6 +270,21 @@
uri = URI.parse(back_url)
# do not redirect user to another host or to the login or register page
if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
+ # soundsoftware: if 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
end
@@ -322,7 +337,7 @@
if api_request?
logger.error "Form authenticity token is missing or is invalid. API calls must include a proper Content-type header (text/xml or text/json)."
end
- render_error "Invalid form authenticity token."
+ render_error "Invalid form authenticity token. Perhaps your session has timed out; try reloading the form and entering your details again."
end
def render_feed(items, options={})
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/attachments_controller.rb
--- a/app/controllers/attachments_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/attachments_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -16,9 +16,11 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class AttachmentsController < ApplicationController
+
before_filter :find_project
before_filter :file_readable, :read_authorize, :except => :destroy
before_filter :delete_authorize, :only => :destroy
+ before_filter :active_authorize, :only => :toggle_active
verify :method => :post, :only => :destroy
@@ -54,6 +56,12 @@
redirect_to :controller => 'projects', :action => 'show', :id => @project
end
+ def toggle_active
+ @attachment.active = !@attachment.active?
+ @attachment.save!
+ render :layout => false
+ end
+
private
def find_project
@attachment = Attachment.find(params[:id])
@@ -77,6 +85,10 @@
@attachment.deletable? ? true : deny_access
end
+ def active_authorize
+ true
+ end
+
def detect_content_type(attachment)
content_type = attachment.content_type
if content_type.blank?
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/files_controller.rb
--- a/app/controllers/files_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/files_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -8,8 +8,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"
@@ -33,4 +34,5 @@
end
redirect_to project_files_path(@project)
end
+
end
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/issues_controller.rb
--- a/app/controllers/issues_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/issues_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -141,7 +141,16 @@
attachments = Attachment.attach_files(@issue, params[:attachments])
render_attachment_warning_if_needed(@issue)
flash[:notice] = l(:notice_successful_create)
+
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 {
redirect_to(params[:continue] ? { :action => 'new', :project_id => @project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
@@ -288,6 +297,18 @@
@notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)
@issue.init_journal(User.current, @notes)
@issue.safe_attributes = params[:issue]
+
+ # 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
+
+
end
# TODO: Refactor, lots of extra code in here
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/members_controller.rb
--- a/app/controllers/members_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/members_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -17,32 +17,55 @@
class MembersController < ApplicationController
model_object Member
- before_filter :find_model_object, :except => [:new, :autocomplete_for_member]
- before_filter :find_project_from_association, :except => [:new, :autocomplete_for_member]
+ menu_item :members
+ before_filter :find_model_object, :except => [:index, :new, :autocomplete_for_member]
+ before_filter :find_project_from_association, :except => [:new, :index, :autocomplete_for_member]
before_filter :find_project, :only => [:new, :autocomplete_for_member]
+ before_filter :find_project_by_project_id, :only => [:index]
before_filter :authorize
+ def index
+ logger.debug('in index')
+ respond_to do |format|
+ format.html {
+ render :layout => false if request.xhr?
+ }
+ end
+ end
+
def new
members = []
if params[:member] && request.post?
attrs = params[:member].dup
if (user_ids = attrs.delete(:user_ids))
user_ids.each do |user_id|
- members << Member.new(attrs.merge(:user_id => user_id))
+ @new_member = Member.new(attrs.merge(:user_id => user_id))
+ members << @new_member
+
+ # send notification to member
+ Mailer.deliver_added_to_project(@new_member, @project)
+
end
else
- members << Member.new(attrs)
+ @new_member = Member.new(attrs)
+ members << @new_member
+
+ # send notification to member
+ Mailer.deliver_added_to_project(@new_member, @project)
+
end
+
@project.members << members
+
end
respond_to do |format|
if members.present? && members.all? {|m| m.valid? }
- format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
+ format.html { redirect_to :action => 'index', :project_id => @project }
format.js {
render(:update) {|page|
- page.replace_html "tab-content-members", :partial => 'projects/settings/members'
+ page.replace_html "memberlist", :partial => 'editlist'
page << 'hideOnLoad()'
members.each {|member| page.visual_effect(:highlight, "member-#{member.id}") }
}
@@ -54,8 +77,8 @@
errors = members.collect {|m|
m.errors.full_messages
}.flatten.uniq
-
- page.alert(l(:notice_failed_to_save_members, :errors => errors.join(', ')))
+
+ # page.alert(l(:notice_failed_to_save_members, :errors => errors.join(', ')))
}
}
@@ -66,10 +89,10 @@
def edit
if request.post? and @member.update_attributes(params[:member])
respond_to do |format|
- format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
+ format.html { redirect_to :action => 'index', :project_id => @project }
format.js {
render(:update) {|page|
- page.replace_html "tab-content-members", :partial => 'projects/settings/members'
+ page.replace_html "memberlist", :partial => 'editlist'
page << 'hideOnLoad()'
page.visual_effect(:highlight, "member-#{@member.id}")
}
@@ -83,9 +106,9 @@
@member.destroy
end
respond_to do |format|
- format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project }
+ format.html { redirect_to :action => 'index', :project_id => @project }
format.js { render(:update) {|page|
- page.replace_html "tab-content-members", :partial => 'projects/settings/members'
+ page.replace_html "memberlist", :partial => 'editlist'
page << 'hideOnLoad()'
}
}
@@ -94,6 +117,7 @@
def autocomplete_for_member
@principals = Principal.active.like(params[:q]).find(:all, :limit => 100) - @project.principals
+ logger.debug "Query for #{params[:q]} returned #{@principals.size} results"
render :layout => false
end
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/my_controller.rb
--- a/app/controllers/my_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/my_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -25,14 +25,16 @@
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
}.merge(Redmine::Views::MyPage::Block.additional_blocks).freeze
- DEFAULT_LAYOUT = { 'left' => ['issuesassignedtome'],
- 'right' => ['issuesreportedbyme']
+ DEFAULT_LAYOUT = { 'left' => ['tipoftheday', 'activitymyprojects'],
+ 'right' => ['issueswatched']
}.freeze
verify :xhr => true,
@@ -53,10 +55,39 @@
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
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/projects_controller.rb
--- a/app/controllers/projects_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/projects_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -43,12 +43,25 @@
helper :repositories
include RepositoriesHelper
include ProjectsHelper
-
- # 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 {
- @projects = Project.visible.find(:all, :order => 'lft')
+ 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)
+ if User.current.logged?
+ # seems sort_by gives us case-sensitive ordering, which we don't want
+# @user_projects = User.current.projects.sort_by(&:name)
+ @user_projects = User.current.projects.all(:order => :name)
+ end
+ render :template => 'projects/index.rhtml', :layout => !request.xhr?
}
format.api {
@offset, @limit = api_offset_and_limit
@@ -205,6 +218,15 @@
end
verify :method => :post, :only => :modules, :render => {:nothing => true, :status => :method_not_allowed }
+
+ 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)
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/repositories_controller.rb
--- a/app/controllers/repositories_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/repositories_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -36,7 +36,11 @@
def edit
@repository = @project.repository
- if !@repository && !params[:repository_scm].blank?
+
+ if !@repository
+
+ params[:repository_scm]='Mercurial'
+
@repository = Repository.factory(params[:repository_scm])
@repository.project = @project if @repository
end
@@ -55,6 +59,7 @@
@repository.merge_extra_info(p_extra)
@repository.save
end
+
render(:update) do |page|
page.replace_html "tab-content-repository",
:partial => 'projects/settings/repository'
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/search_controller.rb
--- a/app/controllers/search_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/search_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -69,6 +69,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}
@@ -82,6 +83,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?
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/sys_controller.rb
--- a/app/controllers/sys_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/sys_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -55,6 +55,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 == "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 == "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 << "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
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/users_controller.rb
--- a/app/controllers/users_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/users_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -68,6 +68,20 @@
end
def show
+
+ if @user.ssamr_user_detail != nil
+ @description = @user.ssamr_user_detail.description
+
+ if @user.ssamr_user_detail.institution_type != nil
+ # institution_type is true for listed institutions
+ if (@user.ssamr_user_detail.institution_type)
+ @institution_name = Institution.find(@user.ssamr_user_detail.institution_id).name
+ else
+ @institution_name = @user.ssamr_user_detail.other_institution
+ end
+ end
+ end
+
# show projects based on current user visibility
@memberships = @user.memberships.all(:conditions => Project.visible_condition(User.current))
@@ -87,15 +101,18 @@
end
end
- def new
+ def new
@user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
@auth_sources = AuthSource.find(:all)
+
+ @ssamr_user_details = SsamrUserDetail.new
end
verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
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
@@ -104,9 +121,16 @@
@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
- @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
+
+ @ssamr_user_details.save!
+
Mailer.deliver_account_information(@user, params[:user][:password]) if params[:send_information]
@@ -133,6 +157,15 @@
end
def edit
+
+ @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
+
@auth_sources = AuthSource.find(:all)
@membership ||= Member.new
end
@@ -151,6 +184,26 @@
@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
+ @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
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
diff -r 0c939c159af4 -r 851510f1b535 app/controllers/welcome_controller.rb
--- a/app/controllers/welcome_controller.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/controllers/welcome_controller.rb Thu Jul 14 10:37:36 2011 +0100
@@ -18,9 +18,22 @@
class WelcomeController < ApplicationController
caches_action :robots
+ include ProjectsHelper
+ helper :projects
+
def index
- @news = News.latest User.current
+ @site_project = Project.find_by_identifier "soundsoftware-site"
+ @site_news = []
+ @site_news = News.latest_for @site_project if @site_project
@projects = Project.latest User.current
+
+ # 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
diff -r 0c939c159af4 -r 851510f1b535 app/helpers/application_helper.rb
--- a/app/helpers/application_helper.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/helpers/application_helper.rb Thu Jul 14 10:37:36 2011 +0100
@@ -52,7 +52,7 @@
if user.is_a?(User)
name = h(user.name(options[:format]))
if user.active?
- link_to name, :controller => 'users', :action => 'show', :id => user
+ link_to(name, :controller => 'users', :action => 'show', :id => user)
else
name
end
@@ -287,7 +287,7 @@
def principals_check_box_tags(name, principals)
s = ''
principals.sort.each do |principal|
- s << "\n"
+ s << "\n"
end
s
end
@@ -387,21 +387,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 << '…'
+ b << '…'
ancestors = ancestors[-2, 2]
end
b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
+ b = b.join(' » ')
+ b << (' »')
end
- b << h(@project)
- b.join(' » ')
+
+ pname << h(@project)
+
+ a = [pname, b]
+
end
end
@@ -906,6 +913,17 @@
''
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
diff -r 0c939c159af4 -r 851510f1b535 app/helpers/projects_helper.rb
--- a/app/helpers/projects_helper.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/helpers/projects_helper.rb Thu Jul 14 10:37:36 2011 +0100
@@ -23,8 +23,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},
@@ -49,6 +49,16 @@
content_tag('select', options, :name => 'project[parent_id]', :id => 'project_parent_id')
end
+ def render_project_short_description(project)
+ s = ''
+ if (project.short_description)
+ s << "
"
+ s << textilizable(project.short_description, :project => project).gsub(/<[^>]+>/, '')
+ s << "
"
+ end
+ s
+ 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)
@@ -73,7 +83,7 @@
classes = (ancestors.empty? ? 'root' : 'child')
s << "
" unless project.description.blank?
+ s << render_project_short_description(project)
s << "
\n"
ancestors << project
end
@@ -83,6 +93,163 @@
s
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 << "
" +
+ link_to_project(project, {}, :class => "project my-project")
+ if project.is_public?
+ s << " " << l("field_is_public") << ""
+ else
+ s << " " << l("field_is_private") << ""
+ end
+ s << render_project_short_description(project)
+ s << "
\n"
+
+ cs = ''
+ project.children.each do |child|
+ cs << render_my_project_in_hierarchy(child)
+ end
+
+ if cs != ''
+ s << "
\n" << cs << "
\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 << "
"
+ a << l("label_my_project_plural")
+ a << "
"
+ a << "
\n"
+ a << s
+ a << "
\n"
+ s = a
+ end
+
+ s
+
+ end
+
+ # 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(projects)
+
+ s = ""
+ s << "
"
+ s << "
"
+ s << "
"
+
+ s << sort_header_tag('name', :caption => l("field_name"))
+ s << "
" << l("label_managers") << "
"
+ s << sort_header_tag('created_on', :default_order => 'desc')
+ s << sort_header_tag('updated_on', :default_order => 'desc')
+
+ s << "
"
+
+ original_project = @project
+
+ projects.each do |project|
+ s << render_project_in_table(project, cycle('odd', 'even'), 0)
+ end
+
+ s << "
"
+
+ @project = original_project
+
+ s
+ 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 << "
"
+ s << render_project_short_description(project)
+
+ s << "
"
+
+ 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 << '' << mgrs.join(', ') << ''
+ else
+ s << mgrs.join(', ')
+ end
+ end
+ end
+ end
+
+ s << "
"
+ s << "
" << format_date(project.created_on) << "
"
+ s << "
" << format_date(project.updated_on) << "
"
+
+ s << "
"
+
+ 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] = []}
diff -r 0c939c159af4 -r 851510f1b535 app/models/institution.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/models/institution.rb Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,2 @@
+class Institution < ActiveRecord::Base
+end
diff -r 0c939c159af4 -r 851510f1b535 app/models/issue.rb
--- a/app/models/issue.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/models/issue.rb Thu Jul 14 10:37:36 2011 +0100
@@ -571,7 +571,8 @@
# Returns a string of css classes that apply to the issue
def css_classes
- s = "issue status-#{status.position} priority-#{priority.position}"
+ s = "issue status-#{status.position} "
+ s << "priority-#{priority.position}"
s << ' closed' if closed?
s << ' overdue' if overdue?
s << ' child' if child?
diff -r 0c939c159af4 -r 851510f1b535 app/models/mailer.rb
--- a/app/models/mailer.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/models/mailer.rb Thu Jul 14 10:37:36 2011 +0100
@@ -32,6 +32,27 @@
{ :host => h, :protocol => Setting.protocol }
end
+
+
+ # Builds a tmail object used to email the specified user that he was added to a project
+ #
+ # Example:
+ # add_to_project(user) => tmail object
+ # Mailer.deliver_add_to_project(user) => sends an email to the registered user
+ def added_to_project(member, project)
+
+ user = User.find(member.user_id)
+
+ set_language_if_valid user.language
+ recipients user.mail
+ subject l(:mail_subject_added_to_project, Setting.app_title)
+ body :project_url => url_for(:controller => 'projects', :action => 'show', :id => project.id),
+ :project_name => project.name
+ render_multipart('added_to_project', body)
+ end
+
+
+
# Builds a tmail object used to email recipients of the added issue.
#
# Example:
@@ -458,3 +479,7 @@
end
end
end
+
+
+
+
diff -r 0c939c159af4 -r 851510f1b535 app/models/news.rb
--- a/app/models/news.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/models/news.rb Thu Jul 14 10:37:36 2011 +0100
@@ -51,4 +51,9 @@
def add_author_as_watcher
Watcher.create(:watchable => self, :user => author)
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
diff -r 0c939c159af4 -r 851510f1b535 app/models/project.rb
--- a/app/models/project.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/models/project.rb Thu Jul 14 10:37:36 2011 +0100
@@ -85,6 +85,7 @@
named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"}
named_scope :all_public, { :conditions => { :is_public => true } }
named_scope :visible, lambda {|*args| {:conditions => Project.visible_condition(args.shift || User.current, *args) }}
+ named_scope :visible_roots, lambda { { :conditions => Project.root_visible_by(User.current) } }
def initialize(attributes = nil)
super
@@ -138,6 +139,10 @@
allowed_to_condition(user, :view_project, options)
end
+ def self.root_visible_by(user=nil)
+ return "#{Project.table_name}.parent_id IS NULL AND " + visible_by(user)
+ end
+
# Returns a SQL conditions string used to find all projects for which +user+ has the given +permission+
#
# Valid options:
@@ -468,7 +473,21 @@
# Returns a short description of the projects (first lines)
def short_description(length = 255)
- description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
+
+ ## 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}}\b).*$/m, '\1 ...').strip if description
end
def css_classes
diff -r 0c939c159af4 -r 851510f1b535 app/models/repository.rb
--- a/app/models/repository.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/models/repository.rb Thu Jul 14 10:37:36 2011 +0100
@@ -268,6 +268,10 @@
nil
end
+ def clear_cache
+ clear_changesets
+ end
+
def self.scm_adapter_class
nil
end
diff -r 0c939c159af4 -r 851510f1b535 app/models/repository/.svn/all-wcprops
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/models/repository/.svn/all-wcprops Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,47 @@
+K 25
+svn:wc:ra_dav:version-url
+V 46
+/svn/!svn/ver/4982/trunk/app/models/repository
+END
+subversion.rb
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/4962/trunk/app/models/repository/subversion.rb
+END
+bazaar.rb
+K 25
+svn:wc:ra_dav:version-url
+V 56
+/svn/!svn/ver/4982/trunk/app/models/repository/bazaar.rb
+END
+git.rb
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/4975/trunk/app/models/repository/git.rb
+END
+mercurial.rb
+K 25
+svn:wc:ra_dav:version-url
+V 59
+/svn/!svn/ver/4975/trunk/app/models/repository/mercurial.rb
+END
+filesystem.rb
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/4975/trunk/app/models/repository/filesystem.rb
+END
+cvs.rb
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/4982/trunk/app/models/repository/cvs.rb
+END
+darcs.rb
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/4982/trunk/app/models/repository/darcs.rb
+END
diff -r 0c939c159af4 -r 851510f1b535 app/models/repository/mercurial.rb
--- a/app/models/repository/mercurial.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/models/repository/mercurial.rb Thu Jul 14 10:37:36 2011 +0100
@@ -22,7 +22,7 @@
has_many :changesets, :order => "#{Changeset.table_name}.id DESC", :foreign_key => 'repository_id'
attr_protected :root_url
- validates_presence_of :url
+ # validates_presence_of :url
FETCH_AT_ONCE = 100 # number of changesets to fetch at once
diff -r 0c939c159af4 -r 851510f1b535 app/models/ssamr_user_detail.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/models/ssamr_user_detail.rb Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,14 @@
+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
+
+
+end
diff -r 0c939c159af4 -r 851510f1b535 app/models/user.rb
--- a/app/models/user.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/app/models/user.rb Thu Jul 14 10:37:36 2011 +0100
@@ -52,6 +52,9 @@
has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
belongs_to :auth_source
+ has_one :ssamr_user_detail, :dependent => :destroy, :class_name => 'SsamrUserDetail'
+ accepts_nested_attributes_for :ssamr_user_detail
+
# Active non-anonymous users scope
named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
@@ -63,6 +66,9 @@
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
+
+ # TODO: is this validation correct validates_presence_of :ssamr_user_detail
+
validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
# Login must contain lettres, numbers, underscores only
@@ -76,6 +82,8 @@
before_destroy :remove_references_before_destroy
+ validates_acceptance_of :terms_and_conditions, :on => :create, :message => :must_accept_terms_and_conditions
+
named_scope :in_group, lambda {|group|
group_id = group.is_a?(Group) ? group.id : group.to_i
{ :conditions => ["#{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] }
@@ -107,6 +115,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, '')
diff -r 0c939c159af4 -r 851510f1b535 app/views/account/register.rhtml
--- a/app/views/account/register.rhtml Thu Jul 14 10:32:19 2011 +0100
+++ b/app/views/account/register.rhtml Thu Jul 14 10:37:36 2011 +0100
@@ -1,3 +1,6 @@
+<%= javascript_include_tag "ssamr_institutions" %>
+<%= javascript_include_tag "ssamr_registration" %>
+
<%=l(:label_register)%> <%=link_to l(:label_login_with_open_id_option), signin_url if Setting.openid? %>
<% end %>
<%= call_hook(:view_welcome_index_right, :projects => @projects) %>
diff -r 0c939c159af4 -r 851510f1b535 config/initializers/.svn/all-wcprops
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/config/initializers/.svn/all-wcprops Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,41 @@
+K 25
+svn:wc:ra_dav:version-url
+V 44
+/svn/!svn/ver/4904/trunk/config/initializers
+END
+inflections.rb
+K 25
+svn:wc:ra_dav:version-url
+V 59
+/svn/!svn/ver/2895/trunk/config/initializers/inflections.rb
+END
+30-redmine.rb
+K 25
+svn:wc:ra_dav:version-url
+V 58
+/svn/!svn/ver/4679/trunk/config/initializers/30-redmine.rb
+END
+10-patches.rb
+K 25
+svn:wc:ra_dav:version-url
+V 58
+/svn/!svn/ver/4904/trunk/config/initializers/10-patches.rb
+END
+backtrace_silencers.rb
+K 25
+svn:wc:ra_dav:version-url
+V 67
+/svn/!svn/ver/2895/trunk/config/initializers/backtrace_silencers.rb
+END
+20-mime_types.rb
+K 25
+svn:wc:ra_dav:version-url
+V 61
+/svn/!svn/ver/1797/trunk/config/initializers/20-mime_types.rb
+END
+bigdecimal-segfault-fix.rb
+K 25
+svn:wc:ra_dav:version-url
+V 71
+/svn/!svn/ver/2895/trunk/config/initializers/bigdecimal-segfault-fix.rb
+END
diff -r 0c939c159af4 -r 851510f1b535 config/locales/bg.yml
--- a/config/locales/bg.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/bg.yml Thu Jul 14 10:37:36 2011 +0100
@@ -501,6 +501,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: Проекти, в които участвам
diff -r 0c939c159af4 -r 851510f1b535 config/locales/bs.yml
--- a/config/locales/bs.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/bs.yml Thu Jul 14 10:37:36 2011 +0100
@@ -441,6 +441,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/ca.yml
--- a/config/locales/ca.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/ca.yml Thu Jul 14 10:37:36 2011 +0100
@@ -477,6 +477,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/cs.yml
--- a/config/locales/cs.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/cs.yml Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/da.yml
--- a/config/locales/da.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/da.yml Thu Jul 14 10:37:36 2011 +0100
@@ -368,6 +368,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/de.yml
--- a/config/locales/de.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/de.yml Thu Jul 14 10:37:36 2011 +0100
@@ -492,6 +492,7 @@
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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/el.yml
--- a/config/locales/el.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/el.yml Thu Jul 14 10:37:36 2011 +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: Τα έργα μου
diff -r 0c939c159af4 -r 851510f1b535 config/locales/en-GB.yml
--- a/config/locales/en-GB.yml Thu Jul 14 10:32:19 2011 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,981 +0,0 @@
-en-GB:
- direction: ltr
- date:
- formats:
- # Use the strftime parameters for formats.
- # When no format has been given, it uses default.
- # You can provide other formats here if you like!
- default: "%d/%m/%Y"
- short: "%d %b"
- long: "%d %B, %Y"
-
- day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
- abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
-
- # Don't forget the nil at the beginning; there's no such thing as a 0th month
- month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
- abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
- # Used in date_select and datime_select.
- order:
- - :year
- - :month
- - :day
-
- time:
- formats:
- default: "%d/%m/%Y %I:%M %p"
- time: "%I:%M %p"
- short: "%d %b %H:%M"
- long: "%d %B, %Y %H:%M"
- am: "am"
- pm: "pm"
-
- datetime:
- distance_in_words:
- half_a_minute: "half a minute"
- less_than_x_seconds:
- one: "less than 1 second"
- other: "less than %{count} seconds"
- x_seconds:
- one: "1 second"
- other: "%{count} seconds"
- less_than_x_minutes:
- one: "less than a minute"
- other: "less than %{count} minutes"
- x_minutes:
- one: "1 minute"
- other: "%{count} minutes"
- about_x_hours:
- one: "about 1 hour"
- other: "about %{count} hours"
- x_days:
- one: "1 day"
- other: "%{count} days"
- about_x_months:
- one: "about 1 month"
- other: "about %{count} months"
- x_months:
- one: "1 month"
- other: "%{count} months"
- about_x_years:
- one: "about 1 year"
- other: "about %{count} years"
- over_x_years:
- one: "over 1 year"
- other: "over %{count} years"
- almost_x_years:
- one: "almost 1 year"
- other: "almost %{count} years"
-
- number:
- format:
- separator: "."
- delimiter: " "
- precision: 3
-
- currency:
- format:
- format: "%u%n"
- unit: "£"
-
- human:
- format:
- delimiter: ""
- precision: 1
- storage_units:
- format: "%n %u"
- units:
- byte:
- one: "Byte"
- other: "Bytes"
- kb: "kB"
- mb: "MB"
- gb: "GB"
- tb: "TB"
-
-
-# Used in array.to_sentence.
- support:
- array:
- sentence_connector: "and"
- skip_last_comma: false
-
- activerecord:
- errors:
- 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"
-
- 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. To activate your account, click on the link that was emailed to you.
- notice_account_unknown_email: Unknown user.
- notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
- notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
- notice_account_activated: Your account has been activated. You can now log in.
- notice_successful_create: Successful creation.
- notice_successful_update: Successful update.
- notice_successful_delete: Successful deletion.
- notice_successful_connection: Successful connection.
- notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
- notice_locking_conflict: Data has been updated by another user.
- notice_not_authorized: You are not authorised to access this page.
- notice_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 RSS access key was reset.
- notice_api_access_key_reseted: Your API access key was reset.
- notice_failed_to_save_issues: "Failed to save %{count} issue(s) on %{total} selected: %{ids}."
- notice_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_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}."
-
- gui_validation_error: 1 error
- gui_validation_error_plural: "%{count} errors"
-
- 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_repositories_encodings: Repositories encodings
- setting_emails_header: Emails header
- setting_emails_footer: Emails footer
- setting_protocol: Protocol
- setting_per_page_options: Objects per page options
- setting_user_format: Users display format
- setting_activity_days_default: Days displayed on project activity
- setting_display_subprojects_issues: Display subprojects issues on main projects by default
- setting_enabled_scm: Enabled SCM
- setting_mail_handler_body_delimiters: "Truncate emails after one of these lines"
- setting_mail_handler_api_enabled: Enable WS for incoming emails
- setting_mail_handler_api_key: API key
- setting_sequential_project_identifiers: Generate sequential project identifiers
- setting_gravatar_enabled: Use Gravatar user icons
- setting_gravatar_default: Default Gravatar image
- setting_diff_max_lines_displayed: Max number of diff lines displayed
- setting_file_max_size_displayed: Max size of text files displayed inline
- setting_repository_log_display_limit: Maximum number of revisions displayed on file log
- setting_openid: Allow OpenID login and registration
- setting_password_min_length: Minimum password length
- setting_new_project_user_role_id: Role given to a non-admin user who creates a project
- setting_default_projects_modules: Default enabled modules for new projects
- setting_issue_done_ratio: Calculate the issue done ratio with
- setting_issue_done_ratio_issue_field: Use the issue field
- setting_issue_done_ratio_issue_status: Use the issue status
- setting_start_of_week: Start calendars on
- setting_rest_api_enabled: Enable REST web service
- setting_cache_formatted_text: Cache formatted text
- 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
-
- permission_add_project: Create project
- permission_add_subprojects: Create subprojects
- permission_edit_project: Edit project
- permission_select_project_modules: Select project modules
- permission_manage_members: Manage members
- permission_manage_project_activities: Manage project activities
- permission_manage_versions: Manage versions
- permission_manage_categories: Manage issue categories
- permission_view_issues: View Issues
- permission_add_issues: Add issues
- permission_edit_issues: Edit issues
- permission_manage_issue_relations: Manage issue relations
- permission_add_issue_notes: Add notes
- permission_edit_issue_notes: Edit notes
- permission_edit_own_issue_notes: Edit own notes
- permission_move_issues: Move issues
- permission_delete_issues: Delete issues
- permission_manage_public_queries: Manage public queries
- permission_save_queries: Save queries
- permission_view_gantt: View gantt chart
- permission_view_calendar: View calendar
- permission_view_issue_watchers: View watchers list
- permission_add_issue_watchers: Add watchers
- permission_delete_issue_watchers: Delete watchers
- permission_log_time: Log spent time
- permission_view_time_entries: View spent time
- permission_edit_time_entries: Edit time logs
- permission_edit_own_time_entries: Edit own time logs
- permission_manage_news: Manage news
- permission_comment_news: Comment news
- permission_manage_documents: Manage documents
- permission_view_documents: View documents
- permission_manage_files: Manage 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_download: "%{count} Download"
- label_download_plural: "%{count} Downloads"
- label_no_data: No data to display
- label_change_status: Change status
- label_history: History
- label_attachment: File
- label_attachment_new: New file
- label_attachment_delete: Delete file
- label_attachment_plural: 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_modification: "%{count} change"
- label_modification_plural: "%{count} changes"
- label_branch: Branch
- label_tag: Tag
- label_revision: Revision
- label_revision_plural: Revisions
- label_revision_id: "Revision %{value}"
- label_associated_revisions: Associated revisions
- label_added: added
- label_modified: modified
- label_copied: copied
- label_renamed: renamed
- label_deleted: deleted
- label_latest_revision: Latest revision
- label_latest_revision_plural: Latest revisions
- label_view_revisions: View revisions
- label_view_all_revisions: View all revisions
- label_max_size: Maximum size
- label_sort_highest: Move to top
- label_sort_higher: Move up
- label_sort_lower: Move down
- label_sort_lowest: Move to bottom
- label_roadmap: Roadmap
- label_roadmap_due_in: "Due in %{value}"
- label_roadmap_overdue: "%{value} late"
- label_roadmap_no_issues: No issues for this version
- label_search: Search
- label_result_plural: Results
- label_all_words: All words
- label_wiki: Wiki
- label_wiki_edit: Wiki edit
- label_wiki_edit_plural: Wiki edits
- label_wiki_page: Wiki page
- label_wiki_page_plural: Wiki pages
- label_index_by_title: Index by title
- label_index_by_date: Index by date
- label_current_version: Current version
- label_preview: Preview
- label_feed_plural: Feeds
- label_changes_details: Details of all changes
- label_issue_tracking: Issue tracking
- label_spent_time: Spent time
- label_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: RSS access key
- label_missing_feeds_access_key: Missing a RSS access key
- label_feeds_access_key_created_on: "RSS access key created %{value} ago"
- label_module_plural: Modules
- label_added_time_by: "Added by %{author} %{age} ago"
- label_updated_time_by: "Updated by %{author} %{age} ago"
- label_updated_time: "Updated %{value} ago"
- label_jump_to_a_project: Jump to a project...
- label_file_plural: 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_are_you_sure_with_children: "Delete issue and all child issues?"
- 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 and dashes are allowed. 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_git_repository_note: Bare and local repository (e.g. /gitrepo, c:\gitrepo)
- 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.
diff -r 0c939c159af4 -r 851510f1b535 config/locales/en.yml
--- a/config/locales/en.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/en.yml Thu Jul 14 10:37:36 2011 +0100
@@ -1,14 +1,13 @@
en:
- # Text direction: Left-to-Right (ltr) or Right-to-Left (rtl)
direction: ltr
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
- default: "%m/%d/%Y"
- short: "%b %d"
- long: "%B %d, %Y"
+ default: "%d/%m/%Y"
+ short: "%d %b"
+ long: "%d %B, %Y"
day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
@@ -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"
@@ -70,10 +69,14 @@
number:
format:
- separator: "."
- delimiter: ""
+ separator: "."
+ delimiter: " "
precision: 3
+ currency:
+ format:
+ format: "%u%n"
+ unit: "£"
human:
format:
delimiter: ""
@@ -127,6 +130,7 @@
not_same_project: "doesn't belong to the same project"
circular_dependency: "This relation would create a circular dependency"
cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks"
+ must_accept_terms_and_conditions: "You must accept the Terms and Conditions"
actionview_instancetag_blank_option: Please select
@@ -139,7 +143,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
@@ -208,7 +212,13 @@
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
@@ -248,6 +258,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
@@ -316,6 +327,11 @@
field_cvsroot: CVSROOT
field_cvs_module: Module
+ 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 with the '
setting_app_title: Application title
setting_app_subtitle: Application subtitle
setting_welcome_text: Welcome text
@@ -370,6 +386,7 @@
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
@@ -408,8 +425,8 @@
permission_comment_news: Comment news
permission_manage_documents: Manage documents
permission_view_documents: View 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
@@ -432,17 +449,25 @@
permission_export_wiki_pages: Export wiki pages
permission_manage_subtasks: Manage subtasks
- 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_files: Files for download
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_embedded: Embedded documentation (Javadoc, Doxygen or MATLAB)
+ label_tipoftheday: Tip of the day
+ label_notifications: Important Message
+ field_terms_and_conditions: 'Terms and Conditions:'
+ accept_terms_and_conditions: 'I have read and agree with the '
+ label_ssamr_description: Research description
+ label_ssamr_details: Other Details
+ label_ssamr_institution: Institution
+ label_ssamr_description: Research description
label_user: User
label_user_plural: Users
label_user_new: New user
@@ -450,12 +475,16 @@
label_project: Project
label_project_new: New project
label_project_plural: Projects
+ label_my_project_plural: My Projects
+ label_other_project_plural: Other Projects
label_x_projects:
zero: no projects
one: 1 project
other: "%{count} projects"
label_project_all: All Projects
label_project_latest: Latest projects
+ label_projects_more: More projects
+ label_managers: Managed by
label_issue: Issue
label_issue_new: New issue
label_issue_plural: Issues
@@ -477,7 +506,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
@@ -501,6 +530,7 @@
label_login_with_open_id_option: or login with OpenID
label_password_lost: Lost password
label_home: Home
+ label_home_heading: Welcome!
label_my_page: My page
label_my_account: My account
label_my_projects: My projects
@@ -514,6 +544,9 @@
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_new: New
@@ -544,7 +577,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
@@ -552,6 +585,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
@@ -600,7 +635,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:
@@ -640,6 +675,7 @@
label_not_contains: doesn't contain
label_day_plural: days
label_repository: Repository
+ label_is_external_repository: Track an external repository
label_repository_plural: Repositories
label_browse: Browse
label_modification: "%{count} change"
@@ -669,7 +705,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
@@ -746,8 +783,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)
@@ -812,8 +850,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
@@ -867,6 +907,8 @@
button_quote: Quote
button_duplicate: Duplicate
button_show: Show
+ button_welcome_page_edit: Create or edit welcome page
+ button_welcome_page_edit_this: Edit this page
status_active: active
status_registered: registered
@@ -877,6 +919,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]+$
@@ -891,13 +934,19 @@
text_journal_set_to: "%{label} set to %{value}"
text_journal_deleted: "%{label} deleted (%{old})"
text_journal_added: "%{label} %{value} added"
- text_tip_issue_begin_day: issue beginning this day
- text_tip_issue_end_day: issue ending this day
- text_tip_issue_begin_end_day: issue beginning and ending this day
+ text_tip_issue_begin_day: task beginning this day
+ text_tip_issue_end_day: task ending this day
+ text_tip_issue_begin_end_day: task beginning and ending this day
text_project_identifier_info: 'Only lower case letters (a-z), numbers and dashes are allowed. Once saved, the identifier cannot be changed.'
+ text_project_homepage_info: '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 throughout this site. You can change your project's name at any time, in the project's settings."
+ text_project_visibility_info: "If your project is not public, it will only be visible to users that you have added as project members."
+ text_user_ssamr_description_info: 'Please describe your current research or development interests. This information will be used at registration to determine that you are a real person – so please be descriptive, or your application may be delayed or rejected. 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).
@@ -930,7 +979,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?"
@@ -941,6 +990,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: Click the star to switch active status for a download on or off. Active files will be shown more prominently in the download page.
+ text_settings_repo_creation: Creating repository...
The source code repository for a project will be set up automatically within a few minutes of the project being created.
Please check again in five minutes, and contact us if there is any problem.
If you wish to use this project to track a repository that is already hosted somewhere else, please wait until the repository has been created here and then return to this settings page to configure it.
If you don't want a repository at all, go to the Modules tab and switch it off there.
text_scm_path_encoding_note: "Default: UTF-8"
text_git_repository_note: "Bare and local repository (e.g. /gitrepo, c:\gitrepo)"
text_mercurial_repository_note: "Local repository (e.g. /hgrepo, c:\hgrepo)"
@@ -948,6 +999,13 @@
text_scm_command_version: Version
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.
+ text_settings_repo_explanation: External repositories
Normally your project's primary repository will be the Mercurial repository hosted at this site.
However, if you already have your project hosted somewhere else, you can specify your existing external repository's URL here – then this site will track that repository in a read-only “mirror” copy. External Mercurial, git and Subversion repositories can be tracked. Note that you cannot switch to an external repository if you have already made any commits to the repository hosted here.
+ text_settings_repo_is_internal: Currently the repository hosted at this site is the primary repository for this project.
+ text_settings_repo_is_external: Currently the repository hosted at this site is a read-only copy of an external repository.
+ text_settings_repo_need_help: Please contact us if you need help deciding how best to set this up. We can also import complete revision history from other systems into a new primary repository for you if you wish.
+ text_has_welcome_page_info: Welcome page
You can replace the standard {{overview_link}} page for this project with your own welcome page. This page will be editable using the project Wiki.
+ text_has_welcome_page_wiki_disabled: Note: You must enable the Wiki module in the {{modules_link}} tab before you can create or edit this page.
+
default_role_manager: Manager
default_role_developer: Developer
@@ -975,3 +1033,13 @@
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}}"
diff -r 0c939c159af4 -r 851510f1b535 config/locales/es.yml
--- a/config/locales/es.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/es.yml Thu Jul 14 10:37:36 2011 +0100
@@ -464,6 +464,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/eu.yml
--- a/config/locales/eu.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/eu.yml Thu Jul 14 10:37:36 2011 +0100
@@ -462,6 +462,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/fi.yml
--- a/config/locales/fi.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/fi.yml Thu Jul 14 10:37:36 2011 +0100
@@ -364,6 +364,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/fr.yml
--- a/config/locales/fr.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/fr.yml Thu Jul 14 10:37:36 2011 +0100
@@ -496,6 +496,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/gl.yml
--- a/config/locales/gl.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/gl.yml Thu Jul 14 10:37:36 2011 +0100
@@ -441,6 +441,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/he.yml
--- a/config/locales/he.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/he.yml Thu Jul 14 10:37:36 2011 +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: הפרויקטים שלי
diff -r 0c939c159af4 -r 851510f1b535 config/locales/hr.yml
--- a/config/locales/hr.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/hr.yml Thu Jul 14 10:37:36 2011 +0100
@@ -457,6 +457,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/hu.yml
--- a/config/locales/hu.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/hu.yml Thu Jul 14 10:37:36 2011 +0100
@@ -380,6 +380,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/id.yml
--- a/config/locales/id.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/id.yml Thu Jul 14 10:37:36 2011 +0100
@@ -448,6 +448,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/it.yml
--- a/config/locales/it.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/it.yml Thu Jul 14 10:37:36 2011 +0100
@@ -324,6 +324,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/ja.yml
--- a/config/locales/ja.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/ja.yml Thu Jul 14 10:37:36 2011 +0100
@@ -522,6 +522,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: マイプロジェクト
diff -r 0c939c159af4 -r 851510f1b535 config/locales/ko.yml
--- a/config/locales/ko.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/ko.yml Thu Jul 14 10:37:36 2011 +0100
@@ -487,6 +487,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: 내 프로젝트
diff -r 0c939c159af4 -r 851510f1b535 config/locales/lt.yml
--- a/config/locales/lt.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/lt.yml Thu Jul 14 10:37:36 2011 +0100
@@ -503,6 +503,7 @@
label_register: Užsiregistruoti
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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/lv.yml
--- a/config/locales/lv.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/lv.yml Thu Jul 14 10:37:36 2011 +0100
@@ -456,6 +456,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/mk.yml
--- a/config/locales/mk.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/mk.yml Thu Jul 14 10:37:36 2011 +0100
@@ -474,6 +474,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: Мои проекти
diff -r 0c939c159af4 -r 851510f1b535 config/locales/mn.yml
--- a/config/locales/mn.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/mn.yml Thu Jul 14 10:37:36 2011 +0100
@@ -460,6 +460,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: Миний төслүүд
diff -r 0c939c159af4 -r 851510f1b535 config/locales/nl.yml
--- a/config/locales/nl.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/nl.yml Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/no.yml
--- a/config/locales/no.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/no.yml Thu Jul 14 10:37:36 2011 +0100
@@ -351,6 +351,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/pl.yml
--- a/config/locales/pl.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/pl.yml Thu Jul 14 10:37:36 2011 +0100
@@ -438,6 +438,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ż
diff -r 0c939c159af4 -r 851510f1b535 config/locales/pt-BR.yml
--- a/config/locales/pt-BR.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/pt-BR.yml Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/pt.yml
--- a/config/locales/pt.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/pt.yml Thu Jul 14 10:37:36 2011 +0100
@@ -374,6 +374,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/ro.yml
--- a/config/locales/ro.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/ro.yml Thu Jul 14 10:37:36 2011 +0100
@@ -420,6 +420,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/ru.yml
--- a/config/locales/ru.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/ru.yml Thu Jul 14 10:37:36 2011 +0100
@@ -519,6 +519,7 @@
label_help: Помощь
label_history: История
label_home: Домашняя страница
+ label_home_heading: Домашняя страница
label_incoming_emails: Приём сообщений
label_index_by_date: История страниц
label_index_by_title: Оглавление
diff -r 0c939c159af4 -r 851510f1b535 config/locales/sk.yml
--- a/config/locales/sk.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/sk.yml Thu Jul 14 10:37:36 2011 +0100
@@ -354,6 +354,7 @@
label_register: Registrovať
label_password_lost: Zabudnuté heslo
label_home: Domovská stránka
+ label_home_heading: Domovská stránka
label_my_page: Moja stránka
label_my_account: Môj účet
label_my_projects: Moje projekty
diff -r 0c939c159af4 -r 851510f1b535 config/locales/sl.yml
--- a/config/locales/sl.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/sl.yml Thu Jul 14 10:37:36 2011 +0100
@@ -416,6 +416,7 @@
label_register: Registracija
label_password_lost: Izgubljeno geslo
label_home: Domov
+ label_home_heading: Domov
label_my_page: Moja stran
label_my_account: Moj račun
label_my_projects: Moji projekti
diff -r 0c939c159af4 -r 851510f1b535 config/locales/sr-YU.yml
--- a/config/locales/sr-YU.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/sr-YU.yml Thu Jul 14 10:37:36 2011 +0100
@@ -471,6 +471,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/sr.yml
--- a/config/locales/sr.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/sr.yml Thu Jul 14 10:37:36 2011 +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: Моји пројекти
diff -r 0c939c159af4 -r 851510f1b535 config/locales/sv.yml
--- a/config/locales/sv.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/sv.yml Thu Jul 14 10:37:36 2011 +0100
@@ -543,6 +543,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/th.yml
--- a/config/locales/th.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/th.yml Thu Jul 14 10:37:36 2011 +0100
@@ -356,6 +356,7 @@
label_register: ลงทะเบียน
label_password_lost: ลืมรหัสผ่าน
label_home: หน้าแรก
+ label_home_heading: หน้าแรก
label_my_page: หน้าของฉัน
label_my_account: บัญชีของฉัน
label_my_projects: โครงการของฉัน
diff -r 0c939c159af4 -r 851510f1b535 config/locales/tr.yml
--- a/config/locales/tr.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/tr.yml Thu Jul 14 10:37:36 2011 +0100
@@ -380,6 +380,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/uk.yml
--- a/config/locales/uk.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/uk.yml Thu Jul 14 10:37:36 2011 +0100
@@ -331,6 +331,7 @@
label_register: Зареєструватися
label_password_lost: Забули пароль
label_home: Домашня сторінка
+ label_home_heading: Домашня сторінка
label_my_page: Моя сторінка
label_my_account: Мій обліковий запис
label_my_projects: Мої проекти
diff -r 0c939c159af4 -r 851510f1b535 config/locales/vi.yml
--- a/config/locales/vi.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/vi.yml Thu Jul 14 10:37:36 2011 +0100
@@ -418,6 +418,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
diff -r 0c939c159af4 -r 851510f1b535 config/locales/zh-TW.yml
--- a/config/locales/zh-TW.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/zh-TW.yml Thu Jul 14 10:37:36 2011 +0100
@@ -583,6 +583,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: 我的專案
diff -r 0c939c159af4 -r 851510f1b535 config/locales/zh.yml
--- a/config/locales/zh.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/locales/zh.yml Thu Jul 14 10:37:36 2011 +0100
@@ -487,6 +487,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: 我的项目
diff -r 0c939c159af4 -r 851510f1b535 config/routes.rb
--- a/config/routes.rb Thu Jul 14 10:32:19 2011 +0100
+++ b/config/routes.rb Thu Jul 14 10:37:36 2011 +0100
@@ -151,6 +151,7 @@
:copy => [:get, :post],
:settings => :get,
:modules => :post,
+ :overview => :post,
:archive => :post,
:unarchive => :post
} do |project|
@@ -231,6 +232,7 @@
#left old routes at the bottom for backwards compat
map.connect 'projects/:project_id/queries/:action', :controller => 'queries'
map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
+ map.connect 'projects/:project_id/members/:action', :controller => 'members'
map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
@@ -250,6 +252,9 @@
map.with_options :controller => 'sys' do |sys|
sys.connect 'sys/projects.:format', :action => 'projects', :conditions => {:method => :get}
sys.connect 'sys/projects/:id/repository.:format', :action => 'create_project_repository', :conditions => {:method => :post}
+ sys.connect 'sys/projects/:id/external_repository.:format', :action => 'get_external_repo_url', :conditions => {:method => :get}
+ sys.connect 'sys/projects/:id/embedded.:format', :action => 'set_embedded_active', :conditions => { :method => :post }
+ sys.connect 'sys/projects/:id/repository_cache.:format', :action => 'clear_repository_cache', :conditions => {:method => :post}
end
# Install the default route as the lowest priority.
diff -r 0c939c159af4 -r 851510f1b535 config/settings.yml
--- a/config/settings.yml Thu Jul 14 10:32:19 2011 +0100
+++ b/config/settings.yml Thu Jul 14 10:37:36 2011 +0100
@@ -25,6 +25,10 @@
default: Project management
welcome_text:
default:
+tipoftheday_text:
+ default:
+notifications_text:
+ default:
login_required:
default: 0
self_registration:
diff -r 0c939c159af4 -r 851510f1b535 console/db/seeds.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/console/db/seeds.rb Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 db/migrate/20101123161346_create_ssamr_user_details.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20101123161346_create_ssamr_user_details.rb Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 db/migrate/20101216140621_create_institutions.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20101216140621_create_institutions.rb Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 db/migrate/20101216145813_fix_university_name_in_ssamr_details_table.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20101216145813_fix_university_name_in_ssamr_details_table.rb Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 db/migrate/20110126153504_add_other_institution_column_to_ssamr_user_details.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110126153504_add_other_institution_column_to_ssamr_user_details.rb Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 db/migrate/20110127161758_add_institution_type_column_to_ssamr_user_details.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110127161758_add_institution_type_column_to_ssamr_user_details.rb Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 db/migrate/20110202170156_add_order_column_to_institutions.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110202170156_add_order_column_to_institutions.rb Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 db/migrate/20110207142856_add_ext_rep_to_repositories.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110207142856_add_ext_rep_to_repositories.rb Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 db/migrate/20110303152903_add_active_column_to_attachments.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110303152903_add_active_column_to_attachments.rb Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,9 @@
+class AddActiveColumnToAttachments < ActiveRecord::Migration
+ def self.up
+ add_column :attachments, :active, :boolean
+ end
+
+ def self.down
+ remove_column :attachments, :active
+ end
+end
diff -r 0c939c159af4 -r 851510f1b535 db/migrate/20110331152140_add_has_welcome_page_to_projects.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110331152140_add_has_welcome_page_to_projects.rb Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 db/seed_data/institutions.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/db/seed_data/institutions.txt Thu Jul 14 10:37:36 2011 +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ŵ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
diff -r 0c939c159af4 -r 851510f1b535 db/seeds.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/db/seeds.rb Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 extra/mercurial/redminehelper.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/mercurial/redminehelper.py Thu Jul 14 10:37:36 2011 +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
+#
+# 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]'),
+}
diff -r 0c939c159af4 -r 851510f1b535 extra/soundsoftware/SoundSoftware-salted.pm
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/SoundSoftware-salted.pm Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,470 @@
+package Apache::Authn::SoundSoftware;
+
+=head1 Apache::Authn::SoundSoftware
+
+SoundSoftware - a mod_perl module for Apache authentication against a
+Redmine database and optional LDAP implementing the access control
+rules required for the SoundSoftware.ac.uk repository site.
+
+=head1 SYNOPSIS
+
+This module is closely based on the Redmine.pm authentication module
+provided with Redmine. It is intended to be used for authentication
+in front of a repository service such as hgwebdir.
+
+Requirements:
+
+1. Clone/pull from repo for public project: Any user, no
+authentication required
+
+2. Clone/pull from repo for private project: Project members only
+
+3. Push to repo for public project: "Permitted" users only (this
+probably means project members who are also identified in the hgrc web
+section for the repository and so will be approved by hgwebdir?)
+
+4. Push to repo for private project: "Permitted" users only (as above)
+
+5. Push to any repo that is tracking an external repo: Refused always
+
+=head1 INSTALLATION
+
+Debian/ubuntu:
+
+ apt-get install libapache-dbi-perl libapache2-mod-perl2 \
+ libdbd-mysql-perl libauthen-simple-ldap-perl libio-socket-ssl-perl
+
+Note that LDAP support is hardcoded "on" in this script (it is
+optional in the original Redmine.pm).
+
+=head1 CONFIGURATION
+
+ ## This module has to be in your perl path
+ ## eg: /usr/local/lib/site_perl/Apache/Authn/SoundSoftware.pm
+ PerlLoadModule Apache::Authn::SoundSoftware
+
+ # Example when using hgwebdir
+ ScriptAlias / "/var/hg/hgwebdir.cgi/"
+
+
+ AuthName "Mercurial"
+ AuthType Basic
+ Require valid-user
+ PerlAccessHandler Apache::Authn::SoundSoftware::access_handler
+ PerlAuthenHandler Apache::Authn::SoundSoftware::authen_handler
+ SoundSoftwareDSN "DBI:mysql:database=redmine;host=localhost"
+ SoundSoftwareDbUser "redmine"
+ SoundSoftwareDbPass "password"
+ Options +ExecCGI
+ AddHandler cgi-script .cgi
+ ## Optional where clause (fulltext search would be slow and
+ ## database dependant).
+ # SoundSoftwareDbWhereClause "and members.role_id IN (1,2)"
+ ## Optional prefix for local repository URLs
+ # SoundSoftwareRepoPrefix "/var/hg/"
+
+
+See the original Redmine.pm for further configuration notes.
+
+=cut
+
+use strict;
+use warnings FATAL => 'all', NONFATAL => 'redefine';
+
+use DBI;
+use Digest::SHA1;
+use Authen::Simple::LDAP;
+use Apache2::Module;
+use Apache2::Access;
+use Apache2::ServerRec qw();
+use Apache2::RequestRec qw();
+use Apache2::RequestUtil qw();
+use Apache2::Const qw(:common :override :cmd_how);
+use APR::Pool ();
+use APR::Table ();
+
+my @directives = (
+ {
+ name => 'SoundSoftwareDSN',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ errmsg => 'Dsn in format used by Perl DBI. eg: "DBI:Pg:dbname=databasename;host=my.db.server"',
+ },
+ {
+ name => 'SoundSoftwareDbUser',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ },
+ {
+ name => 'SoundSoftwareDbPass',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ },
+ {
+ name => 'SoundSoftwareDbWhereClause',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ },
+ {
+ name => 'SoundSoftwareRepoPrefix',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ },
+);
+
+sub SoundSoftwareDSN {
+ my ($self, $parms, $arg) = @_;
+ $self->{SoundSoftwareDSN} = $arg;
+ my $query = "SELECT
+ hashed_password, salt, auth_source_id, permissions
+ FROM members, projects, users, roles, member_roles
+ WHERE
+ projects.id=members.project_id
+ AND member_roles.member_id=members.id
+ AND users.id=members.user_id
+ AND roles.id=member_roles.role_id
+ AND users.status=1
+ AND login=?
+ AND identifier=? ";
+ $self->{SoundSoftwareQuery} = trim($query);
+}
+
+sub SoundSoftwareDbUser { set_val('SoundSoftwareDbUser', @_); }
+sub SoundSoftwareDbPass { set_val('SoundSoftwareDbPass', @_); }
+sub SoundSoftwareDbWhereClause {
+ my ($self, $parms, $arg) = @_;
+ $self->{SoundSoftwareQuery} = trim($self->{SoundSoftwareQuery}.($arg ? $arg : "")." ");
+}
+
+sub SoundSoftwareRepoPrefix {
+ my ($self, $parms, $arg) = @_;
+ if ($arg) {
+ $self->{SoundSoftwareRepoPrefix} = $arg;
+ }
+}
+
+sub trim {
+ my $string = shift;
+ $string =~ s/\s{2,}/ /g;
+ return $string;
+}
+
+sub set_val {
+ my ($key, $self, $parms, $arg) = @_;
+ $self->{$key} = $arg;
+}
+
+Apache2::Module::add(__PACKAGE__, \@directives);
+
+
+my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
+
+sub access_handler {
+ my $r = shift;
+
+ print STDERR "SoundSoftware.pm: In access handler at " . scalar localtime() . "\n";
+
+ unless ($r->some_auth_required) {
+ $r->log_reason("No authentication has been configured");
+ return FORBIDDEN;
+ }
+
+ my $method = $r->method;
+
+ print STDERR "SoundSoftware.pm: Method: $method, uri " . $r->uri . ", location " . $r->location . "\n";
+ print STDERR "SoundSoftware.pm: Accept: " . $r->headers_in->{Accept} . "\n";
+
+ my $dbh = connect_database($r);
+ unless ($dbh) {
+ print STDERR "SoundSoftware.pm: Database connection failed!: " . $DBI::errstr . "\n";
+ return FORBIDDEN;
+ }
+
+ print STDERR "Connected to db, dbh is " . $dbh . "\n";
+
+ my $project_id = get_project_identifier($dbh, $r);
+
+ if (!defined $read_only_methods{$method}) {
+ print STDERR "SoundSoftware.pm: Method is not read-only\n";
+ if (project_repo_is_readonly($dbh, $project_id, $r)) {
+ print STDERR "SoundSoftware.pm: Project repo is read-only, refusing access\n";
+ return FORBIDDEN;
+ } else {
+ print STDERR "SoundSoftware.pm: Project repo is read-write, authentication handler required\n";
+ return OK;
+ }
+ }
+
+ my $status = get_project_status($dbh, $project_id, $r);
+
+ $dbh->disconnect();
+ undef $dbh;
+
+ if ($status == 0) { # nonexistent
+ print STDERR "SoundSoftware.pm: Project does not exist, refusing access\n";
+ return FORBIDDEN;
+ } elsif ($status == 1) { # public
+ print STDERR "SoundSoftware.pm: Project is public, no restriction here\n";
+ $r->set_handlers(PerlAuthenHandler => [\&OK])
+ } else { # private
+ print STDERR "SoundSoftware.pm: Project is private, authentication handler required\n";
+ }
+
+ return OK
+}
+
+sub authen_handler {
+ my $r = shift;
+
+ print STDERR "SoundSoftware.pm: In authentication handler at " . scalar localtime() . "\n";
+
+ my $dbh = connect_database($r);
+ unless ($dbh) {
+ print STDERR "SoundSoftware.pm: Database connection failed!: " . $DBI::errstr . "\n";
+ return AUTH_REQUIRED;
+ }
+
+ my $project_id = get_project_identifier($dbh, $r);
+ my $realm = get_realm($dbh, $project_id, $r);
+ $r->auth_name($realm);
+
+ my ($res, $redmine_pass) = $r->get_basic_auth_pw();
+ unless ($res == OK) {
+ $dbh->disconnect();
+ undef $dbh;
+ return $res;
+ }
+
+ print STDERR "SoundSoftware.pm: User is " . $r->user . ", got password\n";
+
+ my $permitted = is_permitted($dbh, $project_id, $r->user, $redmine_pass, $r);
+
+ $dbh->disconnect();
+ undef $dbh;
+
+ if ($permitted) {
+ return OK;
+ } else {
+ print STDERR "SoundSoftware.pm: Not permitted\n";
+ $r->note_auth_failure();
+ return AUTH_REQUIRED;
+ }
+}
+
+sub get_project_status {
+ my $dbh = shift;
+ my $project_id = shift;
+ my $r = shift;
+
+ if (!defined $project_id or $project_id eq '') {
+ return 0; # nonexistent
+ }
+
+ my $sth = $dbh->prepare(
+ "SELECT is_public FROM projects WHERE projects.identifier = ?;"
+ );
+
+ $sth->execute($project_id);
+ my $ret = 0; # nonexistent
+ if (my @row = $sth->fetchrow_array) {
+ if ($row[0] eq "1" || $row[0] eq "t") {
+ $ret = 1; # public
+ } else {
+ $ret = 2; # private
+ }
+ }
+ $sth->finish();
+ undef $sth;
+
+ $ret;
+}
+
+sub project_repo_is_readonly {
+ my $dbh = shift;
+ my $project_id = shift;
+ my $r = shift;
+
+ if (!defined $project_id or $project_id eq '') {
+ return 0; # nonexistent
+ }
+
+ my $sth = $dbh->prepare(
+ "SELECT repositories.is_external FROM repositories, projects WHERE projects.identifier = ? AND repositories.project_id = projects.id;"
+ );
+
+ $sth->execute($project_id);
+ my $ret = 0; # nonexistent
+ if (my @row = $sth->fetchrow_array) {
+ if (defined($row[0]) && ($row[0] eq "1" || $row[0] eq "t")) {
+ $ret = 1; # read-only (i.e. external)
+ } else {
+ $ret = 0; # read-write
+ }
+ }
+ $sth->finish();
+ undef $sth;
+
+ $ret;
+}
+
+sub is_permitted {
+ my $dbh = shift;
+ my $project_id = shift;
+ my $redmine_user = shift;
+ my $redmine_pass = shift;
+ my $r = shift;
+
+ my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass);
+
+ my $cfg = Apache2::Module::get_config
+ (__PACKAGE__, $r->server, $r->per_dir_config);
+
+ my $query = $cfg->{SoundSoftwareQuery};
+ my $sth = $dbh->prepare($query);
+ $sth->execute($redmine_user, $project_id);
+
+ my $ret;
+ while (my ($hashed_password, $salt, $auth_source_id, $permissions) = $sth->fetchrow_array) {
+
+ # Test permissions for this user before we verify credentials
+ # -- if the user is not permitted this action anyway, there's
+ # not much point in e.g. contacting the LDAP
+
+ my $method = $r->method;
+
+ if ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/)
+ || $permissions =~ /:commit_access/) {
+
+ # User would be permitted this action, if their
+ # credentials checked out -- test those now
+
+ print STDERR "SoundSoftware.pm: User $redmine_user has required role, checking credentials\n";
+
+ unless ($auth_source_id) {
+ my $salted_password = Digest::SHA1::sha1_hex($salt.$pass_digest);
+ if ($hashed_password eq $salted_password) {
+ print STDERR "SoundSoftware.pm: User $redmine_user authenticated via password\n";
+ $ret = 1;
+ last;
+ }
+ } else {
+ my $sthldap = $dbh->prepare(
+ "SELECT host,port,tls,account,account_password,base_dn,attr_login FROM auth_sources WHERE id = ?;"
+ );
+ $sthldap->execute($auth_source_id);
+ while (my @rowldap = $sthldap->fetchrow_array) {
+ my $ldap = Authen::Simple::LDAP->new(
+ host => ($rowldap[2] eq "1" || $rowldap[2] eq "t") ? "ldaps://$rowldap[0]" : $rowldap[0],
+ port => $rowldap[1],
+ basedn => $rowldap[5],
+ binddn => $rowldap[3] ? $rowldap[3] : "",
+ bindpw => $rowldap[4] ? $rowldap[4] : "",
+ filter => "(".$rowldap[6]."=%s)"
+ );
+ if ($ldap->authenticate($redmine_user, $redmine_pass)) {
+ print STDERR "SoundSoftware.pm: User $redmine_user authenticated via LDAP\n";
+ $ret = 1;
+ }
+ }
+ $sthldap->finish();
+ undef $sthldap;
+ }
+ } else {
+ print STDERR "SoundSoftware.pm: User $redmine_user lacks required role for this project\n";
+ }
+ }
+
+ $sth->finish();
+ undef $sth;
+
+ $ret;
+}
+
+sub get_project_identifier {
+ my $dbh = shift;
+ my $r = shift;
+
+ my $location = $r->location;
+ my ($repo) = $r->uri =~ m{$location/*([^/]+)};
+
+ return $repo if (!$repo);
+
+ $repo =~ s/[^a-zA-Z0-9\._-]//g;
+
+ # The original Redmine.pm returns the string just calculated as
+ # the project identifier. That won't do for us -- we may have
+ # (and in fact already do have, in our test instance) projects
+ # whose repository names differ from the project identifiers.
+
+ # This is a rather fundamental change because it means that almost
+ # every request needs more than one database query -- which
+ # prompts us to start passing around $dbh instead of connecting
+ # locally within each function as is done in Redmine.pm.
+
+ my $sth = $dbh->prepare(
+ "SELECT projects.identifier FROM projects, repositories WHERE repositories.project_id = projects.id AND repositories.url LIKE ?;"
+ );
+
+ my $cfg = Apache2::Module::get_config
+ (__PACKAGE__, $r->server, $r->per_dir_config);
+
+ my $prefix = $cfg->{SoundSoftwareRepoPrefix};
+ if (!defined $prefix) { $prefix = '%/'; }
+
+ my $identifier = '';
+
+ $sth->execute($prefix . $repo);
+ my $ret = 0;
+ if (my @row = $sth->fetchrow_array) {
+ $identifier = $row[0];
+ }
+ $sth->finish();
+ undef $sth;
+
+ print STDERR "SoundSoftware.pm: Repository '$repo' belongs to project '$identifier'\n";
+
+ $identifier;
+}
+
+sub get_realm {
+ my $dbh = shift;
+ my $project_id = shift;
+ my $r = shift;
+
+ my $sth = $dbh->prepare(
+ "SELECT projects.name FROM projects WHERE projects.identifier = ?;"
+ );
+
+ my $name = $project_id;
+
+ $sth->execute($project_id);
+ my $ret = 0;
+ if (my @row = $sth->fetchrow_array) {
+ $name = $row[0];
+ }
+ $sth->finish();
+ undef $sth;
+
+ # be timid about characters not permitted in auth realm and revert
+ # to project identifier if any are found
+ if ($name =~ m/[^\w\d\s\._-]/) {
+ $name = $project_id;
+ }
+
+ my $realm = '"Mercurial repository for ' . "'$name'" . '"';
+
+ $realm;
+}
+
+sub connect_database {
+ my $r = shift;
+
+ my $cfg = Apache2::Module::get_config
+ (__PACKAGE__, $r->server, $r->per_dir_config);
+
+ return DBI->connect($cfg->{SoundSoftwareDSN},
+ $cfg->{SoundSoftwareDbUser},
+ $cfg->{SoundSoftwareDbPass});
+}
+
+1;
diff -r 0c939c159af4 -r 851510f1b535 extra/soundsoftware/SoundSoftware.pm
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/SoundSoftware.pm Thu Jul 14 10:37:36 2011 +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/"
+
+
+ AuthName "Mercurial"
+ AuthType Basic
+ Require valid-user
+ PerlAccessHandler Apache::Authn::SoundSoftware::access_handler
+ PerlAuthenHandler Apache::Authn::SoundSoftware::authen_handler
+ SoundSoftwareDSN "DBI:mysql:database=redmine;host=localhost"
+ SoundSoftwareDbUser "redmine"
+ SoundSoftwareDbPass "password"
+ Options +ExecCGI
+ AddHandler cgi-script .cgi
+ ## Optional where clause (fulltext search would be slow and
+ ## database dependant).
+ # SoundSoftwareDbWhereClause "and members.role_id IN (1,2)"
+ ## Optional prefix for local repository URLs
+ # SoundSoftwareRepoPrefix "/var/hg/"
+
+
+See the original Redmine.pm for further configuration notes.
+
+=cut
+
+use strict;
+use warnings FATAL => 'all', NONFATAL => 'redefine';
+
+use DBI;
+use Digest::SHA1;
+use Authen::Simple::LDAP;
+use Apache2::Module;
+use Apache2::Access;
+use Apache2::ServerRec qw();
+use Apache2::RequestRec qw();
+use Apache2::RequestUtil qw();
+use Apache2::Const qw(:common :override :cmd_how);
+use APR::Pool ();
+use APR::Table ();
+
+my @directives = (
+ {
+ name => 'SoundSoftwareDSN',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ errmsg => 'Dsn in format used by Perl DBI. eg: "DBI:Pg:dbname=databasename;host=my.db.server"',
+ },
+ {
+ name => 'SoundSoftwareDbUser',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ },
+ {
+ name => 'SoundSoftwareDbPass',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ },
+ {
+ name => 'SoundSoftwareDbWhereClause',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ },
+ {
+ name => 'SoundSoftwareRepoPrefix',
+ req_override => OR_AUTHCFG,
+ args_how => TAKE1,
+ },
+);
+
+sub SoundSoftwareDSN {
+ my ($self, $parms, $arg) = @_;
+ $self->{SoundSoftwareDSN} = $arg;
+ my $query = "SELECT
+ hashed_password, 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;
diff -r 0c939c159af4 -r 851510f1b535 extra/soundsoftware/convert-external-repos.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/convert-external-repos.rb Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,183 @@
+#!/usr/bin/env ruby
+
+# == Synopsis
+#
+# convert-external-repos: Update local Mercurial mirrors of external repos,
+# by running an external command for each project requiring an update.
+#
+# == Usage
+#
+# convert-external-repos [OPTIONS...] -s [DIR] -r [HOST]
+#
+# == Arguments (mandatory)
+#
+# -s, --scm-dir=DIR use DIR as base directory for repositories
+# -r, --redmine-host=HOST assume Redmine is hosted on HOST. Examples:
+# -r redmine.example.net
+# -r http://redmine.example.net
+# -r https://example.net/redmine
+# -k, --key=KEY use KEY as the Redmine API key
+# -c, --command=COMMAND use this command to update each external
+# repository: command is called with the name
+# of the project, the path to its repo, and
+# its external repo url as its three args
+#
+# == Options
+#
+# --http-user=USER User for HTTP Basic authentication with Redmine WS
+# --http-pass=PASSWORD Password for Basic authentication with Redmine WS
+# -t, --test only show what should be done
+# -h, --help show help and exit
+# -v, --verbose verbose
+# -V, --version print version and exit
+# -q, --quiet no log
+
+
+require 'getoptlong'
+require 'rdoc/usage'
+require 'find'
+require 'etc'
+
+Version = "1.0"
+
+opts = GetoptLong.new(
+ ['--scm-dir', '-s', GetoptLong::REQUIRED_ARGUMENT],
+ ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
+ ['--key', '-k', GetoptLong::REQUIRED_ARGUMENT],
+ ['--http-user', GetoptLong::REQUIRED_ARGUMENT],
+ ['--http-pass', GetoptLong::REQUIRED_ARGUMENT],
+ ['--command' , '-c', GetoptLong::REQUIRED_ARGUMENT],
+ ['--test', '-t', GetoptLong::NO_ARGUMENT],
+ ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
+ ['--version', '-V', GetoptLong::NO_ARGUMENT],
+ ['--help' , '-h', GetoptLong::NO_ARGUMENT],
+ ['--quiet' , '-q', GetoptLong::NO_ARGUMENT]
+ )
+
+$verbose = 0
+$quiet = false
+$redmine_host = ''
+$repos_base = ''
+$http_user = ''
+$http_pass = ''
+$test = false
+
+$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'; RDoc::usage
+ when '--quiet'; $quiet = true
+ end
+ end
+rescue
+ exit 1
+end
+
+if $test
+ log("running in test mode")
+end
+
+if ($redmine_host.empty? or $repos_base.empty? or $command.empty?)
+ RDoc::usage
+end
+
+unless File.directory?($repos_base)
+ log("directory '#{$repos_base}' doesn't exist", :exit => true)
+end
+
+begin
+ require 'active_resource'
+rescue LoadError
+ log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true)
+end
+
+class Project < ActiveResource::Base
+ self.headers["User-agent"] = "SoundSoftware external repository converter/#{Version}"
+end
+
+log("querying Redmine for projects...", :level => 1);
+
+$redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
+$redmine_host.gsub!(/\/$/, '')
+
+Project.site = "#{$redmine_host}/sys";
+Project.user = $http_user;
+Project.password = $http_pass;
+
+begin
+ # Get all active projects that have the Repository module enabled
+ projects = Project.find(:all, :params => {:key => $api_key})
+rescue => e
+ log("Unable to connect to #{Project.site}: #{e}", :exit => true)
+end
+
+if projects.nil?
+ log('no project found, perhaps you forgot to "Enable WS for repository management"', :exit => true)
+end
+
+log("retrieved #{projects.size} projects", :level => 1)
+
+projects.each do |project|
+ log("treating project #{project.name}", :level => 1)
+
+ if project.identifier.empty?
+ log("\tno identifier for project #{project.name}")
+ next
+ elsif not project.identifier.match(/^[a-z0-9\-]+$/)
+ log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
+ next
+ end
+
+ if !project.respond_to?(:repository) or !project.repository.is_external?
+ log("\tproject #{project.identifier} does not use an external repository");
+ next
+ end
+
+ external_url = project.repository.external_url;
+ log("\tproject #{project.identifier} has external repository url #{external_url}");
+
+ if !external_url.match(/^[a-z][a-z+]{0,8}[a-z]:\/\//)
+ log("\tthis doesn't look like a plausible url to me, skipping")
+ next
+ end
+
+ repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
+
+ unless File.directory?(repos_path)
+ log("\tproject repo directory '#{repos_path}' doesn't exist")
+ next
+ end
+
+ system($command, project.identifier, repos_path, external_url)
+
+ $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
+
diff -r 0c939c159af4 -r 851510f1b535 extra/soundsoftware/doxysafe.pl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/doxysafe.pl Thu Jul 14 10:37:36 2011 +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";
diff -r 0c939c159af4 -r 851510f1b535 extra/soundsoftware/extract-docs.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/extract-docs.sh Thu Jul 14 10:37:36 2011 +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"
diff -r 0c939c159af4 -r 851510f1b535 extra/soundsoftware/extract-doxygen.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/extract-doxygen.sh Thu Jul 14 10:37:36 2011 +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 "
+ 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 -
+
diff -r 0c939c159af4 -r 851510f1b535 extra/soundsoftware/extract-javadoc.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/extract-javadoc.sh Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+docdir="/var/doc"
+
+project="$1"
+projectdir="$2"
+targetdir="$3"
+
+if [ -z "$project" ] || [ -z "$targetdir" ] || [ -z "$projectdir" ]; then
+ echo "Usage: $0 "
+ 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 grep '^ *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
+ if [ -n "$current_packages" ]; then
+ echo "Running Javadoc for packages $current_packages from prefix $current_prefix"
+ javadoc -sourcepath "$current_prefix" -d "$targetdir" -subpackages $current_packages
+ fi
+ current_prefix="$prefix"
+ current_packages=
+ else
+ 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"
+ javadoc -sourcepath "$current_prefix" -d "$targetdir" -subpackages $current_packages
+ fi
+ )
+
+# for exit code:
+[ -f "$targetdir/index.html" ]
+
diff -r 0c939c159af4 -r 851510f1b535 extra/soundsoftware/extract-matlabdocs.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/extract-matlabdocs.sh Thu Jul 14 10:37:36 2011 +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 "
+ 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"
+
diff -r 0c939c159af4 -r 851510f1b535 extra/soundsoftware/matlab-docs-credit.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/matlab-docs-credit.html Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,1 @@
+
Produced by mtree2html by Hartmut Pohlheim
diff -r 0c939c159af4 -r 851510f1b535 extra/soundsoftware/matlab-docs.conf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/matlab-docs.conf Thu Jul 14 10:37:36 2011 +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 =
+
+#------------------------------------------------------------------------
+# 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 = Index of 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 , do what you want
+#------------------------------------------------------------------------
+set codehr =
+
+#------------------------------------------------------------------------
+# codeheader: HTML-code added to tags, use for centering header text
+# or changing the colour/size/font of the header text
+#------------------------------------------------------------------------
+set codeheader =
+
+
+# End of parameter file
diff -r 0c939c159af4 -r 851510f1b535 extra/soundsoftware/matlab-docs.pl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/matlab-docs.pl Thu Jul 14 10:37:36 2011 +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', '',
+ 'codehr', '',
+ '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 = '';
+$TextDocTypeFrame = '';
+$TextMetaCharset = '';
+
+#------------------------------------------------------------------------
+# 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 () {
+ 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\n\n$var{'codeheadmeta'}\n$TextMetaCharset\n";
+ print IFILE " $var{'texttitleframelayout'}\n";
+ print IFILE "\n";
+
+ # definition of 2 frames, left the tree of directories,
+ # right the index of that directory or the docu of a file
+ print IFILE "\n";
+
+ print IFILE "\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\n\n$var{'codeheadmeta'}\n$TextMetaCharset\n$var{'csslink'}\n";
+
+ if ($var{'texttitleindexalldirs'} eq '') {
+ print IFILE "Index of Directories of $var{'dirmfiles'}\n";
+ } else {
+ print IFILE "$var{'texttitleindexalldirs'}\n";
+ }
+
+ if ($var{'frames'} eq 'yes') {
+ print IFILE "\n";
+ }
+
+ print IFILE "\n";
+ print IFILE "\n";
+ print IFILE "
\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 "
\n $var{'codehr'}\n";
+ } else { die "wrong parameter for LocalShortLong in ConstructAZIndexFile: $LocalShortLong."; }
+
+ # Include info about author from authorfile
+ &WriteFile2Handle($var{'authorfile'}, IFILE);
+
+ print IFILE "\n";
+ print IFILE "\n";
+ print IFILE "
\n\n\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\n\n$var{'codeheadmeta'}\n$TextMetaCharset\n$var{'csslink'}\n";
+
+ if ($var{'texttitleindex'} eq '') {
+ print IFILE "A-Z jump index in directory $dirToPrint\n";
+ } else {
+ if ($LocalGlobalLocal eq 'global') { print IFILE "$var{'texttitleindex'}\n"; }
+ else { print IFILE "$var{'texttitleindex'} in Directory $dirToPrint\n"; }
+ }
+
+ if ($var{'frames'} eq 'yes') {
+ print IFILE "\n";
+ }
+ print IFILE "\n";
+ print IFILE "\n";
+ print IFILE "
\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 "\U$_\E\n";
+ } else {
+ print IFILE "\U$_\E \n";
+ }
+ }
+
+ print IFILE "
\n\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\n\n$var{'codeheadmeta'}\n$TextMetaCharset\n$var{'csslink'}\n";
+
+ if ($var{'texttitleindex'} eq '') {
+ print IFILE "Index of Matlab Files in Directory $dirToPrint\n";
+ } else {
+ if ($LocalGlobalLocal eq 'global') { print IFILE "$var{'texttitleindex'}\n"; }
+ else { print IFILE "$var{'texttitleindex'} in Directory $dirToPrint\n"; }
+ }
+ print IFILE "\n";
+
+ # definition of 2 frames, top the A-Z index, below the jump letter line
+ print IFILE "\n";
+
+ print IFILE "\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
\n$var{'codehr'}\n";
+ }
+
+ # Look for the matlab help text block
+ $functionline = "\n";
+ do {
+ $_ = ;
+ # remember functionline, if before help text block
+ if (/^\s*function/) { $functionline = $_; }
+ } until (/^\s*%/ || eof);
+ if (! (eof(MFILE))) {
+ print HFILE "
Help text
\n";
+ print HFILE "
\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) { $_ = ; }
+ }
+ print HFILE "
\n$var{'codehr'}\n";
+ }
+
+ # Write the cross reference information
+ if (@xref || @yref) {
+ print HFILE "
+<%= text_field_tag 'settings[path]', @settings['path'], :size => 80 %>
+ Use {PROJECT} to include the project identifier in the path
+
+
+<%= text_area_tag 'settings[index]', @settings['index'], :cols => 60, :rows => 3 %>
+ Space separated list of index files by priority
+
+
+<%= text_area_tag 'settings[extensions]', @settings['extensions'], :cols => 60, :rows => 3 %>
+ Space separated list of file extensions that can be viewed (case insensitive)
Generated on Tue Jun 24 21:43:53 +0200 2008 with rcov 0.8.1.2
+
+
+
Code reported as executed by Ruby looks like this...
+and this: this line is also marked as covered.
+Lines considered as run by rcov, but not reported by Ruby, look like this,
+and this: these lines were inferred by rcov (using simple heuristics).
+Finally, here's a line marked as not executed.
+
1 # redMine - project management software
+ 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
+ 3 #
+ 4 # This program is free software; you can redistribute it and/or
+ 5 # modify it under the terms of the GNU General Public License
+ 6 # as published by the Free Software Foundation; either version 2
+ 7 # of the License, or (at your option) any later version.
+ 8 #
+ 9 # This program is distributed in the hope that it will be useful,
+ 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 12 # GNU General Public License for more details.
+ 13 #
+ 14 # You should have received a copy of the GNU General Public License
+ 15 # along with this program; if not, write to the Free Software
+ 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ 17
+ 18 class AccountController < ApplicationController
+ 19 layout 'base'
+ 20 helper :custom_fields
+ 21 include CustomFieldsHelper
+ 22
+ 23 # prevents login action to be filtered by check_if_login_required application scope filter
+ 24 skip_before_filter :check_if_login_required, :only => [:login, :lost_password, :register, :activate]
+ 25
+ 26 # Show user's account
+ 27 def show
+ 28 @user = User.find_active(params[:id])
+ 29 @custom_values = @user.custom_values.find(:all, :include => :custom_field)
+ 30
+ 31 # show only public projects and private projects that the logged in user is also a member of
+ 32 @memberships = @user.memberships.select do |membership|
+ 33 membership.project.is_public? || (User.current.member_of?(membership.project))
+ 34 end
+ 35 rescue ActiveRecord::RecordNotFound
+ 36 render_404
+ 37 end
+ 38
+ 39 # Login request and validation
+ 40 def login
+ 41 if request.get?
+ 42 # Logout user
+ 43 self.logged_user = nil
+ 44 else
+ 45 # Authenticate user
+ 46 user = User.try_to_login(params[:username], params[:password])
+ 47 if user
+ 48 self.logged_user = user
+ 49 # generate a key and set cookie if autologin
+ 50 if params[:autologin] && Setting.autologin?
+ 51 token = Token.create(:user => user, :action => 'autologin')
+ 52 cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
+ 53 end
+ 54 redirect_back_or_default :controller => 'my', :action => 'page'
+ 55 else
+ 56 flash.now[:error] = l(:notice_account_invalid_creditentials)
+ 57 end
+ 58 end
+ 59 rescue User::OnTheFlyCreationFailure
+ 60 flash.now[:error] = 'Redmine could not retrieve the required information from the LDAP to create your account. Please, contact your Redmine administrator.'
+ 61 end
+ 62
+ 63 # Log out current user and redirect to welcome page
+ 64 def logout
+ 65 cookies.delete :autologin
+ 66 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) if User.current.logged?
+ 67 self.logged_user = nil
+ 68 redirect_to home_url
+ 69 end
+ 70
+ 71 # Enable user to choose a new password
+ 72 def lost_password
+ 73 redirect_to(home_url) && return unless Setting.lost_password?
+ 74 if params[:token]
+ 75 @token = Token.find_by_action_and_value("recovery", params[:token])
+ 76 redirect_to(home_url) && return unless @token and !@token.expired?
+ 77 @user = @token.user
+ 78 if request.post?
+ 79 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
+ 80 if @user.save
+ 81 @token.destroy
+ 82 flash[:notice] = l(:notice_account_password_updated)
+ 83 redirect_to :action => 'login'
+ 84 return
+ 85 end
+ 86 end
+ 87 render :template => "account/password_recovery"
+ 88 return
+ 89 else
+ 90 if request.post?
+ 91 user = User.find_by_mail(params[:mail])
+ 92 # user not found in db
+ 93 flash.now[:error] = l(:notice_account_unknown_email) and return unless user
+ 94 # user uses an external authentification
+ 95 flash.now[:error] = l(:notice_can_t_change_password) and return if user.auth_source_id
+ 96 # create a new token for password recovery
+ 97 token = Token.new(:user => user, :action => "recovery")
+ 98 if token.save
+ 99 Mailer.deliver_lost_password(token)
+100 flash[:notice] = l(:notice_account_lost_email_sent)
+101 redirect_to :action => 'login'
+102 return
+103 end
+104 end
+105 end
+106 end
+107
+108 # User self-registration
+109 def register
+110 redirect_to(home_url) && return unless Setting.self_registration?
+111 if request.get?
+112 @user = User.new(:language => Setting.default_language)
+113 else
+114 @user = User.new(params[:user])
+115 @user.admin = false
+116 @user.login = params[:user][:login]
+117 @user.status = User::STATUS_REGISTERED
+118 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
+119 case Setting.self_registration
+120 when '1'
+121 # Email activation
+122 token = Token.new(:user => @user, :action => "register")
+123 if @user.save and token.save
+124 Mailer.deliver_register(token)
+125 flash[:notice] = l(:notice_account_register_done)
+126 redirect_to :action => 'login'
+127 end
+128 when '3'
+129 # Automatic activation
+130 @user.status = User::STATUS_ACTIVE
+131 if @user.save
+132 self.logged_user = @user
+133 flash[:notice] = l(:notice_account_activated)
+134 redirect_to :controller => 'my', :action => 'account'
+135 end
+136 else
+137 # Manual activation by the administrator
+138 if @user.save
+139 # Sends an email to the administrators
+140 Mailer.deliver_account_activation_request(@user)
+141 flash[:notice] = l(:notice_account_pending)
+142 redirect_to :action => 'login'
+143 end
+144 end
+145 end
+146 end
+147
+148 # Token based account activation
+149 def activate
+150 redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
+151 token = Token.find_by_action_and_value('register', params[:token])
+152 redirect_to(home_url) && return unless token and !token.expired?
+153 user = token.user
+154 redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
+155 user.status = User::STATUS_ACTIVE
+156 if user.save
+157 token.destroy
+158 flash[:notice] = l(:notice_account_activated)
+159 end
+160 redirect_to :action => 'login'
+161 end
+162
+163 private
+164 def logged_user=(user)
+165 if user && user.is_a?(User)
+166 User.current = user
+167 session[:user_id] = user.id
+168 else
+169 User.current = User.anonymous
+170 session[:user_id] = nil
+171 end
+172 end
+173 end
+
+
+
diff -r 0c939c159af4 -r 851510f1b535 vendor/plugins/embedded/test/fixtures/html/misc/misc.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/embedded/test/fixtures/html/misc/misc.html Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,1 @@
+Misc file
diff -r 0c939c159af4 -r 851510f1b535 vendor/plugins/embedded/test/fixtures/html/misc/misc.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/embedded/test/fixtures/html/misc/misc.txt Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,1 @@
+Misc file
diff -r 0c939c159af4 -r 851510f1b535 vendor/plugins/embedded/test/functional/embedded_controller_test.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/embedded/test/functional/embedded_controller_test.rb Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,63 @@
+# 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 File.dirname(__FILE__) + '/../test_helper'
+
+class EmbeddedControllerTest < ActionController::TestCase
+ fixtures :projects, :enabled_modules, :users, :roles, :members
+
+ def setup
+ fixtures_path = File.dirname(__FILE__) + '/../fixtures/html'
+
+ Setting.plugin_embedded = { 'path' => fixtures_path,
+ 'index' => 'main.html overview-summary.html index.html',
+ 'extensions' => 'html png gif',
+ 'template' => '',
+ 'encoding' => '',
+ 'menu' => 'Embedded' }
+
+ Project.find(1).enabled_modules << EnabledModule.new(:name => 'embedded')
+
+ anonymous = Role.anonymous
+ anonymous.permissions += [:view_embedded_doc]
+ assert anonymous.save
+ end
+
+ def test_get_root_should_redirect_to_index_file
+ get :index, :id => 'ecookbook'
+ assert_redirected_to :path => ['index.html']
+ end
+
+ def test_get_index_file
+ get :index, :id => 'ecookbook', :path => ['index.html']
+ assert_response :success
+ assert_template 'index'
+ assert_tag :h3, :content => 'C0 code coverage information'
+ end
+
+ def test_get_subdirectory_file
+ get :index, :id => 'ecookbook', :path => ['misc', 'misc.html']
+ assert_response :success
+ assert_template 'index'
+ assert_tag :b, :content => 'Misc file'
+ end
+
+ def test_get_invalid_extension_should_be_denied
+ get :index, :id => 'ecookbook', :path => ['misc', 'misc.txt']
+ assert_response 500
+ end
+end
diff -r 0c939c159af4 -r 851510f1b535 vendor/plugins/embedded/test/test_helper.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/embedded/test/test_helper.rb Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,38 @@
+ENV["RAILS_ENV"] ||= "test"
+require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
+require 'test_help'
+
+class Test::Unit::TestCase
+ # Transactional fixtures accelerate your tests by wrapping each test method
+ # in a transaction that's rolled back on completion. This ensures that the
+ # test database remains unchanged so your fixtures don't have to be reloaded
+ # between every test method. Fewer database queries means faster tests.
+ #
+ # Read Mike Clark's excellent walkthrough at
+ # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
+ #
+ # Every Active Record database supports transactions except MyISAM tables
+ # in MySQL. Turn off transactional fixtures in this case; however, if you
+ # don't care one way or the other, switching from MyISAM to InnoDB tables
+ # is recommended.
+ #
+ # The only drawback to using transactional fixtures is when you actually
+ # need to test transactions. Since your test is bracketed by a transaction,
+ # any transactions started in your code will be automatically rolled back.
+ self.use_transactional_fixtures = true
+
+ # Instantiated fixtures are slow, but give you @david where otherwise you
+ # would need people(:david). If you don't want to migrate your existing
+ # test cases which use the @david style and don't mind the speed hit (each
+ # instantiated fixtures translates to a database query per test method),
+ # then set this back to true.
+ self.use_instantiated_fixtures = false
+
+ # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
+ #
+ # Note: You'll currently still have to declare fixtures explicitly in integration tests
+ # -- they do not yet inherit this setting
+ fixtures :all
+
+ # Add more helper methods to be used by all tests here...
+end
diff -r 0c939c159af4 -r 851510f1b535 vendor/plugins/embedded/test/unit/embedded_test.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/embedded/test/unit/embedded_test.rb Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,54 @@
+# 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 File.dirname(__FILE__) + '/../test_helper'
+
+class EmbeddedTest < ActiveSupport::TestCase
+
+ def setup
+ Setting.plugin_embedded = { 'path' => '/path/to',
+ 'index' => 'main.html overview-summary.html index.html',
+ 'extensions' => 'html png gif',
+ 'template' => 'doxygen',
+ 'encoding' => '',
+ 'menu' => 'Embedded' }
+ end
+
+ def test_available_templates
+ assert_equal ['doxygen', 'javadoc', 'rcov'], Redmine::Plugins::Embedded.available_templates
+ end
+
+ def test_assets
+ assert_equal ['rcov.css', 'rcov.js'], Redmine::Plugins::Embedded.assets('rcov')
+ end
+
+ def test_detect_template_from_path
+ to_test = { '/path/to/doc' => 'doxygen',
+ '/path/to/javadoc/html' => 'javadoc' }
+
+ to_test.each { |path, template| assert_equal template, Redmine::Plugins::Embedded.detect_template_from_path(path) }
+ end
+
+ def test_valid_extension
+ to_test = {'index.html' => true,
+ 'path/to/index.html' => true,
+ 'path/to/image.png' => true,
+ 'path/to/something.else' => false}
+
+ to_test.each { |path, expected| assert_equal expected, Redmine::Plugins::Embedded.valid_extension?(path) }
+ end
+end
diff -r 0c939c159af4 -r 851510f1b535 vendor/plugins/engines/tasks/.svn/entries
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/engines/tasks/.svn/entries Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,62 @@
+10
+
+dir
+4993
+http://redmine.rubyforge.org/svn/trunk/vendor/plugins/engines/tasks
+http://redmine.rubyforge.org/svn
+
+
+
+2009-12-18T14:41:37.649419Z
+3186
+jplang
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+e93f8b46-1217-0410-a6f0-8f06a7374b81
+
+engines.rake
+file
+
+
+
+
+2011-03-03T11:05:12.000000Z
+10b9e5aacd9d0673e694f6ababd6ee3b
+2009-12-18T14:41:37.649419Z
+3186
+jplang
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10953
+
diff -r 0c939c159af4 -r 851510f1b535 vendor/plugins/open_id_authentication/tasks/.svn/entries
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/open_id_authentication/tasks/.svn/entries Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,62 @@
+10
+
+dir
+4993
+http://redmine.rubyforge.org/svn/trunk/vendor/plugins/open_id_authentication/tasks
+http://redmine.rubyforge.org/svn
+
+
+
+2009-02-11T19:06:45.087605Z
+2438
+edavis10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+e93f8b46-1217-0410-a6f0-8f06a7374b81
+
+open_id_authentication_tasks.rake
+file
+
+
+
+
+2011-03-03T11:05:12.000000Z
+54b595ee92e0adb364a9086340b2af51
+2009-02-11T19:06:45.087605Z
+2438
+edavis10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1041
+
diff -r 0c939c159af4 -r 851510f1b535 vendor/plugins/redmine_checkout/README.rdoc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/redmine_checkout/README.rdoc Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 vendor/plugins/redmine_checkout/Rakefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/redmine_checkout/Rakefile Thu Jul 14 10:37:36 2011 +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
diff -r 0c939c159af4 -r 851510f1b535 vendor/plugins/redmine_checkout/app/views/projects/settings/_repository_checkout.rhtml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/redmine_checkout/app/views/projects/settings/_repository_checkout.rhtml Thu Jul 14 10:37:36 2011 +0100
@@ -0,0 +1,54 @@
+