"
-
- 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 << "
"
- s << "
"
- s << "
"
- s << textilizable(project.short_description, :project => project) unless project.description.blank?
- s << "
"
- s << "
"
-
- ancestors << project
+ s << render_project_in_table(project, cycle('odd', 'even'), 0)
end
s << "
"
@@ -237,6 +184,58 @@
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 << textilizable(project.short_description, :project => project)
+ s << "
"
+ end
+
+ 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|
+ s << render_project_in_table(child, oddeven, level + 1)
+ end
+
+ s
+ end
+
# Returns a set of options for a select field, grouped by project.
def version_options_for_select(versions, selected=nil)
diff -r ec56461d3770 -r ab672e427b9b app/models/project.rb
--- a/app/models/project.rb Mon Mar 21 14:40:14 2011 +0000
+++ b/app/models/project.rb Mon Mar 21 16:02:30 2011 +0000
@@ -83,6 +83,7 @@
named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"}
named_scope :all_public, { :conditions => { :is_public => true } }
named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } }
+ named_scope :visible_roots, lambda { { :conditions => Project.root_visible_by(User.current) } }
def identifier=(identifier)
super unless identifier_frozen?
@@ -114,6 +115,10 @@
end
end
+ def self.root_visible_by(user=nil)
+ return "#{Project.table_name}.parent_id IS NULL AND " + visible_by(user)
+ end
+
def self.allowed_to_condition(user, permission, options={})
statements = []
base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
diff -r ec56461d3770 -r ab672e427b9b app/views/layouts/base.rhtml
--- a/app/views/layouts/base.rhtml Mon Mar 21 14:40:14 2011 +0000
+++ b/app/views/layouts/base.rhtml Mon Mar 21 16:02:30 2011 +0000
@@ -3,8 +3,8 @@
<%=h html_title %>
-
-
+
+
<%= favicon %>
<%= stylesheet_link_tag 'application', :media => 'all' %>
<%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
diff -r ec56461d3770 -r ab672e427b9b config/locales/en-GB.yml
--- a/config/locales/en-GB.yml Mon Mar 21 14:40:14 2011 +0000
+++ b/config/locales/en-GB.yml Mon Mar 21 16:02:30 2011 +0000
@@ -387,8 +387,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
@@ -410,14 +410,17 @@
permission_delete_own_messages: Delete own messages
permission_export_wiki_pages: Export wiki pages
- 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_boards: Boards
+ project_module_repository: Source code repository
+ project_module_boards: Forums
+ project_module_gantt: Gantt chart
+ project_module_calendar: Calendar
+ project_module_embedded: Embedded documentation (Javadoc or Doxygen)
label_tipoftheday: Tip of the day
label_notifications: Important Message
@@ -433,6 +436,8 @@
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
@@ -524,7 +529,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
@@ -714,7 +719,7 @@
label_week: Week
label_date_from: From
label_date_to: To
- label_language_based: Based on user's language
+ 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
@@ -725,7 +730,7 @@
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_file_plural: Downloads
label_changeset_plural: Changesets
label_default_columns: Default columns
label_no_change_option: (No change)
@@ -948,8 +953,6 @@
notice_failed_to_save_members: "Failed to save member(s): {{errors}}."
label_project_copy_notifications: Send email notifications during the project copy
field_time_entries: Log time
- project_module_gantt: Gantt
- project_module_calendar: Calendar
field_member_of_group: Member of Group
field_assigned_to_role: Member of Role
button_edit_associated_wikipage: "Edit associated Wiki page: {{page_title}}"
diff -r ec56461d3770 -r ab672e427b9b config/locales/en.yml
--- a/config/locales/en.yml Mon Mar 21 14:40:14 2011 +0000
+++ b/config/locales/en.yml Mon Mar 21 16:02:30 2011 +0000
@@ -402,8 +402,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
@@ -426,16 +426,17 @@
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_boards: Boards
+ 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 or Doxygen)
label_tipoftheday: Tip of the day
label_ssamr_details: Other Details
label_ssamr_institution: Institution
@@ -541,7 +542,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
@@ -743,7 +744,7 @@
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_file_plural: Downloads
label_changeset_plural: Changesets
label_default_columns: Default columns
label_no_change_option: (No change)
diff -r ec56461d3770 -r ab672e427b9b config/routes.rb
--- a/config/routes.rb Mon Mar 21 14:40:14 2011 +0000
+++ b/config/routes.rb Mon Mar 21 16:02:30 2011 +0000
@@ -236,6 +236,8 @@
map.with_options :controller => 'sys' do |sys|
sys.connect 'sys/projects.:format', :action => 'projects', :conditions => {:method => :get}
sys.connect 'sys/projects/:id/repository.:format', :action => 'create_project_repository', :conditions => {:method => :post}
+ sys.connect 'sys/projects/:id/external-repository.:format', :action => 'get_external_repo_url', :conditions => {:method => :get}
+ sys.connect 'sys/projects/:id/embedded.:format', :action => 'set_embedded_active', :conditions => { :method => :post }
end
# Install the default route as the lowest priority.
diff -r ec56461d3770 -r ab672e427b9b extra/soundsoftware/SoundSoftware.pm
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/SoundSoftware.pm Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,433 @@
+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 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";
+
+ if (!defined $read_only_methods{$method}) {
+ print STDERR "SoundSoftware.pm: Method is not read-only, authentication handler required\n";
+ return OK;
+ }
+
+ my $dbh = connect_database($r);
+ unless ($dbh) {
+ print STDERR "SoundSoftware.pm: Database connection failed!: " . $DBI::errstr . "\n";
+ return FORBIDDEN;
+ }
+
+
+print STDERR "Connected to db, dbh is " . $dbh . "\n";
+
+ 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 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 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 ec56461d3770 -r ab672e427b9b extra/soundsoftware/convert-external-repos.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/convert-external-repos.rb Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,173 @@
+#!/usr/bin/env ruby
+
+# == Synopsis
+#
+# convert-external-repos: Update local Mercurial mirrors of external repos,
+# by running an external command for each project requiring an update.
+#
+# == Usage
+#
+# convert-external-repos [OPTIONS...] -s [DIR] -r [HOST]
+#
+# == Arguments (mandatory)
+#
+# -s, --scm-dir=DIR use DIR as base directory for repositories
+# -r, --redmine-host=HOST assume Redmine is hosted on HOST. Examples:
+# -r redmine.example.net
+# -r http://redmine.example.net
+# -r https://example.net/redmine
+# -k, --key=KEY use KEY as the Redmine API key
+# -c, --command=COMMAND use this command to update each external
+# repository: command is called with the name
+# of the project, the path to its repo, and
+# its external repo url as its three args
+#
+# == Options
+#
+# --http-user=USER User for HTTP Basic authentication with Redmine WS
+# --http-pass=PASSWORD Password for Basic authentication with Redmine WS
+# -t, --test only show what should be done
+# -h, --help show help and exit
+# -v, --verbose verbose
+# -V, --version print version and exit
+# -q, --quiet no log
+
+
+require 'getoptlong'
+require 'rdoc/usage'
+require 'find'
+require 'etc'
+
+Version = "1.0"
+
+opts = GetoptLong.new(
+ ['--scm-dir', '-s', GetoptLong::REQUIRED_ARGUMENT],
+ ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT],
+ ['--key', '-k', GetoptLong::REQUIRED_ARGUMENT],
+ ['--http-user', GetoptLong::REQUIRED_ARGUMENT],
+ ['--http-pass', GetoptLong::REQUIRED_ARGUMENT],
+ ['--command' , '-c', GetoptLong::REQUIRED_ARGUMENT],
+ ['--test', '-t', GetoptLong::NO_ARGUMENT],
+ ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
+ ['--version', '-V', GetoptLong::NO_ARGUMENT],
+ ['--help' , '-h', GetoptLong::NO_ARGUMENT],
+ ['--quiet' , '-q', GetoptLong::NO_ARGUMENT]
+ )
+
+$verbose = 0
+$quiet = false
+$redmine_host = ''
+$repos_base = ''
+$http_user = ''
+$http_pass = ''
+$test = false
+
+def log(text, options={})
+ level = options[:level] || 0
+ puts text unless $quiet or level > $verbose
+ exit 1 if options[:exit]
+end
+
+def system_or_raise(command)
+ raise "\"#{command}\" failed" unless system command
+end
+
+begin
+ opts.each do |opt, arg|
+ case opt
+ when '--scm-dir'; $repos_base = arg.dup
+ when '--redmine-host'; $redmine_host = arg.dup
+ when '--key'; $api_key = arg.dup
+ when '--http-user'; $http_user = arg.dup
+ when '--http-pass'; $http_pass = arg.dup
+ when '--command'; $command = arg.dup
+ when '--verbose'; $verbose += 1
+ when '--test'; $test = true
+ when '--version'; puts Version; exit
+ when '--help'; RDoc::usage
+ when '--quiet'; $quiet = true
+ end
+ end
+rescue
+ exit 1
+end
+
+if $test
+ log("running in test mode")
+end
+
+if ($redmine_host.empty? or $repos_base.empty? or $command.empty?)
+ RDoc::usage
+end
+
+unless File.directory?($repos_base)
+ log("directory '#{$repos_base}' doesn't exist", :exit => true)
+end
+
+begin
+ require 'active_resource'
+rescue LoadError
+ log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true)
+end
+
+class Project < ActiveResource::Base
+ self.headers["User-agent"] = "SoundSoftware external repository converter/#{Version}"
+end
+
+log("querying Redmine for projects...", :level => 1);
+
+$redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://")
+$redmine_host.gsub!(/\/$/, '')
+
+Project.site = "#{$redmine_host}/sys";
+Project.user = $http_user;
+Project.password = $http_pass;
+
+begin
+ # Get all active projects that have the Repository module enabled
+ projects = Project.find(:all, :params => {:key => $api_key})
+rescue => e
+ log("Unable to connect to #{Project.site}: #{e}", :exit => true)
+end
+
+if projects.nil?
+ log('no project found, perhaps you forgot to "Enable WS for repository management"', :exit => true)
+end
+
+log("retrieved #{projects.size} projects", :level => 1)
+
+projects.each do |project|
+ log("treating project #{project.name}", :level => 1)
+
+ if project.identifier.empty?
+ log("\tno identifier for project #{project.name}")
+ next
+ elsif not project.identifier.match(/^[a-z0-9\-]+$/)
+ log("\tinvalid identifier for project #{project.name} : #{project.identifier}");
+ next
+ end
+
+ if !project.respond_to?(:repository) or !project.repository.is_external?
+ log("\tproject #{project.identifier} does not use an external repository");
+ next
+ end
+
+ external_url = project.repository.external_url;
+ log("\tproject #{project.identifier} has external repository url #{external_url}");
+
+ if !external_url.match(/^[a-z][a-z+]{0,8}[a-z]:\/\//)
+ log("\tthis doesn't look like a plausible url to me, skipping")
+ next
+ end
+
+ repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
+
+ unless File.directory?(repos_path)
+ log("\tproject repo directory '#{repos_path}' doesn't exist")
+ next
+ end
+
+ system($command, project.identifier, repos_path, external_url)
+
+end
+
diff -r ec56461d3770 -r ab672e427b9b extra/soundsoftware/doxysafe.pl
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/doxysafe.pl Mon Mar 21 16:02:30 2011 +0000
@@ -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 ec56461d3770 -r ab672e427b9b extra/soundsoftware/extract-docs.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/extract-docs.sh Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,149 @@
+#!/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=""
+apihost=""
+apiuser=""
+apipass=""
+
+progdir=$(dirname $0)
+case "$progdir" in
+ /*) ;;
+ *) progdir="$(pwd)/$progdir" ;;
+esac
+
+types="doxygen javadoc" # 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" "http://$apihost/sys/projects/$p/embedded.xml?enable=1&key=$apikey" -d ""
+ else
+ sudo -u docgen curl "http://$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 ec56461d3770 -r ab672e427b9b extra/soundsoftware/extract-doxygen.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/extract-doxygen.sh Mon Mar 21 16:02:30 2011 +0000
@@ -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 ec56461d3770 -r ab672e427b9b extra/soundsoftware/extract-javadoc.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/extract-javadoc.sh Mon Mar 21 16:02:30 2011 +0000
@@ -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 ec56461d3770 -r ab672e427b9b extra/soundsoftware/reposman-soundsoftware.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/reposman-soundsoftware.rb Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,359 @@
+#!/usr/bin/env ruby
+
+# == Synopsis
+#
+# reposman: manages your repositories with Redmine
+#
+# == Usage
+#
+# reposman [OPTIONS...] -s [DIR] -r [HOST]
+#
+# Examples:
+# reposman --scm-dir=/var/svn --redmine-host=redmine.example.net --scm subversion
+# reposman -s /var/git -r redmine.example.net -u http://svn.example.net --scm git
+#
+# == Arguments (mandatory)
+#
+# -s, --scm-dir=DIR use DIR as base directory for repositories
+# -r, --redmine-host=HOST assume Redmine is hosted on HOST. Examples:
+# -r redmine.example.net
+# -r http://redmine.example.net
+# -r https://example.net/redmine
+# -k, --key=KEY use KEY as the Redmine API key
+#
+# == 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(
+ ['--scm-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 '--scm-dir'; $repos_base = arg.dup
+ when '--redmine-host'; $redmine_host = arg.dup
+ when '--key'; $api_key = arg.dup
+ when '--owner'; $svn_owner = arg.dup; $use_groupid = false;
+ 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 exist", :exit => true)
+end
+
+begin
+ require 'active_resource'
+rescue LoadError
+ log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true)
+end
+
+class Project < ActiveResource::Base
+ self.headers["User-agent"] = "SoundSoftware repository manager/#{Version}"
+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
+
+ if project.respond_to?(:repository) and project.repository.is_external?
+ external_url = project.repository.external_url;
+ log("\tproject #{project.identifier} has external repository url #{external_url}");
+ if !external_url.match(/^https?:/)
+ # wot about git, svn/svn+ssh, etc? should we just check for a scheme at all?
+ log("\tthis is not an http(s) url: ignoring");
+ end
+ end
+
+end
+
diff -r ec56461d3770 -r ab672e427b9b extra/soundsoftware/update-external-repo.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/soundsoftware/update-external-repo.sh Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,106 @@
+#!/bin/sh
+
+mirrordir="/var/mirror"
+logfile="/var/www/test-cannam/log/update-external-repo.log"
+
+project="$1"
+local_repo="$2"
+remote_repo="$3"
+
+if [ -z "$project" ] || [ -z "$local_repo" ] || [ -z "$remote_repo" ]; then
+ echo "Usage: $0 "
+ exit 2
+fi
+
+ # We need to handle different source repository types separately.
+ #
+ # The convert extension cannot convert directly from a remote git
+ # repo; we'd have to mirror to a local repo first. Incremental
+ # conversions do work though. The hg-git plugin will convert
+ # directly from remote repositories, but not via all schemes
+ # (e.g. https is not currently supported). It's probably easier to
+ # use git itself to clone locally and then convert or hg-git from
+ # there.
+ #
+ # We can of course convert directly from remote Subversion repos,
+ # but we need to keep track of that -- you can ask to convert into a
+ # repo that has already been used (for Mercurial) and it'll do so
+ # happily; we don't want that.
+ #
+ # Converting from a remote Hg repo should be fine!
+ #
+ # One other thing -- we can't actually tell the difference between
+ # the various SCM types based on URL alone. We have to try them
+ # (ideally in an order determined by a guess based on the URL) and
+ # see what happens.
+
+project_mirror="$mirrordir/$project"
+mkdir -p "$project_mirror"
+project_repo_mirror="$project_mirror/repo"
+
+ # Some test URLs:
+ #
+ # http://aimc.googlecode.com/svn/trunk/
+ # http://aimc.googlecode.com/svn/
+ # http://vagar.org/git/flam
+ # https://github.com/wslihgt/IMMF0salience.git
+ # http://hg.breakfastquay.com/dssi-vst/
+ # git://github.com/schacon/hg-git.git
+ # http://svn.drobilla.net/lad (externals!)
+
+# If we are importing from another distributed system, then our aim is
+# to create either a Hg repo or a git repo at $project_mirror, which
+# we can then pull from directly to the Hg repo at $local_repo (using
+# hg-git, in the case of a git repo).
+
+# Importing from SVN, we should use hg convert directly to the target
+# hg repo (or should we?) but keep a record of the last changeset ID
+# we brought in, and test each time whether it matches the last
+# changeset ID actually in the repo
+
+success=""
+
+if [ -d "$project_repo_mirror" ]; then
+
+ # Repo mirror exists: update it
+ echo "$$: Mirror for project $project exists at $project_repo_mirror, updating" 1>&2
+
+ if [ -d "$project_repo_mirror/.hg" ]; then
+ hg --config extensions.convert= convert --datesort "$remote_repo" "$project_repo_mirror" && success=true
+ elif [ -d "$project_repo_mirror/.git" ]; then
+ ( cd "$project_repo_mirror" && git fetch "$remote_repo" ) && success=true
+ else
+ echo "$$: ERROR: Repo mirror dir $project_repo_mirror exists but is not an Hg or git repo" 1>&2
+ fi
+
+else
+
+ # Repo mirror does not exist yet
+ echo "$$: Mirror for project $project does not yet exist at $project_repo_mirror, trying to convert or clone" 1>&2
+
+ case "$remote_repo" in
+ *git*)
+ git clone "$remote_repo" "$project_repo_mirror" ||
+ hg --config extensions.convert= convert --datesort "$remote_repo" "$project_repo_mirror"
+ ;;
+ *)
+ hg --config extensions.convert= convert --datesort "$remote_repo" "$project_repo_mirror" ||
+ git clone "$remote_repo" "$project_repo_mirror"
+ ;;
+ esac && success=true
+
+fi
+
+echo "Success=$success"
+
+if [ -n "$success" ]; then
+ echo "$$: Update successful, pulling into local repo at $local_repo"
+ if [ -d "$project_repo_mirror/.git" ]; then
+ if [ ! -d "$local_repo" ]; then
+ hg init "$local_repo"
+ fi
+ ( cd "$local_repo" && hg --config extensions.hgext.git= pull "$project_repo_mirror" )
+ else
+ ( cd "$local_repo" && hg pull "$project_repo_mirror" )
+ fi
+fi
diff -r ec56461d3770 -r ab672e427b9b extra/svn/SoundSoftware.pm
--- a/extra/svn/SoundSoftware.pm Mon Mar 21 14:40:14 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 ec56461d3770 -r ab672e427b9b extra/svn/reposman-soundsoftware.rb
--- a/extra/svn/reposman-soundsoftware.rb Mon Mar 21 14:40:14 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 ec56461d3770 -r ab672e427b9b public/themes/soundsoftware/stylesheets/application.css
--- a/public/themes/soundsoftware/stylesheets/application.css Mon Mar 21 14:40:14 2011 +0000
+++ b/public/themes/soundsoftware/stylesheets/application.css Mon Mar 21 16:02:30 2011 +0000
@@ -26,7 +26,7 @@
color: #000;
margin: 0;
margin-bottom: 40px;
-/* font-size: 95%; */
+ min-width: 620px;
}
h1 {
@@ -69,8 +69,9 @@
table.projects th { text-align: left; }
table.projects th.managers { color: #3e442c; }
-table.projects .root .name { font-size: 1.2em; }
-table.projects .root .description { padding-bottom: 0.5em; }
+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; }
@@ -139,3 +140,66 @@
#top-menu a.projects { background-image: url(../../../images/projects.png); }
#top-menu a.administration { background-image: url(../images/wrench.png); }
#top-menu a.help { background-image: url(../../../images/help.png); }
+
+/* for Javadoc in Embedded context: */
+
+.TableHeadingColor { background-color: #fdf7e4; color: #3e442c; border: 0px solid #fff; }
+.TableHeadingColor th { background-color: #fdf7e4; color: #3e442c; border: 1px solid #a9b680; }
+.TableHeadingColor th font { font-size: 1.4em; color: #3e442c; }
+.TableSubHeadingColor { background-color: #fdfaf0; color: #3e442c; border: 0px solid #fff; }
+.TableSubHeadingColor th { background-color: #fdfaf0; color: #3e442c; border: 1px solid #a9b680; }
+.TableRowColor { background-color: #fdfbf5; color: #000000; border: 0; }
+.TableRowColor td { background-color: #fdfbf5; color: #000000; border: 0; }
+.NavBarCell1 { background-color: #ffe69b; color:#000000 }
+
+.embedded table { border: 0px solid #fff; }
+.embedded h1 { font-family: DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; }
+.embedded h3 { margin-top: 0.5em; }
+.embedded hr { color: #a9b680; background-color: #a9b680 }
+.embedded center { text-align: left; } /* haha */
+
+/* Special hack to hide the FRAMES | NO FRAMES links -- they don't
+ work in this context -- and right-align the All Classes and Detail links */
+.embedded .NavBarCell2 a[target=_top] { width: 0px; visibility: hidden; }
+.embedded .NavBarCell2 + .NavBarCell2 { text-align: right; }
+.embedded .NavBarCell3 + .NavBarCell3 { text-align: right; }
+
+/* For Doxygen in Embedded context (though note some of the Javadoc
+ rules will also apply to Doxygen): */
+
+.memItemLeft,
+.memItemRight,
+.memTemplParams,
+.memTemplItemLeft,
+.memTemplItemRight,
+.indexkey,
+.indexvalue,
+.memproto,
+.memproto td,
+.memdoc a,
+.embedded li .el,
+.embedded a.el { font-family: monospace; }
+
+.embedded .memTemplParams { font-style: italic; }
+
+.embedded .memItemRight a:first-child,
+.embedded .memTemplItemRight a:first-child,
+.embedded .indexkey a:first-child,
+.embedded a.el,
+.embedded .memdoc a { font-weight: bold; } /* function names, etc */
+
+.embedded .memitem { border-bottom: 1px solid #a9b680; padding-top: 0.5em; }
+.embedded .memitem:last-child { border-bottom: 0px; }
+
+.embedded .contents { margin-top: 0.5em; }
+.embedded .contents td { padding: 0px; }
+
+.embedded .contents h1,
+.embedded .contents h2,
+.embedded .navigation h1,
+.embedded .navigation h2 { padding-top: 0.5em; padding-bottom: 0.25em; }
+
+.embedded .contents .center { text-align: center; } /* undo javadoc hack above */
+
+
+
diff -r ec56461d3770 -r ab672e427b9b vendor/plugins/embedded/COPYING
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/embedded/COPYING Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff -r ec56461d3770 -r ab672e427b9b vendor/plugins/embedded/README
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/embedded/README Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,6 @@
+Embedded plugin for Redmine
+Copyright (C) 2008 Jean-Philippe Lang
+
+The plugin lets you embed html pages (eg. documentation, test reports) in your projects.
+
+See: http://www.redmine.org/wiki/redmine/PluginEmbedded
diff -r ec56461d3770 -r ab672e427b9b vendor/plugins/embedded/RUNNING_TESTS
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/embedded/RUNNING_TESTS Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,1 @@
+rake test:plugins PLUGIN=embedded
diff -r ec56461d3770 -r ab672e427b9b vendor/plugins/embedded/app/controllers/embedded_controller.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/embedded/app/controllers/embedded_controller.rb Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,112 @@
+# Redmine - project management software
+# Copyright (C) 2008 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+require 'iconv'
+
+class EmbeddedController < ApplicationController
+ class EmbeddedControllerError < StandardError; end
+
+ unloadable
+ layout 'base'
+ before_filter :find_project, :authorize
+
+ def index
+ path = get_real_path(params[:path])
+ if File.directory?(path)
+ file = get_index_file(path)
+ target = params[:path] || []
+ target << file
+ # Forces redirect to the index file when the requested path is a directory
+ # so that relative links in embedded html pages work
+ redirect_to :path => target
+ return
+ end
+
+ # Check file extension
+ raise EmbeddedControllerError.new('This file can not be viewed (invalid extension).') unless Redmine::Plugins::Embedded.valid_extension?(path)
+
+ if Redmine::MimeType.is_type?('image', path)
+ send_file path, :disposition => 'inline', :type => Redmine::MimeType.of(path)
+ else
+ embed_file path
+ end
+
+ rescue Errno::ENOENT => e
+ # File was not found
+ render_404
+ rescue Errno::EACCES => e
+ # Can not read the file
+ render_error "Unable to read the file: #{e.message}"
+ rescue EmbeddedControllerError => e
+ render_error e.message
+ end
+
+ private
+
+ def find_project
+ @project = Project.find(params[:id])
+ rescue ActiveRecord::RecordNotFound
+ render_404
+ end
+
+ # Return the path to the html root directory for the current project
+ def get_project_directory
+ @project_directory ||= Setting.plugin_embedded['path'].to_s.gsub('{PROJECT}', @project.identifier)
+ end
+
+ # Returns the absolute path of the requested file
+ # Parameter is an Array
+ def get_real_path(path)
+ real = get_project_directory
+ real = File.join(real, path) unless path.nil? || path.empty?
+ dir = File.expand_path(get_project_directory)
+ real = File.expand_path(real)
+ raise Errno::ENOENT unless real.starts_with?(dir) && File.exist?(real)
+ real
+ end
+
+ # Returns the index file in the given directory
+ # and raises an exception if none is found
+ def get_index_file(dir)
+ indexes = Setting.plugin_embedded['index'].to_s.split
+ file = indexes.find {|f| File.exist?(File.join(dir, f))}
+ raise EmbeddedControllerError.new("No index file found in #{dir} (#{indexes.join(', ')}).") if file.nil?
+ file
+ end
+
+ # Renders a given HTML file
+ def embed_file(path)
+ @content = File.read(path)
+
+ # Extract html title from embedded page
+ if @content =~ %r{([^<]*)}mi
+ @title = $1.strip
+ end
+
+ # Keep html body only
+ @content.gsub!(%r{^.*]*>(.*).*$}mi, '\\1')
+
+ # Re-encode content if needed
+ source_encoding = Setting.plugin_embedded['encoding'].to_s
+ unless source_encoding.blank?
+ begin; @content = Iconv.new('UTF-8', source_encoding).iconv(@content); rescue; end
+ end
+
+ @doc_template = Redmine::Plugins::Embedded.detect_template_from_path(path)
+ render :action => 'index'
+ end
+end
diff -r ec56461d3770 -r ab672e427b9b vendor/plugins/embedded/app/helpers/embedded_helper.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/embedded/app/helpers/embedded_helper.rb Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,35 @@
+# Redmine - project management software
+# Copyright (C) 2008 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+module EmbeddedHelper
+
+ # Adds include tags for assets of the given template
+ def asset_include_tags(template)
+
+ Redmine::Plugins::Embedded.assets(template).each { |asset| content_for(:header_tags) { asset_include_tag(asset) } }
+ end
+
+ private
+
+ def asset_include_tag(asset)
+ if asset =~ %r{\.js$}
+ javascript_include_tag(asset, :plugin => 'embedded')
+ else
+ stylesheet_link_tag(asset, :plugin => 'embedded')
+ end
+ end
+end
diff -r ec56461d3770 -r ab672e427b9b vendor/plugins/embedded/app/views/embedded/index.rhtml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/embedded/app/views/embedded/index.rhtml Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,9 @@
+
+
+<%= 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 ec56461d3770 -r ab672e427b9b 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 Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,1 @@
+Misc file
diff -r ec56461d3770 -r ab672e427b9b 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 Mon Mar 21 16:02:30 2011 +0000
@@ -0,0 +1,1 @@
+Misc file
diff -r ec56461d3770 -r ab672e427b9b 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 Mon Mar 21 16:02:30 2011 +0000
@@ -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 ec56461d3770 -r ab672e427b9b 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 Mon Mar 21 16:02:30 2011 +0000
@@ -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 ec56461d3770 -r ab672e427b9b 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 Mon Mar 21 16:02:30 2011 +0000
@@ -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