<%=l(:label_project_latest)%>
diff -r 594ed6aef7bd -r 752184172a34 config/locales/en-GB.yml
--- a/config/locales/en-GB.yml Thu Feb 03 15:31:33 2011 +0000
+++ b/config/locales/en-GB.yml Thu Feb 03 15:34:57 2011 +0000
@@ -241,6 +241,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
@@ -432,6 +433,7 @@
other: "{{count}} projects"
label_project_all: All Projects
label_project_latest: Latest projects
+ label_managers: Managed by
label_issue: Issue
label_issue_new: New issue
label_issue_plural: Issues
@@ -843,6 +845,7 @@
text_tip_issue_end_day: task ending this day
text_tip_issue_begin_end_day: task beginning and ending this day
text_project_identifier_info: 'Only lower case letters (a-z), numbers and dashes are allowed.
This will be used in all project-related URLs, and as the repository name. Once saved, the identifier
can not be changed.'
+ text_project_homepage_info: 'Link to an external project page.'
text_project_name_info: "This will be the name of your project throughout this site.
You can change your project's name at any time, in the project's settings."
text_project_visibility_info: "If your project is not public, it will only be visible to users that you have added as project members."
text_user_ssamr_description_info: 'Please describe your current research or development interests, within the fields of audio and music.
This information is publicly visible in your profile and you can edit it at any time.'
@@ -887,6 +890,7 @@
text_wiki_page_destroy_children: "Delete child pages and all their descendants"
text_wiki_page_reassign_children: "Reassign child pages to this parent page"
text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?"
+ text_settings_repo_creation: The repository for a project should be set up automatically within a few minutes of the project being created.
You should not have to adjust any settings here; please check again in ten minutes.
default_role_manager: Manager
default_role_developer: Developer
@@ -945,7 +949,7 @@
label_user_mail_option_only_my_events: Only for things I watch or I'm involved in
label_user_mail_option_only_assigned: Only for things I am assigned to
notice_not_authorized_archived_project: The project you're trying to access has been archived.
- label_principal_search: "Search for user or group:"
+ label_principal_search: "Search by name:"
label_user_search: "Search for user:"
field_visible: Visible
setting_emails_header: Emails header
diff -r 594ed6aef7bd -r 752184172a34 config/locales/en.yml
--- a/config/locales/en.yml Thu Feb 03 15:31:33 2011 +0000
+++ b/config/locales/en.yml Thu Feb 03 15:34:57 2011 +0000
@@ -246,6 +246,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
@@ -448,6 +449,7 @@
other: "{{count}} projects"
label_project_all: All Projects
label_project_latest: Latest projects
+ label_managers: Managed by
label_issue: Issue
label_issue_new: New issue
label_issue_plural: Issues
@@ -797,7 +799,7 @@
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:"
button_login: Login
@@ -918,6 +920,7 @@
text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?"
text_zoom_in: Zoom in
text_zoom_out: Zoom out
+ text_settings_repo_creation: The repository for a project should be set up automatically within a few minutes of the project being created.
You should not have to adjust any settings here.
Please check again in ten minutes, and
contact us if there is any problem.
default_role_manager: Manager
default_role_developer: Developer
diff -r 594ed6aef7bd -r 752184172a34 extra/soundsoftware/SoundSoftware.pm
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/SoundSoftware.pm Thu Feb 03 15:34:57 2011 +0000
@@ -0,0 +1,422 @@
+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)
+
+=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\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";
+
+ if (!defined $read_only_methods{$method}) {
+ print STDERR "SoundSoftware.pm: Method is not read-only, authentication handler required\n";
+ return OK;
+ }
+
+ my $dbh = connect_database($r);
+
+ my $project_id = get_project_identifier($dbh, $r);
+ 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\n";
+
+ my $dbh = connect_database($r);
+
+ 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 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 594ed6aef7bd -r 752184172a34 extra/soundsoftware/reposman-soundsoftware.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/reposman-soundsoftware.rb Thu Feb 03 15:34:57 2011 +0000
@@ -0,0 +1,350 @@
+#!/usr/bin/env ruby
+
+# == Synopsis
+#
+# reposman: manages your repositories with Redmine
+#
+# == Usage
+#
+# reposman [OPTIONS...] -s [DIR] -r [HOST]
+#
+# Examples:
+# reposman --svn-dir=/var/svn --redmine-host=redmine.example.net --scm subversion
+# reposman -s /var/git -r redmine.example.net -u http://svn.example.net --scm git
+#
+# == Arguments (mandatory)
+#
+# -s, --svn-dir=DIR use DIR as base directory for svn repositories
+# -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
+#
+# == Options
+#
+# -o, --owner=OWNER owner of the repository. using the rails login
+# allow user to browse the repository within
+# Redmine even for private project. If you want to
+# share repositories through Redmine.pm, you need
+# to use the apache owner.
+# -g, --group=GROUP group of the repository. (default: root)
+# --scm=SCM the kind of SCM repository you want to create (and
+# register) in Redmine (default: Subversion).
+# reposman is able to create Git and Subversion
+# repositories. For all other kind, you must specify
+# a --command option
+# -u, --url=URL the base url Redmine will use to access your
+# repositories. This option is used to automatically
+# register the repositories in Redmine. The project
+# identifier will be appended to this url. Examples:
+# -u https://example.net/svn
+# -u file:///var/svn/
+# if this option isn't set, reposman will register
+# the repositories with local file paths in Redmine
+# -c, --command=COMMAND use this command instead of "svnadmin create" to
+# create a repository. This option can be used to
+# create repositories other than subversion and git
+# kind.
+# This command override the default creation for git
+# and subversion.
+# --http-user=USER User for HTTP Basic authentication with Redmine WS
+# --http-pass=PASSWORD Password for Basic authentication with Redmine WS
+# -t, --test only show what should be done
+# -h, --help show help and exit
+# -v, --verbose verbose
+# -V, --version print version and exit
+# -q, --quiet no log
+#
+# == References
+#
+# You can find more information on the redmine's wiki : http://www.redmine.org/wiki/redmine/HowTos
+
+
+require 'getoptlong'
+require 'rdoc/usage'
+require 'find'
+require 'etc'
+
+Version = "1.3"
+SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem )
+
+opts = GetoptLong.new(
+ ['--svn-dir', '-s', GetoptLong::REQUIRED_ARGUMENT],
+ ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
+ ['--key', '-k', GetoptLong::REQUIRED_ARGUMENT],
+ ['--owner', '-o', GetoptLong::REQUIRED_ARGUMENT],
+ ['--group', '-g', GetoptLong::REQUIRED_ARGUMENT],
+ ['--url', '-u', GetoptLong::REQUIRED_ARGUMENT],
+ ['--command' , '-c', GetoptLong::REQUIRED_ARGUMENT],
+ ['--scm', GetoptLong::REQUIRED_ARGUMENT],
+ ['--http-user', GetoptLong::REQUIRED_ARGUMENT],
+ ['--http-pass', GetoptLong::REQUIRED_ARGUMENT],
+ ['--test', '-t', GetoptLong::NO_ARGUMENT],
+ ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
+ ['--version', '-V', GetoptLong::NO_ARGUMENT],
+ ['--help' , '-h', GetoptLong::NO_ARGUMENT],
+ ['--quiet' , '-q', GetoptLong::NO_ARGUMENT]
+ )
+
+$verbose = 0
+$quiet = false
+$redmine_host = ''
+$repos_base = ''
+$http_user = ''
+$http_pass = ''
+$svn_owner = 'root'
+$svn_group = 'root'
+$use_groupid = true
+$svn_url = false
+$test = false
+$scm = 'Subversion'
+
+def log(text, options={})
+ level = options[:level] || 0
+ puts text unless $quiet or level > $verbose
+ exit 1 if options[:exit]
+end
+
+def system_or_raise(command)
+ raise "\"#{command}\" failed" unless system command
+end
+
+module SCM
+
+ module Subversion
+ def self.create(path)
+ system_or_raise "svnadmin create #{path}"
+ end
+ end
+
+ module Git
+ def self.create(path)
+ Dir.mkdir path
+ Dir.chdir(path) do
+ system_or_raise "git --bare init --shared"
+ system_or_raise "git update-server-info"
+ end
+ end
+ end
+
+end
+
+begin
+ opts.each do |opt, arg|
+ case opt
+ when '--svn-dir'; $repos_base = arg.dup
+ when '--redmine-host'; $redmine_host = arg.dup
+ when '--key'; $api_key = arg.dup
+ when '--owner'; $svn_owner = arg.dup; $use_groupid = false;
+ when '--group'; $svn_group = arg.dup; $use_groupid = false;
+ when '--url'; $svn_url = arg.dup
+ when '--scm'; $scm = arg.dup.capitalize; log("Invalid SCM: #{$scm}", :exit => true) unless SUPPORTED_SCM.include?($scm)
+ when '--http-user'; $http_user = arg.dup
+ when '--http-pass'; $http_pass = arg.dup
+ when '--command'; $command = arg.dup
+ when '--verbose'; $verbose += 1
+ when '--test'; $test = true
+ when '--version'; puts Version; exit
+ when '--help'; RDoc::usage
+ when '--quiet'; $quiet = true
+ end
+ end
+rescue
+ exit 1
+end
+
+if $test
+ log("running in test mode")
+end
+
+# Make sure command is overridden if SCM vendor is not handled internally (for the moment Subversion and Git)
+if $command.nil?
+ begin
+ scm_module = SCM.const_get($scm)
+ rescue
+ log("Please use --command option to specify how to create a #{$scm} repository.", :exit => true)
+ end
+end
+
+$svn_url += "/" if $svn_url and not $svn_url.match(/\/$/)
+
+if ($redmine_host.empty? or $repos_base.empty?)
+ RDoc::usage
+end
+
+unless File.directory?($repos_base)
+ log("directory '#{$repos_base}' doesn't exists", :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"] = "Redmine repository manager/#{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)
+
+def set_owner_and_rights(project, repos_path, &block)
+ if RUBY_PLATFORM =~ /mswin/
+ yield if block_given?
+ else
+ uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project.identifier).gid : Etc.getgrnam($svn_group).gid)
+ right = project.is_public ? 02775 : 02770
+ yield if block_given?
+ Find.find(repos_path) do |f|
+ File.chmod right, f
+ File.chown uid, gid, f
+ end
+ end
+end
+
+def other_read_right?(file)
+ (File.stat(file).mode & 0007).zero? ? false : true
+end
+
+def owner_name(file)
+ mswin? ?
+ $svn_owner :
+ Etc.getpwuid( File.stat(file).uid ).name
+end
+
+def mswin?
+ (RUBY_PLATFORM =~ /(:?mswin|mingw)/) || (RUBY_PLATFORM == 'java' && (ENV['OS'] || ENV['os']) =~ /windows/i)
+end
+
+projects.each do |project|
+ log("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
+
+ repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
+
+ create_repos = false
+
+ # Logic required for SoundSoftware.ac.uk repositories:
+ #
+ # * If the project has a repository path declared already,
+ # - if it's a local path,
+ # - if it does not exist
+ # - if it has the right root
+ # - create it
+ # - else
+ # - leave alone (remote repository)
+ # * else
+ # - create repository with same name as project
+ # - set to project
+
+ if project.respond_to?(:repository)
+
+ repos_url = project.repository.url;
+ log("\texisting url for project #{project.identifier} is #{repos_url}");
+
+ if repos_url.match(/^file:\//) || repos_url.match(/^\//)
+
+ repos_url = repos_url.gsub(/^file:\/*/, "/");
+ log("\tthis is a local file path, at #{repos_url}");
+
+ if repos_url.slice(0, $repos_base.length) != $repos_base
+ log("\tit is in the wrong place: replacing it");
+ # leave repos_path set to our original suggestion
+ create_repos = true
+ else
+ if !File.directory?(repos_url)
+ log("\tit doesn't exist; we should create it");
+ repos_path = repos_url
+ create_repos = true
+ else
+ log("\tit exists and is in the right place");
+ end
+ end
+ else
+ log("\tthis is a remote path, leaving alone");
+ end
+ else
+ log("\tproject #{project.identifier} has no repository registered")
+# if File.directory?(repos_path)
+# log("\trepository path #{repos_path} already exists, not creating")
+# else
+ create_repos = true
+# end
+ end
+
+ if create_repos
+
+ registration_url = repos_path
+ if $svn_url
+ registration_url = "#{$svn_url}#{project.identifier}"
+ end
+
+ if $test
+ log("\tproposal: create repository #{repos_path}")
+ log("\tproposal: register repository #{repos_path} in Redmine with vendor #{$scm}, url #{registration_url}")
+ next
+ end
+
+# No -- we need "other" users to be able to read it. Access control
+# is not handled through Unix user id anyway
+# project.is_public ? File.umask(0002) : File.umask(0007)
+ File.umask(0002)
+
+ log("\taction: create repository #{repos_path}")
+
+ begin
+ if !File.directory?(repos_path)
+ set_owner_and_rights(project, repos_path) do
+ if scm_module.nil?
+ log("\trunning command: #{$command} #{repos_path}")
+ system_or_raise "#{$command} #{repos_path}"
+ else
+ scm_module.create(repos_path)
+ end
+ end
+ end
+ rescue => e
+ log("\tunable to create #{repos_path} : #{e}\n")
+ next
+ end
+
+ begin
+ log("\taction: register repository #{repos_path} in Redmine with vendor #{$scm}, url #{registration_url}");
+ project.post(:repository, :vendor => $scm, :repository => {:url => "#{registration_url}"}, :key => $api_key)
+ rescue => e
+ log("\trepository #{repos_path} not registered in Redmine: #{e.message}");
+ end
+
+ log("\trepository #{repos_path} created");
+ end
+
+end
+
diff -r 594ed6aef7bd -r 752184172a34 extra/svn/SoundSoftware.pm
--- a/extra/svn/SoundSoftware.pm Thu Feb 03 15:31:33 2011 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,422 +0,0 @@
-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)
-
-=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\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";
-
- if (!defined $read_only_methods{$method}) {
- print STDERR "SoundSoftware.pm: Method is not read-only, authentication handler required\n";
- return OK;
- }
-
- my $dbh = connect_database($r);
-
- my $project_id = get_project_identifier($dbh, $r);
- 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\n";
-
- my $dbh = connect_database($r);
-
- 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 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 594ed6aef7bd -r 752184172a34 extra/svn/reposman-soundsoftware.rb
--- a/extra/svn/reposman-soundsoftware.rb Thu Feb 03 15:31:33 2011 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,350 +0,0 @@
-#!/usr/bin/env ruby
-
-# == Synopsis
-#
-# reposman: manages your repositories with Redmine
-#
-# == Usage
-#
-# reposman [OPTIONS...] -s [DIR] -r [HOST]
-#
-# Examples:
-# reposman --svn-dir=/var/svn --redmine-host=redmine.example.net --scm subversion
-# reposman -s /var/git -r redmine.example.net -u http://svn.example.net --scm git
-#
-# == Arguments (mandatory)
-#
-# -s, --svn-dir=DIR use DIR as base directory for svn repositories
-# -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
-#
-# == Options
-#
-# -o, --owner=OWNER owner of the repository. using the rails login
-# allow user to browse the repository within
-# Redmine even for private project. If you want to
-# share repositories through Redmine.pm, you need
-# to use the apache owner.
-# -g, --group=GROUP group of the repository. (default: root)
-# --scm=SCM the kind of SCM repository you want to create (and
-# register) in Redmine (default: Subversion).
-# reposman is able to create Git and Subversion
-# repositories. For all other kind, you must specify
-# a --command option
-# -u, --url=URL the base url Redmine will use to access your
-# repositories. This option is used to automatically
-# register the repositories in Redmine. The project
-# identifier will be appended to this url. Examples:
-# -u https://example.net/svn
-# -u file:///var/svn/
-# if this option isn't set, reposman will register
-# the repositories with local file paths in Redmine
-# -c, --command=COMMAND use this command instead of "svnadmin create" to
-# create a repository. This option can be used to
-# create repositories other than subversion and git
-# kind.
-# This command override the default creation for git
-# and subversion.
-# --http-user=USER User for HTTP Basic authentication with Redmine WS
-# --http-pass=PASSWORD Password for Basic authentication with Redmine WS
-# -t, --test only show what should be done
-# -h, --help show help and exit
-# -v, --verbose verbose
-# -V, --version print version and exit
-# -q, --quiet no log
-#
-# == References
-#
-# You can find more information on the redmine's wiki : http://www.redmine.org/wiki/redmine/HowTos
-
-
-require 'getoptlong'
-require 'rdoc/usage'
-require 'find'
-require 'etc'
-
-Version = "1.3"
-SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem )
-
-opts = GetoptLong.new(
- ['--svn-dir', '-s', GetoptLong::REQUIRED_ARGUMENT],
- ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
- ['--key', '-k', GetoptLong::REQUIRED_ARGUMENT],
- ['--owner', '-o', GetoptLong::REQUIRED_ARGUMENT],
- ['--group', '-g', GetoptLong::REQUIRED_ARGUMENT],
- ['--url', '-u', GetoptLong::REQUIRED_ARGUMENT],
- ['--command' , '-c', GetoptLong::REQUIRED_ARGUMENT],
- ['--scm', GetoptLong::REQUIRED_ARGUMENT],
- ['--http-user', GetoptLong::REQUIRED_ARGUMENT],
- ['--http-pass', GetoptLong::REQUIRED_ARGUMENT],
- ['--test', '-t', GetoptLong::NO_ARGUMENT],
- ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
- ['--version', '-V', GetoptLong::NO_ARGUMENT],
- ['--help' , '-h', GetoptLong::NO_ARGUMENT],
- ['--quiet' , '-q', GetoptLong::NO_ARGUMENT]
- )
-
-$verbose = 0
-$quiet = false
-$redmine_host = ''
-$repos_base = ''
-$http_user = ''
-$http_pass = ''
-$svn_owner = 'root'
-$svn_group = 'root'
-$use_groupid = true
-$svn_url = false
-$test = false
-$scm = 'Subversion'
-
-def log(text, options={})
- level = options[:level] || 0
- puts text unless $quiet or level > $verbose
- exit 1 if options[:exit]
-end
-
-def system_or_raise(command)
- raise "\"#{command}\" failed" unless system command
-end
-
-module SCM
-
- module Subversion
- def self.create(path)
- system_or_raise "svnadmin create #{path}"
- end
- end
-
- module Git
- def self.create(path)
- Dir.mkdir path
- Dir.chdir(path) do
- system_or_raise "git --bare init --shared"
- system_or_raise "git update-server-info"
- end
- end
- end
-
-end
-
-begin
- opts.each do |opt, arg|
- case opt
- when '--svn-dir'; $repos_base = arg.dup
- when '--redmine-host'; $redmine_host = arg.dup
- when '--key'; $api_key = arg.dup
- when '--owner'; $svn_owner = arg.dup; $use_groupid = false;
- when '--group'; $svn_group = arg.dup; $use_groupid = false;
- when '--url'; $svn_url = arg.dup
- when '--scm'; $scm = arg.dup.capitalize; log("Invalid SCM: #{$scm}", :exit => true) unless SUPPORTED_SCM.include?($scm)
- when '--http-user'; $http_user = arg.dup
- when '--http-pass'; $http_pass = arg.dup
- when '--command'; $command = arg.dup
- when '--verbose'; $verbose += 1
- when '--test'; $test = true
- when '--version'; puts Version; exit
- when '--help'; RDoc::usage
- when '--quiet'; $quiet = true
- end
- end
-rescue
- exit 1
-end
-
-if $test
- log("running in test mode")
-end
-
-# Make sure command is overridden if SCM vendor is not handled internally (for the moment Subversion and Git)
-if $command.nil?
- begin
- scm_module = SCM.const_get($scm)
- rescue
- log("Please use --command option to specify how to create a #{$scm} repository.", :exit => true)
- end
-end
-
-$svn_url += "/" if $svn_url and not $svn_url.match(/\/$/)
-
-if ($redmine_host.empty? or $repos_base.empty?)
- RDoc::usage
-end
-
-unless File.directory?($repos_base)
- log("directory '#{$repos_base}' doesn't exists", :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"] = "Redmine repository manager/#{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)
-
-def set_owner_and_rights(project, repos_path, &block)
- if RUBY_PLATFORM =~ /mswin/
- yield if block_given?
- else
- uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project.identifier).gid : Etc.getgrnam($svn_group).gid)
- right = project.is_public ? 02775 : 02770
- yield if block_given?
- Find.find(repos_path) do |f|
- File.chmod right, f
- File.chown uid, gid, f
- end
- end
-end
-
-def other_read_right?(file)
- (File.stat(file).mode & 0007).zero? ? false : true
-end
-
-def owner_name(file)
- mswin? ?
- $svn_owner :
- Etc.getpwuid( File.stat(file).uid ).name
-end
-
-def mswin?
- (RUBY_PLATFORM =~ /(:?mswin|mingw)/) || (RUBY_PLATFORM == 'java' && (ENV['OS'] || ENV['os']) =~ /windows/i)
-end
-
-projects.each do |project|
- log("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
-
- repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
-
- create_repos = false
-
- # Logic required for SoundSoftware.ac.uk repositories:
- #
- # * If the project has a repository path declared already,
- # - if it's a local path,
- # - if it does not exist
- # - if it has the right root
- # - create it
- # - else
- # - leave alone (remote repository)
- # * else
- # - create repository with same name as project
- # - set to project
-
- if project.respond_to?(:repository)
-
- repos_url = project.repository.url;
- log("\texisting url for project #{project.identifier} is #{repos_url}");
-
- if repos_url.match(/^file:\//) || repos_url.match(/^\//)
-
- repos_url = repos_url.gsub(/^file:\/*/, "/");
- log("\tthis is a local file path, at #{repos_url}");
-
- if repos_url.slice(0, $repos_base.length) != $repos_base
- log("\tit is in the wrong place: replacing it");
- # leave repos_path set to our original suggestion
- create_repos = true
- else
- if !File.directory?(repos_url)
- log("\tit doesn't exist; we should create it");
- repos_path = repos_url
- create_repos = true
- else
- log("\tit exists and is in the right place");
- end
- end
- else
- log("\tthis is a remote path, leaving alone");
- end
- else
- log("\tproject #{project.identifier} has no repository registered")
-# if File.directory?(repos_path)
-# log("\trepository path #{repos_path} already exists, not creating")
-# else
- create_repos = true
-# end
- end
-
- if create_repos
-
- registration_url = repos_path
- if $svn_url
- registration_url = "#{$svn_url}#{project.identifier}"
- end
-
- if $test
- log("\tproposal: create repository #{repos_path}")
- log("\tproposal: register repository #{repos_path} in Redmine with vendor #{$scm}, url #{registration_url}")
- next
- end
-
-# No -- we need "other" users to be able to read it. Access control
-# is not handled through Unix user id anyway
-# project.is_public ? File.umask(0002) : File.umask(0007)
- File.umask(0002)
-
- log("\taction: create repository #{repos_path}")
-
- begin
- if !File.directory?(repos_path)
- set_owner_and_rights(project, repos_path) do
- if scm_module.nil?
- log("\trunning command: #{$command} #{repos_path}")
- system_or_raise "#{$command} #{repos_path}"
- else
- scm_module.create(repos_path)
- end
- end
- end
- rescue => e
- log("\tunable to create #{repos_path} : #{e}\n")
- next
- end
-
- begin
- log("\taction: register repository #{repos_path} in Redmine with vendor #{$scm}, url #{registration_url}");
- project.post(:repository, :vendor => $scm, :repository => {:url => "#{registration_url}"}, :key => $api_key)
- rescue => e
- log("\trepository #{repos_path} not registered in Redmine: #{e.message}");
- end
-
- log("\trepository #{repos_path} created");
- end
-
-end
-
diff -r 594ed6aef7bd -r 752184172a34 public/themes/soundsoftware/images/home.png
Binary file public/themes/soundsoftware/images/home.png has changed
diff -r 594ed6aef7bd -r 752184172a34 public/themes/soundsoftware/stylesheets/application.css
--- a/public/themes/soundsoftware/stylesheets/application.css Thu Feb 03 15:31:33 2011 +0000
+++ b/public/themes/soundsoftware/stylesheets/application.css Thu Feb 03 15:34:57 2011 +0000
@@ -26,7 +26,7 @@
color: #000;
margin: 0;
margin-bottom: 40px;
-/* font-size: 95%; */
+ min-width: 620px;
}
h1 {
@@ -67,6 +67,20 @@
tr.entry { border-left: 1px solid #a9b680; border-right: 1px solid #a9b680; }
tr.entry:last-child { border-bottom: 1px solid #a9b680; }
+table.projects th { text-align: left; }
+table.projects th.managers { color: #3e442c; }
+table.projects .root .name { font-size: 1.2em; padding-top: 0.3em; }
+table.projects .description { padding-bottom: 0.5em; }
+table.projects .no_description { padding-bottom: 0.5em; }
+table.projects .hosted_here { font-weight: bold; }
+table.projects .child .name { font-weight: normal; }
+table.projects .child .description { font-size: 0.95em; }
+table.projects .child .firstcol { padding-left: 1em }
+table.projects .level2 .firstcol { padding-left: 2em; }
+table.projects .level3 .firstcol { padding-left: 3em; }
+
+ul.projects .public, ul.projects .private { padding-left: 0.5em; color: #3e442c; font-size: 0.95em }
+
#top-menu { position: absolute; top: 0; z-index: 1; left: 0px; width: 100%; font-size: 90%; /* height: 2em; */ margin: 0; padding: 0; padding-top: 0.5em; background-color: #3e442c; }
#top-menu ul { margin-left: 10px; }
#top-menu a { font-weight: bold; }
@@ -74,9 +88,9 @@
#header a { color: #be5700; }
#header h1 { color: #525a38; margin-top: 25px; font-size: 3em; font-weight: normal; margin-left: 10px; }
.header-general h1 {
- background: url('soundsoftware-logo-title-only-transparent.png') no-repeat 0 0;
+ background: url('soundsoftware-logo-title-only-transparent-beta.png') no-repeat 0 0;
text-indent: -9999px;
- width: 446px;
+ width: 500px;
height: 34px;
}
diff -r 594ed6aef7bd -r 752184172a34 public/themes/soundsoftware/stylesheets/soundsoftware-logo-title-only-transparent-beta.png
Binary file public/themes/soundsoftware/stylesheets/soundsoftware-logo-title-only-transparent-beta.png has changed
diff -r 594ed6aef7bd -r 752184172a34 vendor/plugins/redmine_checkout/config/locales/en-GB.yml
--- a/vendor/plugins/redmine_checkout/config/locales/en-GB.yml Thu Feb 03 15:31:33 2011 +0000
+++ b/vendor/plugins/redmine_checkout/config/locales/en-GB.yml Thu Feb 03 15:34:57 2011 +0000
@@ -1,4 +1,4 @@
-en:
+en-GB:
label_checkout: "Checkout"
setting_checkout_display_checkout_info: "Display checkout information"