Mercurial > hg > soundsoftware-site
changeset 247:73ff0e6a11b1 cannam
* Merge from branch cannam-pre-20110113-merge
author | Chris Cannam |
---|---|
date | Thu, 03 Mar 2011 12:11:53 +0000 |
parents | eeebe205a056 (current diff) 7cec015f07ce (diff) |
children | 3bcfbf971c40 |
files | app/controllers/account_controller.rb app/controllers/application_controller.rb app/controllers/issues_controller.rb app/controllers/my_controller.rb app/controllers/projects_controller.rb app/controllers/users_controller.rb app/helpers/application_helper.rb app/helpers/projects_helper.rb app/models/issue.rb app/models/project.rb app/models/user.rb app/views/layouts/base.rhtml app/views/users/_form.rhtml config/locales/en-GB.yml config/locales/en.yml config/routes.rb extra/svn/SoundSoftware.pm extra/svn/reposman-soundsoftware.rb public/javascripts/application.js public/stylesheets/application.css |
diffstat | 70 files changed, 4620 insertions(+), 901 deletions(-) [+] |
line wrap: on
line diff
--- a/app/controllers/account_controller.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/controllers/account_controller.rb Thu Mar 03 12:11:53 2011 +0000 @@ -108,7 +108,9 @@ # associates the 2 objects @user.ssamr_user_detail = @ssamr_user_details + @selected_institution_id = params[:ssamr_user_details][:institution_id].to_i + case Setting.self_registration when '1' register_by_email_activation(@user)
--- a/app/controllers/application_controller.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/controllers/application_controller.rb Thu Mar 03 12:11:53 2011 +0000 @@ -263,6 +263,12 @@ uri = URI.parse(back_url) # do not redirect user to another host or to the login or register page if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)}) + # soundsoftware: if login page is https but back_url http, + # switch back_url to https to ensure cookie validity (#83) + if (uri.scheme == "http") && (URI.parse(request.url).scheme == "https") + uri.scheme = "https" + back_url = uri.to_s + end redirect_to(back_url) return end
--- a/app/controllers/issues_controller.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/controllers/issues_controller.rb Thu Mar 03 12:11:53 2011 +0000 @@ -139,15 +139,12 @@ call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue}) - # Adds user to watcher's list - @issue.add_watcher(User.current) - # Also adds the assignee to the watcher's list - if params[:issue][:assigned_to_id] && !params[:issue][:assigned_to_id].empty?: - unless @issue.watcher_ids.include?(params[:issue][:assigned_to_id]): - @issue.add_watcher(User.find(params[:issue][:assigned_to_id])) - end + if params[:issue][:assigned_to_id] && !params[:issue][:assigned_to_id].empty?: + unless @issue.watcher_ids.include?(params[:issue][:assigned_to_id]): + @issue.add_watcher(User.find(params[:issue][:assigned_to_id])) end + end respond_to do |format| format.html { @@ -289,9 +286,9 @@ # if not, adds it. if params[:issue][:assigned_to_id] && !params[:issue][:assigned_to_id].empty?: - unless @issue.watcher_ids.include?(params[:issue][:assigned_to_id]): - @issue.add_watcher(User.find(params[:issue][:assigned_to_id])) - end + unless @issue.watched_by?(User.find(params[:issue][:assigned_to_id])): + @issue.add_watcher(User.find(params[:issue][:assigned_to_id])) + end end
--- a/app/controllers/my_controller.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/controllers/my_controller.rb Thu Mar 03 12:11:53 2011 +0000 @@ -53,10 +53,39 @@ def account @user = User.current @pref = @user.pref + @ssamr_user_details = @user.ssamr_user_detail + + + if @user.ssamr_user_detail == nil + @selected_institution_id = nil + else + @selected_institution_id = @ssamr_user_details.institution_id.to_i + end + if request.post? @user.safe_attributes = params[:user] @user.pref.attributes = params[:pref] @user.pref[:no_self_notified] = (params[:no_self_notified] == '1') + + if @user.ssamr_user_detail == nil + @ssamr_user_details = SsamrUserDetail.new() + @user.ssamr_user_detail = @ssamr_user_details + else + @ssamr_user_details = @user.ssamr_user_detail + end + + if params[:ssamr_user_details].nil? or params[:ssamr_user_details].empty? + @ssamr_user_details.description = @user.ssamr_user_detail.description + @ssamr_user_details.institution_id = @user.ssamr_user_detail.institution_id + @institution_type = @ssamr_user_details.institution_type + @other_institution = @ssamr_user_details.other_institution + else + @ssamr_user_details.description = params[:ssamr_user_details][:description] + @ssamr_user_details.institution_id = params[:ssamr_user_details][:institution_id] + @ssamr_user_details.institution_type = params[:ssamr_user_details][:institution_type] + @ssamr_user_details.other_institution = params[:ssamr_user_details][:other_institution] + end + if @user.save @user.pref.save @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
--- a/app/controllers/projects_controller.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/controllers/projects_controller.rb Thu Mar 03 12:11:53 2011 +0000 @@ -43,19 +43,20 @@ include RepositoriesHelper include ProjectsHelper - # Lists visible projects + # Lists visible projects. Paginator is for top-level projects only + # (subprojects belong to them) def index respond_to do |format| format.html { - sort_init 'lft' - sort_update %w(lft title created_on updated_on) + sort_init 'name' + sort_update %w(name lft created_on updated_on) @limit = per_page_option - @project_count = Project.visible.count + @project_count = Project.visible_roots.count @project_pages = Paginator.new self, @project_count, @limit, params['page'] @offset ||= @project_pages.current.offset - @projects = Project.visible.all(:offset => @offset, :limit => @limit, :order => sort_clause) + @projects = Project.visible_roots.all(:offset => @offset, :limit => @limit, :order => sort_clause) if User.current.logged? - @user_projects = User.current.projects.sort_by(&:lft) + @user_projects = User.current.projects.sort_by(&:name) end render :template => 'projects/index.rhtml', :layout => !request.xhr? }
--- a/app/controllers/sys_controller.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/controllers/sys_controller.rb Thu Mar 03 12:11:53 2011 +0000 @@ -55,6 +55,31 @@ render :nothing => true, :status => 404 end + def set_embedded_active + project = Project.find(params[:id]) + mods = project.enabled_modules + enable = (params[:enable] == "1") + if mods.detect {|m| m.name == "embedded"} + logger.info "Project #{project.name} currently has Embedded enabled" + if !enable + logger.info "Disabling Embedded" + modnames = mods.all(:select => :name).collect{|m| m.name}.reject{|n| n == "embedded"} + project.enabled_module_names = modnames + end + else + logger.info "Project #{project.name} currently has Embedded disabled" + if enable + logger.info "Enabling Embedded" + modnames = mods.all(:select => :name).collect{|m| m.name} + modnames << "embedded" + project.enabled_module_names = modnames + end + end + render :nothing => true, :status => 200 + rescue ActiveRecord::RecordNotFound + render :nothing => true, :status => 404 + end + protected def check_enabled
--- a/app/controllers/users_controller.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/controllers/users_controller.rb Thu Mar 03 12:11:53 2011 +0000 @@ -65,6 +65,15 @@ if @user.ssamr_user_detail != nil @description = @user.ssamr_user_detail.description + + if @user.ssamr_user_detail.institution_type != nil + # institution_type is true for listed institutions + if (@user.ssamr_user_detail.institution_type) + @institution_name = Institution.find(@user.ssamr_user_detail.institution_id).name + else + @institution_name = @user.ssamr_user_detail.other_institution + end + end end # show projects based on current user visibility @@ -91,7 +100,6 @@ @auth_sources = AuthSource.find(:all) @ssamr_user_details = SsamrUserDetail.new - end verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed } @@ -146,6 +154,12 @@ @ssamr_user_details = @user.ssamr_user_detail + if @user.ssamr_user_detail == nil + @selected_institution_id = nil + else + @selected_institution_id = @user.ssamr_user_detail.institution_id.to_i + end + @auth_sources = AuthSource.find(:all) @membership ||= Member.new end @@ -170,13 +184,18 @@ else @ssamr_user_details = @user.ssamr_user_detail end - - + if params[:ssamr_user_details].nil? or params[:ssamr_user_details].empty? @ssamr_user_details.description = @user.ssamr_user_detail.description + @ssamr_user_details.institution_id = @user.ssamr_user_detail.institution_id + @ssamr_user_details.other_institution = @user.ssamr_user_detail.other_institution + @ssamr_user_details.institution_type = @user.ssamr_user_detail.institution_type + else @ssamr_user_details.description = params[:ssamr_user_details][:description] - @ssamr_user_details.save! + @ssamr_user_details.institution_id = params[:ssamr_user_details][:institution_id] + @ssamr_user_details.other_institution = params[:ssamr_user_details][:other_institution] + @ssamr_user_details.institution_type = params[:ssamr_user_details][:institution_type] end if @user.save
--- a/app/helpers/application_helper.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/helpers/application_helper.rb Thu Mar 03 12:11:53 2011 +0000 @@ -52,7 +52,7 @@ if user.is_a?(User) name = h(user.name(options[:format])) if user.active? - link_to name, :controller => 'users', :action => 'show', :id => user + link_to(name, :controller => 'users', :action => 'show', :id => user) else name end @@ -395,21 +395,28 @@ def page_header_title if @project.nil? || @project.new_record? - h(Setting.app_title) + a = [h(Setting.app_title), ''] + else + pname = [] b = [] ancestors = (@project.root? ? [] : @project.ancestors.visible) if ancestors.any? root = ancestors.shift b << link_to_project(root, {:jump => current_menu_item}, :class => 'root') if ancestors.size > 2 - b << '…' + b << '…' ancestors = ancestors[-2, 2] end b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') } + b = b.join(' » ') + b << (' »') end - b << h(@project) - b.join(' » ') + + pname << h(@project) + + a = [pname, b] + end end
--- a/app/helpers/projects_helper.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/helpers/projects_helper.rb Thu Mar 03 12:11:53 2011 +0000 @@ -163,82 +163,91 @@ s << "<table class='list projects'>" s << "<thead><tr>" - s << sort_header_tag('lft', :caption => l("field_name"), :default_order => 'desc') + s << sort_header_tag('name', :caption => l("field_name")) s << "<th class='managers'>" << l("label_managers") << "</th>" s << sort_header_tag('created_on', :default_order => 'desc') s << sort_header_tag('updated_on', :default_order => 'desc') s << "</tr></thead><tbody>" - ancestors = [] original_project = @project - oddeven = 'even' + level = 0 projects.each do |project| + s << render_project_in_table(project, cycle('odd', 'even'), 0) + end - # set the project environment to please macros. + s << "</table>" - @project = project - - if (ancestors.empty? || project.is_descendant_of?(ancestors.last)) - level = level + 1 - else + @project = original_project + s level = 0 oddeven = cycle('odd','even') - ancestors.pop - while (ancestors.any? && !project.is_descendant_of?(ancestors.last)) - ancestors.pop - end - end + end + + + def render_project_in_table(project, oddeven, level) + + # set the project environment to please macros. + @project = project + + classes = (level == 0 ? 'root' : 'child') + + s = "" + + s << "<tr class='#{oddeven} #{classes} level#{level}'>" + s << "<td class='firstcol' align=top><div class='name hosted_here" + s << " no_description" if project.description.blank? + s << "'>" << link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}"); + s << "</div>" + unless project.description.blank? + s << "<div class='wiki description'>" + s << textilizable(project.short_description, :project => project) + s << "</div>" + end - classes = (ancestors.empty? ? 'root' : 'child') + s << "<td class='managers' align=top>" - s << "<tr class='#{oddeven} #{classes} level#{level}'>" - s << "<td class='firstcol name hosted_here'>" << link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}") << "</td>" - s << "<td class='managers'>" - - 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| + u = project.users_by_role + if u + u.keys.each do |r| + if r.allowed_to?(:edit_project) + mgrs = [] + u[r].sort.each do |m| + mgrs << link_to_user(m) + end + if mgrs.size < 3 + s << '<nobr>' << mgrs.join(', ') << '</nobr>' mgrs << link_to_user(m) end if mgrs.size < 3 s << '<nobr>' << mgrs.join(', ') << '</nobr>' - else - s << mgrs.join(', ') + else + s << mgrs.join(', ') end end end end - - s << "</td>" - s << "<td class='created_on'>" << format_date(project.created_on) << "</td>" - s << "<td class='updated_on'>" << format_date(project.updated_on) << "</td>" - - s << "</tr>" - s << "<tr class='#{oddeven} #{classes}'>" - s << "<td class='firstcol wiki description'>" - s << textilizable(project.short_description, :project => project) unless project.description.blank? - s << "</td>" - s << "<td colspan=3> </td>" s << "</tr>" ancestors << project end - s << "</table>" + s << "</td>" + s << "<td class='created_on' align=top>" << format_date(project.created_on) << "</td>" + s << "<td class='updated_on' align=top>" << format_date(project.updated_on) << "</td>" + + s << "</tr>" - @project = original_project - + 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) grouped = Hash.new {|h,k| h[k] = []}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/models/institution.rb Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,2 @@ +class Institution < ActiveRecord::Base +end
--- a/app/models/issue.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/models/issue.rb Thu Mar 03 12:11:53 2011 +0000 @@ -90,7 +90,7 @@ before_save :close_duplicates, :update_done_ratio_from_issue_status after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal after_destroy :update_parent_attributes - + # Returns true if usr or current user is allowed to view the issue def visible?(usr=nil) (usr || User.current).allowed_to?(:view_issues, self.project) @@ -529,7 +529,8 @@ # Returns a string of css classes that apply to the issue def css_classes - s = "issue status-#{status.position} priority-#{priority.position}" + s = "issue status-#{status.position} " + s << "priority-#{priority.position}" s << ' closed' if closed? s << ' overdue' if overdue? s << ' created-by-me' if User.current.logged? && author_id == User.current.id
--- a/app/models/project.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/models/project.rb Thu Mar 03 12:11:53 2011 +0000 @@ -85,6 +85,7 @@ named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"} named_scope :all_public, { :conditions => { :is_public => true } } named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } } + named_scope :visible_roots, lambda { { :conditions => Project.root_visible_by(User.current) } } def initialize(attributes = nil) super @@ -134,6 +135,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}"
--- a/app/models/ssamr_user_detail.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/models/ssamr_user_detail.rb Thu Mar 03 12:11:53 2011 +0000 @@ -2,4 +2,13 @@ belongs_to :user validates_presence_of :description + + validate :check_institution + + def check_institution() + errors.add(:institution_id, "Please insert an institution") if + institution_id.blank? and other_institution.blank? + end + + end
--- a/app/models/user.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/app/models/user.rb Thu Mar 03 12:11:53 2011 +0000 @@ -82,6 +82,8 @@ before_destroy :remove_references_before_destroy + validates_acceptance_of :terms_and_conditions, :on => :create, :message => :must_accept_terms_and_conditions + def before_create self.mail_notification = Setting.default_notification_option if self.mail_notification.blank? true
--- a/app/views/account/register.rhtml Thu Mar 03 12:02:03 2011 +0000 +++ b/app/views/account/register.rhtml Thu Mar 03 12:11:53 2011 +0000 @@ -1,3 +1,6 @@ +<%= javascript_include_tag "ssamr_institutions" %> +<%= javascript_include_tag "ssamr_registration" %> + <h2><%=l(:label_register)%> <%=link_to l(:label_login_with_open_id_option), signin_url if Setting.openid? %></h2> <% form_tag({:action => 'register'}, :class => "tabular") do %> @@ -34,17 +37,21 @@ <% fields_for :ssamr_user_details, :builder => TabularFormBuilder, :lang => current_language do |ssamr_user_detail| %> <p> + <%= ssamr_user_detail.text_area :description, :rows => 3, :cols => 40, :required => true, :class => 'wiki-edit' %> + <em> <%=l(:text_user_ssamr_description_info)%></em> + </p> - <%= ssamr_user_detail.text_area :description, :rows => 3, :cols => 40, :required => true, :class => 'wiki-edit' %> - <%= wikitoolbar_for 'ssamr_user_details_description' %> + <p><label for="institution"><%=l("field_ssamr_user_detail.institution")%> <span class="required">*</span></label> + <%= ssamr_user_detail.radio_button :institution_type, true %> + <%= ssamr_user_detail.collection_select(:institution_id, Institution.find(:all, :order => "institutions.order"), :id, :name, {:selected => @selected_institution_id, :prompt => true}).gsub('&', '&') %> + </p> - <em> <%=l(:text_user_ssamr_description_info)%></em> + <p> + <%= ssamr_user_detail.radio_button :institution_type, false %> Other: + <%= ssamr_user_detail.text_field(:other_institution) %> </p> <% end %> - - - <% if Setting.openid? %> <p><label for="user_identity_url"><%=l(:field_identity_url)%></label> <%= text_field 'user', 'identity_url' %></p> @@ -56,5 +63,9 @@ <!--[eoform:user]--> </div> +<%= check_box :user, :terms_and_conditions %> <%= l(:accept_terms_and_conditions) %> <%= link_to("Terms and Conditions", "https://code.soundsoftware.ac.uk/projects/soundsoftware-site/wiki/TandCs", {:target => "_blank"}) %>. +<br /> +<br /> + <%= submit_tag l(:button_submit) %> <% end %>
--- a/app/views/issues/_attributes.rhtml Thu Mar 03 12:02:03 2011 +0000 +++ b/app/views/issues/_attributes.rhtml Thu Mar 03 12:11:53 2011 +0000 @@ -8,7 +8,7 @@ <% end %> <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), {:required => true}, :disabled => !@issue.leaf? %></p> -<p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p> +<p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), {:include_blank => true} %></p> <% unless @project.issue_categories.empty? %> <p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %> <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
--- a/app/views/issues/_form.rhtml Thu Mar 03 12:02:03 2011 +0000 +++ b/app/views/issues/_form.rhtml Thu Mar 03 12:11:53 2011 +0000 @@ -35,7 +35,7 @@ <% if @issue.new_record? && User.current.allowed_to?(:add_issue_watchers, @project) -%> <p id="watchers_form"><label><%= l(:label_issue_watchers) %></label> <% @issue.project.users.sort.each do |user| -%> -<label class="floating"><%= check_box_tag 'issue[watcher_user_ids][]', user.id, @issue.watched_by?(user) %> <%=h user %></label> +<label class="floating"><%= check_box_tag 'issue[watcher_user_ids][]', user.id, !!(@issue.watched_by?(user) or user == User.current) %> <%=h user %></label> <% end -%> </p> <% end %>
--- a/app/views/layouts/base.rhtml Thu Mar 03 12:02:03 2011 +0000 +++ b/app/views/layouts/base.rhtml Thu Mar 03 12:11:53 2011 +0000 @@ -3,8 +3,8 @@ <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title><%=h html_title %></title> -<meta name="description" content="<%= Redmine::Info.app_name %>" /> -<meta name="keywords" content="issue,bug,tracker" /> +<meta name="description" content="A repository for software developed and published by audio and music researchers in the UK." /> +<meta name="keywords" content="audio,music,software,research,UK,sound,repository,code,redmine" /> <%= favicon %> <%= stylesheet_link_tag 'application', :media => 'all' %> <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %> @@ -33,17 +33,32 @@ </div> <%= tag('div', {:id => 'header', :class => (display_main_menu?(@project) ? 'header-project' : 'header-general')}, true) %> - <div id="quick-search"> - <% form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %> - <%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %> - <%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>: - <%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %> - <% end %> + + + <div id="project-search-jump"> + <div id="quick-search"> + <% form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %> + <%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %> + <%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>: + <%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %> + <% end %> + </div> + + <div id="project-jump-box"> <%= render_project_jump_box %> + </div> </div> + + <% unless page_header_title[1].empty? %> + <h3 id="project-ancestors-title"><%= page_header_title[1] %></h3> + <% end %> + + <h1 id="project-title" + <% unless page_header_title[1].empty? %> + style="margin-top: 0px; " + <% end %> + ><%= page_header_title[0] %></h1> - <h1><%= page_header_title %></h1> - <% if display_main_menu?(@project) %> <div id="main-menu"> <%= render_main_menu(@project) %>
--- a/app/views/my/account.rhtml Thu Mar 03 12:02:03 2011 +0000 +++ b/app/views/my/account.rhtml Thu Mar 03 12:11:53 2011 +0000 @@ -1,3 +1,5 @@ +<%= javascript_include_tag "ssamr_institutions" %> + <div class="contextual"> <%= link_to(l(:button_change_password), :action => 'password') if @user.change_password_allowed? %> <%= call_hook(:view_my_account_contextual, :user => @user)%> @@ -9,6 +11,8 @@ :builder => TabularFormBuilder, :lang => current_language, :html => { :id => 'my_account_form' } do |f| %> + + <div class="splitcontentleft"> <h3><%=l(:label_information_plural)%></h3> <div class="box tabular"> @@ -23,9 +27,35 @@ <% @user.custom_field_values.select(&:editable?).each do |value| %> <p><%= custom_field_tag_with_label :user, value %></p> <% end %> + <%= call_hook(:view_my_account, :user => @user, :form => f) %> </div> +<h3><%=l(:label_ssamr_details)%></h3> +<div class="box tabular"> + <% fields_for :ssamr_user_details, :builder => TabularFormBuilder, :lang => current_language do |ssamr_user_detail| %> + <p> + <%= ssamr_user_detail.text_area :description, :rows => 3, :cols => 25, :required => true, :class => 'wiki-edit' %> + </p> + + + <p><label for="institution"><%=l("field_ssamr_user_detail.institution")%> <span class="required">*</span></label> + <nobr> + <%= ssamr_user_detail.radio_button :institution_type, true %> + <%= ssamr_user_detail.collection_select(:institution_id, Institution.find(:all, :order => "institutions.order"), :id, :name, {:selected => @selected_institution_id, :prompt => true} ).gsub('&', '&') %> + </nobr> + </p> + + <p> + <nobr> + <%= ssamr_user_detail.radio_button :institution_type, false %> Other: + <%= ssamr_user_detail.text_field :other_institution, :size => 19 %> + </nobr> + </p> + <% end %> +</div> + + <%= submit_tag l(:button_save) %> </div> @@ -43,6 +73,14 @@ </div> <% end %> + + + + + + + + <% content_for :sidebar do %> <%= render :partial => 'sidebar' %> <% end %>
--- a/app/views/users/_form.rhtml Thu Mar 03 12:02:03 2011 +0000 +++ b/app/views/users/_form.rhtml Thu Mar 03 12:11:53 2011 +0000 @@ -1,3 +1,6 @@ +<%= javascript_include_tag "ssamr_institutions" %> + + <%= error_messages_for 'user' %> <!--[form:user]--> @@ -23,12 +26,25 @@ <h3><%=l(:label_ssamr_details)%></h3> <% fields_for :ssamr_user_details, :builder => TabularFormBuilder, :lang => current_language do |ssamr_user_detail| %> <p> - <%= ssamr_user_detail.text_area :description, :rows => 3, :cols => 40, :required => true, :class => 'wiki-edit' %> - <%= wikitoolbar_for 'ssamr_user_details_description' %> - <br /> - <em> <%=l(:text_user_ssamr_description_info)%></em> + <%= ssamr_user_detail.text_area :description, :rows => 3, :cols => 40, :required => true, :class => 'wiki-edit' %> </p> - <% end %> + + <p><label for="institution"><%=l("field_ssamr_user_detail.institution")%> <span class="required">*</span></label> + <nobr> + <%= ssamr_user_detail.radio_button :institution_type, true %> + <%= ssamr_user_detail.collection_select(:institution_id, Institution.find(:all, :order => "institutions.order"), :id, :name, {:selected => @selected_institution_id, :prompt => true} ).gsub('&', '&') %> + </nobr> + </p> + + + + <p> + <nobr> + <%= ssamr_user_detail.radio_button :institution_type, false %> Other: + <%= ssamr_user_detail.text_field :other_institution %> + </nobr> + </p> + <% end %> </div>
--- a/app/views/users/show.rhtml Thu Mar 03 12:02:03 2011 +0000 +++ b/app/views/users/show.rhtml Thu Mar 03 12:11:53 2011 +0000 @@ -23,6 +23,9 @@ <h3><%=l(:label_ssamr_description)%></h3> <%= textilizable @description %> +<h3><%=l(:label_ssamr_institution)%></h3> +<%= h @institution_name %> + <% unless @memberships.empty? %> <h3><%=l(:label_project_plural)%></h3>
--- a/app/views/welcome/index.rhtml Thu Mar 03 12:02:03 2011 +0000 +++ b/app/views/welcome/index.rhtml Thu Mar 03 12:11:53 2011 +0000 @@ -9,14 +9,6 @@ <div class="splitcontentleft"> <%= textilizable Setting.welcome_text %> - <% if not @tipsoftheday.empty? %> - <div class="newsoftheday box"> - <h3><%=l(:label_tipoftheday)%></h3> - <%= textilizable @tipsoftheday %> - </div> - <% end %> - - <% if @news.any? %> <div class="news box"> <h3><%=l(:label_news_latest)%></h3> @@ -28,6 +20,13 @@ </div> <div class="splitcontentright"> + <% if not @tipsoftheday.empty? %> + <div class="newsoftheday box"> + <h3><%=l(:label_tipoftheday)%></h3> + <%= textilizable @tipsoftheday %> + </div> + <% end %> + <% if @projects.any? %> <div class="projects box"> <h3><%=l(:label_project_latest)%></h3>
--- a/config/locales/en-GB.yml Thu Mar 03 12:02:03 2011 +0000 +++ b/config/locales/en-GB.yml Thu Mar 03 12:11:53 2011 +0000 @@ -127,6 +127,7 @@ not_same_project: "doesn't belong to the same project" circular_dependency: "This relation would create a circular dependency" cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" + must_accept_terms_and_conditions: "You must accept the Terms and Conditions" actionview_instancetag_blank_option: Please select @@ -201,6 +202,10 @@ gui_validation_error: 1 error gui_validation_error_plural: "%{count} errors" + institution: Institution + + field_other_institution: '' + field_name: Name field_description: Description field_summary: Summary @@ -239,6 +244,7 @@ field_role: Role field_homepage: Homepage field_is_public: Public + field_is_private: Private field_parent: Subproject of field_is_in_roadmap: Issues displayed in roadmap field_login: Login @@ -378,8 +384,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 @@ -401,15 +407,25 @@ 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 + field_terms_and_conditions: 'Terms and Conditions:' + accept_terms_and_conditions: 'I have read and agree with the ' + label_ssamr_description: Research description + label_ssamr_details: Other Details + label_ssamr_institution: Institution label_user: User label_user_plural: Users label_user_new: New user @@ -417,12 +433,15 @@ label_project: Project label_project_new: New project label_project_plural: Projects + label_my_project_plural: My Projects + label_other_project_plural: Other Projects label_x_projects: zero: no projects one: 1 project other: "%{count} projects" label_project_all: All Projects label_project_latest: Latest projects + label_managers: Managed by label_issue: Issue label_issue_new: New issue label_issue_plural: Issues @@ -505,7 +524,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 @@ -705,7 +724,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) @@ -836,6 +855,12 @@ text_caracters_maximum: "%{count} characters maximum." text_caracters_minimum: "Must be at least %{count} characters long." text_length_between: "Length between %{min} and %{max} characters." + text_project_identifier_info: 'Only lower case letters (a-z), numbers and dashes are allowed.<br /> This will be used in all project-related URLs, and as the repository name. Once saved, the identifier <b>can not</b> be changed.' + text_project_homepage_info: 'Link to an external project page.' + text_project_name_info: "This will be the name of your project throughout this site.<br /> You can change your project's name at any time, in the project's settings." + text_project_visibility_info: "If your project is not public, it will only be visible to users that you have added as project members." + text_user_ssamr_description_info: 'Please describe your current research or development interests, within the fields of audio and music.<br/>This information is publicly visible in your profile and you can edit it at any time.' + text_issue_parent_issue_info: 'If this is a subtask, please insert its parent task number or write the main task name.' text_tracker_no_workflow: No workflow defined for this tracker text_unallowed_characters: Unallowed characters text_comma_separated: Multiple values allowed (comma separated). @@ -873,6 +898,7 @@ text_wiki_page_destroy_children: "Delete child pages and all their descendants" text_wiki_page_reassign_children: "Reassign child pages to this parent page" text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?" + text_settings_repo_creation: The repository for a project should be set up automatically within a few minutes of the project being created.<br>You should not have to adjust any settings here; please check again in ten minutes. default_role_manager: Manager default_role_developer: Developer @@ -919,8 +945,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}" @@ -931,7 +955,7 @@ label_user_mail_option_only_my_events: Only for things I watch or I'm involved in label_user_mail_option_only_assigned: Only for things I am assigned to notice_not_authorized_archived_project: The project you're trying to access has been archived. - label_principal_search: "Search for user or group:" + label_principal_search: "Search by name:" label_user_search: "Search for user:" field_visible: Visible setting_emails_header: Emails header
--- a/config/locales/en.yml Thu Mar 03 12:02:03 2011 +0000 +++ b/config/locales/en.yml Thu Mar 03 12:11:53 2011 +0000 @@ -86,7 +86,6 @@ gb: "GB" tb: "TB" - # Used in array.to_sentence. support: array: @@ -124,6 +123,7 @@ not_same_project: "doesn't belong to the same project" circular_dependency: "This relation would create a circular dependency" cant_link_an_issue_with_a_descendant: "An issue can not be linked to one of its subtasks" + must_accept_terms_and_conditions: "You must accept the Terms and Conditions" actionview_instancetag_blank_option: Please select @@ -206,6 +206,10 @@ gui_validation_error: 1 error gui_validation_error_plural: "%{count} errors" + institution: Institution + + field_other_institution: '' + field_name: Name field_description: Description field_summary: Summary @@ -245,6 +249,7 @@ field_role: Role field_homepage: Homepage field_is_public: Public + field_is_private: Private field_parent: Subproject of field_is_in_roadmap: Issues displayed in roadmap field_login: Login @@ -305,6 +310,8 @@ field_visible: Visible field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text" + field_terms_and_conditions: 'Terms and Conditions:' + accept_terms_and_conditions: 'I have read and agree with the ' setting_app_title: Application title setting_app_subtitle: Application subtitle setting_welcome_text: Welcome text @@ -396,8 +403,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 @@ -420,17 +427,20 @@ 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 label_user: User label_user_plural: Users label_user_new: New user @@ -438,12 +448,15 @@ label_project: Project label_project_new: New project label_project_plural: Projects + label_my_project_plural: My Projects + label_other_project_plural: Other Projects label_x_projects: zero: no projects one: 1 project other: "%{count} projects" label_project_all: All Projects label_project_latest: Latest projects + label_managers: Managed by label_issue: Issue label_issue_new: New issue label_issue_plural: Issues @@ -527,7 +540,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 @@ -729,7 +742,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) @@ -793,7 +806,7 @@ label_profile: Profile label_subtask_plural: Subtasks label_project_copy_notifications: Send email notifications during the project copy - label_principal_search: "Search for user or group:" + label_principal_search: "Search by name:" label_user_search: "Search for user:" button_login: Login @@ -871,6 +884,7 @@ text_caracters_maximum: "%{count} characters maximum." text_caracters_minimum: "Must be at least %{count} characters long." text_length_between: "Length between %{min} and %{max} characters." + text_user_ssamr_description_info: 'Please describe your current research or development interests, within the fields of audio and music.<br/>This information is publicly visible in your profile and you can edit it at any time.' text_tracker_no_workflow: No workflow defined for this tracker text_unallowed_characters: Unallowed characters text_comma_separated: Multiple values allowed (comma separated). @@ -912,6 +926,7 @@ text_zoom_in: Zoom in text_zoom_out: Zoom out text_warn_on_leaving_unsaved: "The current page contains unsaved text that will be lost if you leave this page." + text_settings_repo_creation: The repository for a project should be set up automatically within a few minutes of the project being created.<br>You should not have to adjust any settings here.<br>Please check again in ten minutes, and <a href="/projects/soundsoftware-site/wiki/Help">contact us</a> if there is any problem. default_role_manager: Manager default_role_developer: Developer
--- a/config/routes.rb Thu Mar 03 12:02:03 2011 +0000 +++ b/config/routes.rb Thu Mar 03 12:11:53 2011 +0000 @@ -235,6 +235,7 @@ map.with_options :controller => 'sys' do |sys| sys.connect 'sys/projects.:format', :action => 'projects', :conditions => {:method => :get} sys.connect 'sys/projects/:id/repository.:format', :action => 'create_project_repository', :conditions => {:method => :post} + sys.connect 'sys/projects/:id/embedded.:format', :action => 'set_embedded_active', :conditions => { :method => :post } end # Install the default route as the lowest priority.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/db/seeds.rb Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,9 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). +# +# Examples: +# +# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) +# Major.create(:name => 'Daley', :city => cities.first) + +puts "this is a test" \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/db/migrate/20101216140621_create_institutions.rb Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,13 @@ +class CreateInstitutions < ActiveRecord::Migration + def self.up + create_table :institutions do |t| + t.string :name + + t.timestamps + end + end + + def self.down + drop_table :institutions + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/db/migrate/20101216145813_fix_university_name_in_ssamr_details_table.rb Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,10 @@ +class FixUniversityNameInSsamrDetailsTable < ActiveRecord::Migration + def self.up + rename_column :ssamr_user_details, :university, :institution_id + end + + def self.down + # there's no need to rollback the name of this column + # because it was not used previously + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/db/migrate/20110126153504_add_other_institution_column_to_ssamr_user_details.rb Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,9 @@ +class AddOtherInstitutionColumnToSsamrUserDetails < ActiveRecord::Migration + def self.up + add_column :ssamr_user_details, :other_institution, :string + end + + def self.down + remove_column :ssamr_user_details, :other_institution + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/db/migrate/20110127161758_add_institution_type_column_to_ssamr_user_details.rb Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,9 @@ +class AddInstitutionTypeColumnToSsamrUserDetails < ActiveRecord::Migration + def self.up + add_column :ssamr_user_details, :institution_type, :boolean + end + + def self.down + remove_column :ssamr_user_details, :institution_type + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/db/migrate/20110202170156_add_order_column_to_institutions.rb Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,9 @@ +class AddOrderColumnToInstitutions < ActiveRecord::Migration + def self.up + add_column :institutions, :order, :integer + end + + def self.down + remove_column :institutions, :order + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/db/seed_data/institutions.txt Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,141 @@ +University of Aberdeen|1 +University of Abertay Dundee |2 +Aberystwyth University|3 +Anglia Ruskin University|4 +University of the Arts London|5 +Aston University|6 +Bangor University|7 +University of Bath|8 +Bath Spa University|9 +University of Bedfordshire|10 +Birkbeck, University of London|11 +University of Birmingham|12 +Birmingham City University|13 +Birmingham Conservatoire|14 +University Centre at Blackburn College|15 +University of Bolton|16 +Bournemouth University|17 +University of Bradford|18 +University of Brighton|19 +University of Bristol|20 +Brunel University|21 +University of Buckingham|22 +Buckinghamshire New University|23 +University of Cambridge|24 +Institute of Cancer Research, University of London|25 +Canterbury Christ Church University|26 +Cardiff University|27 +University of Central Lancashire|28 +Central School of Speech and Drama, University of London|29 +University of Chester|30 +University of Chichester|31 +City University London|32 +Courtauld Institute of Art|33 +Coventry University|34 +Cranfield University|35 +University for the Creative Arts|36 +University of Cumbria|37 +De Montfort University|38 +University of Derby|39 +University of Dundee|40 +Durham University|41 +University of East Anglia|42 +University of East London|43 +Edge Hill University|44 +University of Edinburgh|45 +Edinburgh Napier University|46 +Institute of Education, University of London|47 +University of Essex|48 +University of Exeter|49 +University of Glamorgan|50 +University of Glasgow|51 +Glasgow Caledonian University|52 +University of Gloucestershire|53 +Goldsmiths, University of London|54 +University of Greenwich|55 +Glyndŵr University|56 +Heriot-Watt University|57 +University of Hertfordshire|58 +Heythrop College|59 +University of Huddersfield|60 +University of Hull|61 +Hull York Medical School|62 +Imperial College London|63 +Keele University|64 +University of Kent|65 +King's College London|66 +Kingston University|67 +Lancaster University|68 +University of Leeds|69 +Leeds Metropolitan University|70 +University of Leicester|71 +University of Lincoln|72 +University of Liverpool|73 +Liverpool Hope University|74 +Liverpool John Moores University|75 +London Business School|76 +London College of Music|77 +London Metropolitan University|78 +London School of Economics and Political Science|79 +London School of Hygiene and Tropical Medicine|80 +London South Bank University|81 +Loughborough University|82 +University of Manchester|83 +Manchester Metropolitan University|84 +Middlesex University|85 +Newcastle University|86 +University of Northampton|87 +Northumbria University|88 +University of Nottingham|89 +Nottingham Trent University|90 +The Open University|91 +University of Oxford|92 +Oxford Brookes University|93 +Peninsula College of Medicine and Dentistry|94 +University of Plymouth|95 +University of Portsmouth|96 +Queen's University Belfast|97 +Queen Margaret University|98 +Queen Mary, University of London|99 +University of Reading|100 +The Robert Gordon University, Aberdeen|101 +Roehampton University|102 +Royal Academy of Music|103 +Royal College of Art|104 +Royal Holloway, University of London|105 +Royal Veterinary College|106 +University of St Andrews|107 +St George's, University of London|108 +University of Salford|109 +School of Advanced Study, University of London|110 +School of Oriental and African Studies|111 +School of Pharmacy, University of London|112 +University of Sheffield|113 +Sheffield Hallam University|114 +University of Southampton|115 +Southampton Solent University|116 +Staffordshire University|117 +University of Stirling|118 +University of Strathclyde|119 +University of Sunderland|120 +University of Surrey|121 +University of Sussex|122 +Swansea Metropolitan University|123 +Swansea University|124 +University of Teesside|125 +Thames Valley University|126 +University of Ulster|127 +University College London|128 +University of Wales|129 +University of Wales Institute, Cardiff|130 +University of Wales, Newport|131 +University of Wales, Trinity Saint David|132 +University of Warwick|133 +University of Westminster|134 +University of the West of England|135 +University of the West of Scotland|136 +University of Winchester|137 +University of Wolverhampton|138 +University of Worcester|139 +University of York|140 +York St John University|141
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/db/seeds.rb Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,27 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). +# +# Examples: +# +# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) +# Major.create(:name => 'Daley', :city => cities.first) + +def truncate_table(table_name) + quoted = connection.quote_table_name(table_name) + connection.execute("TRUNCATE #{quoted}") +end + +def connection + ActiveRecord::Base.connection +end + +truncate_table('institutions') + +open("db/seed_data/institutions.txt") do |institutions| + institutions.read.each_line do |institution| + inst=institution.split('|') + + + Institution.create(:name => inst[0].chomp, :order => inst[1].chomp) + end +end \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/soundsoftware/SoundSoftware.pm Thu Mar 03 12:11:53 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/" + + <Location /> + AuthName "Mercurial" + AuthType Basic + Require valid-user + PerlAccessHandler Apache::Authn::SoundSoftware::access_handler + PerlAuthenHandler Apache::Authn::SoundSoftware::authen_handler + SoundSoftwareDSN "DBI:mysql:database=redmine;host=localhost" + SoundSoftwareDbUser "redmine" + SoundSoftwareDbPass "password" + Options +ExecCGI + AddHandler cgi-script .cgi + ## Optional where clause (fulltext search would be slow and + ## database dependant). + # SoundSoftwareDbWhereClause "and members.role_id IN (1,2)" + ## Optional prefix for local repository URLs + # SoundSoftwareRepoPrefix "/var/hg/" + </Location> + +See the original Redmine.pm for further configuration notes. + +=cut + +use strict; +use warnings FATAL => 'all', NONFATAL => 'redefine'; + +use DBI; +use Digest::SHA1; +use Authen::Simple::LDAP; +use Apache2::Module; +use Apache2::Access; +use Apache2::ServerRec qw(); +use Apache2::RequestRec qw(); +use Apache2::RequestUtil qw(); +use Apache2::Const qw(:common :override :cmd_how); +use APR::Pool (); +use APR::Table (); + +my @directives = ( + { + name => 'SoundSoftwareDSN', + req_override => OR_AUTHCFG, + args_how => TAKE1, + errmsg => 'Dsn in format used by Perl DBI. eg: "DBI:Pg:dbname=databasename;host=my.db.server"', + }, + { + name => 'SoundSoftwareDbUser', + req_override => OR_AUTHCFG, + args_how => TAKE1, + }, + { + name => 'SoundSoftwareDbPass', + req_override => OR_AUTHCFG, + args_how => TAKE1, + }, + { + name => 'SoundSoftwareDbWhereClause', + req_override => OR_AUTHCFG, + args_how => TAKE1, + }, + { + name => 'SoundSoftwareRepoPrefix', + req_override => OR_AUTHCFG, + args_how => TAKE1, + }, +); + +sub SoundSoftwareDSN { + my ($self, $parms, $arg) = @_; + $self->{SoundSoftwareDSN} = $arg; + my $query = "SELECT + hashed_password, auth_source_id, permissions + FROM members, projects, users, roles, member_roles + WHERE + projects.id=members.project_id + AND member_roles.member_id=members.id + AND users.id=members.user_id + AND roles.id=member_roles.role_id + AND users.status=1 + AND login=? + AND identifier=? "; + $self->{SoundSoftwareQuery} = trim($query); +} + +sub SoundSoftwareDbUser { set_val('SoundSoftwareDbUser', @_); } +sub SoundSoftwareDbPass { set_val('SoundSoftwareDbPass', @_); } +sub SoundSoftwareDbWhereClause { + my ($self, $parms, $arg) = @_; + $self->{SoundSoftwareQuery} = trim($self->{SoundSoftwareQuery}.($arg ? $arg : "")." "); +} + +sub SoundSoftwareRepoPrefix { + my ($self, $parms, $arg) = @_; + if ($arg) { + $self->{SoundSoftwareRepoPrefix} = $arg; + } +} + +sub trim { + my $string = shift; + $string =~ s/\s{2,}/ /g; + return $string; +} + +sub set_val { + my ($key, $self, $parms, $arg) = @_; + $self->{$key} = $arg; +} + +Apache2::Module::add(__PACKAGE__, \@directives); + + +my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/; + +sub access_handler { + my $r = shift; + + print STDERR "SoundSoftware.pm: In access handler at " . scalar localtime() . "\n"; + + unless ($r->some_auth_required) { + $r->log_reason("No authentication has been configured"); + return FORBIDDEN; + } + + my $method = $r->method; + + print STDERR "SoundSoftware.pm: Method: $method, uri " . $r->uri . ", location " . $r->location . "\n"; + print STDERR "SoundSoftware.pm: Accept: " . $r->headers_in->{Accept} . "\n"; + + 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;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/soundsoftware/doxysafe.pl Thu Mar 03 12:11:53 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";
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/soundsoftware/extract-docs.sh Thu Mar 03 12:11:53 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"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/soundsoftware/extract-doxygen.sh Thu Mar 03 12:11:53 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 <project> <projectdir> <targetdir>" + exit 2 +fi + +if [ ! -d "$projectdir" ]; then + echo "Project directory $projectdir not found" + exit 1 +fi + +if [ ! -d "$targetdir" ]; then + echo "Target dir $targetdir not found" + exit 1 +fi + +if [ -f "$targetdir/index.html" ]; then + echo "Target dir $targetdir already contains index.html" + exit 1 +fi + +doxyfile=$(find "$projectdir" -type f -name Doxyfile -print | head -1) + +if [ -z "$doxyfile" ]; then + echo "No Doxyfile found for project $project" + exit 1 +fi + +echo "Project $project contains a Doxyfile at $doxyfile" + +cd "$projectdir" || exit 1 + +"$progdir/doxysafe.pl" "$doxyfile" | \ + sed -e '$a OUTPUT_DIRECTORY='"$targetdir" | \ + doxygen - +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/soundsoftware/extract-javadoc.sh Thu Mar 03 12:11:53 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 <project> <projectdir> <targetdir>" + exit 2 +fi + +if [ ! -d "$projectdir" ]; then + echo "Project directory $projectdir not found" + exit 1 +fi + +if [ ! -d "$targetdir" ]; then + echo "Target dir $targetdir not found" + exit 1 +fi + +if [ -f "$targetdir/index.html" ]; then + echo "Target dir $targetdir already contains index.html" + exit 1 +fi + +# Identify Java files whose packages match the trailing parts of their +# paths, and list the resulting packages and the path prefixes with +# the packages removed (so as to find code in subdirs, +# e.g. src/com/example/...) + +# Regexp match is very rough; check what is actually permitted for +# package declarations + +find "$projectdir" -type f -name \*.java \ + -exec 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" ] +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extra/soundsoftware/reposman-soundsoftware.rb Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,350 @@ +#!/usr/bin/env ruby + +# == Synopsis +# +# reposman: manages your repositories with Redmine +# +# == Usage +# +# reposman [OPTIONS...] -s [DIR] -r [HOST] +# +# Examples: +# reposman --svn-dir=/var/svn --redmine-host=redmine.example.net --scm subversion +# reposman -s /var/git -r redmine.example.net -u http://svn.example.net --scm git +# +# == Arguments (mandatory) +# +# -s, --svn-dir=DIR use DIR as base directory for svn repositories +# -r, --redmine-host=HOST assume Redmine is hosted on HOST. Examples: +# -r redmine.example.net +# -r http://redmine.example.net +# -r https://example.net/redmine +# -k, --key=KEY use KEY as the Redmine API key +# +# == Options +# +# -o, --owner=OWNER owner of the repository. using the rails login +# allow user to browse the repository within +# Redmine even for private project. If you want to +# share repositories through Redmine.pm, you need +# to use the apache owner. +# -g, --group=GROUP group of the repository. (default: root) +# --scm=SCM the kind of SCM repository you want to create (and +# register) in Redmine (default: Subversion). +# reposman is able to create Git and Subversion +# repositories. For all other kind, you must specify +# a --command option +# -u, --url=URL the base url Redmine will use to access your +# repositories. This option is used to automatically +# register the repositories in Redmine. The project +# identifier will be appended to this url. Examples: +# -u https://example.net/svn +# -u file:///var/svn/ +# if this option isn't set, reposman will register +# the repositories with local file paths in Redmine +# -c, --command=COMMAND use this command instead of "svnadmin create" to +# create a repository. This option can be used to +# create repositories other than subversion and git +# kind. +# This command override the default creation for git +# and subversion. +# --http-user=USER User for HTTP Basic authentication with Redmine WS +# --http-pass=PASSWORD Password for Basic authentication with Redmine WS +# -t, --test only show what should be done +# -h, --help show help and exit +# -v, --verbose verbose +# -V, --version print version and exit +# -q, --quiet no log +# +# == References +# +# You can find more information on the redmine's wiki : http://www.redmine.org/wiki/redmine/HowTos + + +require 'getoptlong' +require 'rdoc/usage' +require 'find' +require 'etc' + +Version = "1.3" +SUPPORTED_SCM = %w( Subversion Darcs Mercurial Bazaar Git Filesystem ) + +opts = GetoptLong.new( + ['--svn-dir', '-s', GetoptLong::REQUIRED_ARGUMENT], + ['--redmine-host', '-r', GetoptLong::REQUIRED_ARGUMENT], + ['--key', '-k', GetoptLong::REQUIRED_ARGUMENT], + ['--owner', '-o', GetoptLong::REQUIRED_ARGUMENT], + ['--group', '-g', GetoptLong::REQUIRED_ARGUMENT], + ['--url', '-u', GetoptLong::REQUIRED_ARGUMENT], + ['--command' , '-c', GetoptLong::REQUIRED_ARGUMENT], + ['--scm', GetoptLong::REQUIRED_ARGUMENT], + ['--http-user', GetoptLong::REQUIRED_ARGUMENT], + ['--http-pass', GetoptLong::REQUIRED_ARGUMENT], + ['--test', '-t', GetoptLong::NO_ARGUMENT], + ['--verbose', '-v', GetoptLong::NO_ARGUMENT], + ['--version', '-V', GetoptLong::NO_ARGUMENT], + ['--help' , '-h', GetoptLong::NO_ARGUMENT], + ['--quiet' , '-q', GetoptLong::NO_ARGUMENT] + ) + +$verbose = 0 +$quiet = false +$redmine_host = '' +$repos_base = '' +$http_user = '' +$http_pass = '' +$svn_owner = 'root' +$svn_group = 'root' +$use_groupid = true +$svn_url = false +$test = false +$scm = 'Subversion' + +def log(text, options={}) + level = options[:level] || 0 + puts text unless $quiet or level > $verbose + exit 1 if options[:exit] +end + +def system_or_raise(command) + raise "\"#{command}\" failed" unless system command +end + +module SCM + + module Subversion + def self.create(path) + system_or_raise "svnadmin create #{path}" + end + end + + module Git + def self.create(path) + Dir.mkdir path + Dir.chdir(path) do + system_or_raise "git --bare init --shared" + system_or_raise "git update-server-info" + end + end + end + +end + +begin + opts.each do |opt, arg| + case opt + when '--svn-dir'; $repos_base = arg.dup + when '--redmine-host'; $redmine_host = arg.dup + when '--key'; $api_key = arg.dup + when '--owner'; $svn_owner = arg.dup; $use_groupid = false; + when '--group'; $svn_group = arg.dup; $use_groupid = false; + when '--url'; $svn_url = arg.dup + when '--scm'; $scm = arg.dup.capitalize; log("Invalid SCM: #{$scm}", :exit => true) unless SUPPORTED_SCM.include?($scm) + when '--http-user'; $http_user = arg.dup + when '--http-pass'; $http_pass = arg.dup + when '--command'; $command = arg.dup + when '--verbose'; $verbose += 1 + when '--test'; $test = true + when '--version'; puts Version; exit + when '--help'; RDoc::usage + when '--quiet'; $quiet = true + end + end +rescue + exit 1 +end + +if $test + log("running in test mode") +end + +# Make sure command is overridden if SCM vendor is not handled internally (for the moment Subversion and Git) +if $command.nil? + begin + scm_module = SCM.const_get($scm) + rescue + log("Please use --command option to specify how to create a #{$scm} repository.", :exit => true) + end +end + +$svn_url += "/" if $svn_url and not $svn_url.match(/\/$/) + +if ($redmine_host.empty? or $repos_base.empty?) + RDoc::usage +end + +unless File.directory?($repos_base) + log("directory '#{$repos_base}' doesn't exists", :exit => true) +end + +begin + require 'active_resource' +rescue LoadError + log("This script requires activeresource.\nRun 'gem install activeresource' to install it.", :exit => true) +end + +class Project < ActiveResource::Base + self.headers["User-agent"] = "Redmine repository manager/#{Version}" +end + +log("querying Redmine for projects...", :level => 1); + +$redmine_host.gsub!(/^/, "http://") unless $redmine_host.match("^https?://") +$redmine_host.gsub!(/\/$/, '') + +Project.site = "#{$redmine_host}/sys"; +Project.user = $http_user; +Project.password = $http_pass; + +begin + # Get all active projects that have the Repository module enabled + projects = Project.find(:all, :params => {:key => $api_key}) +rescue => e + log("Unable to connect to #{Project.site}: #{e}", :exit => true) +end + +if projects.nil? + log('no project found, perhaps you forgot to "Enable WS for repository management"', :exit => true) +end + +log("retrieved #{projects.size} projects", :level => 1) + +def set_owner_and_rights(project, repos_path, &block) + if RUBY_PLATFORM =~ /mswin/ + yield if block_given? + else + uid, gid = Etc.getpwnam($svn_owner).uid, ($use_groupid ? Etc.getgrnam(project.identifier).gid : Etc.getgrnam($svn_group).gid) + right = project.is_public ? 02775 : 02770 + yield if block_given? + Find.find(repos_path) do |f| + File.chmod right, f + File.chown uid, gid, f + end + end +end + +def other_read_right?(file) + (File.stat(file).mode & 0007).zero? ? false : true +end + +def owner_name(file) + mswin? ? + $svn_owner : + Etc.getpwuid( File.stat(file).uid ).name +end + +def mswin? + (RUBY_PLATFORM =~ /(:?mswin|mingw)/) || (RUBY_PLATFORM == 'java' && (ENV['OS'] || ENV['os']) =~ /windows/i) +end + +projects.each do |project| + log("treating project #{project.name}", :level => 1) + + if project.identifier.empty? + log("\tno identifier for project #{project.name}") + next + elsif not project.identifier.match(/^[a-z0-9\-]+$/) + log("\tinvalid identifier for project #{project.name} : #{project.identifier}"); + next; + end + + repos_path = File.join($repos_base, project.identifier).gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR) + + create_repos = false + + # Logic required for SoundSoftware.ac.uk repositories: + # + # * If the project has a repository path declared already, + # - if it's a local path, + # - if it does not exist + # - if it has the right root + # - create it + # - else + # - leave alone (remote repository) + # * else + # - create repository with same name as project + # - set to project + + if project.respond_to?(:repository) + + repos_url = project.repository.url; + log("\texisting url for project #{project.identifier} is #{repos_url}"); + + if repos_url.match(/^file:\//) || repos_url.match(/^\//) + + repos_url = repos_url.gsub(/^file:\/*/, "/"); + log("\tthis is a local file path, at #{repos_url}"); + + if repos_url.slice(0, $repos_base.length) != $repos_base + log("\tit is in the wrong place: replacing it"); + # leave repos_path set to our original suggestion + create_repos = true + else + if !File.directory?(repos_url) + log("\tit doesn't exist; we should create it"); + repos_path = repos_url + create_repos = true + else + log("\tit exists and is in the right place"); + end + end + else + log("\tthis is a remote path, leaving alone"); + end + else + log("\tproject #{project.identifier} has no repository registered") +# if File.directory?(repos_path) +# log("\trepository path #{repos_path} already exists, not creating") +# else + create_repos = true +# end + end + + if create_repos + + registration_url = repos_path + if $svn_url + registration_url = "#{$svn_url}#{project.identifier}" + end + + if $test + log("\tproposal: create repository #{repos_path}") + log("\tproposal: register repository #{repos_path} in Redmine with vendor #{$scm}, url #{registration_url}") + next + end + +# No -- we need "other" users to be able to read it. Access control +# is not handled through Unix user id anyway +# project.is_public ? File.umask(0002) : File.umask(0007) + File.umask(0002) + + log("\taction: create repository #{repos_path}") + + begin + if !File.directory?(repos_path) + set_owner_and_rights(project, repos_path) do + if scm_module.nil? + log("\trunning command: #{$command} #{repos_path}") + system_or_raise "#{$command} #{repos_path}" + else + scm_module.create(repos_path) + end + end + end + rescue => e + log("\tunable to create #{repos_path} : #{e}\n") + next + end + + begin + log("\taction: register repository #{repos_path} in Redmine with vendor #{$scm}, url #{registration_url}"); + project.post(:repository, :vendor => $scm, :repository => {:url => "#{registration_url}"}, :key => $api_key) + rescue => e + log("\trepository #{repos_path} not registered in Redmine: #{e.message}"); + end + + log("\trepository #{repos_path} created"); + end + +end +
--- a/extra/svn/SoundSoftware.pm Thu Mar 03 12:02:03 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/" - - <Location /> - AuthName "Mercurial" - AuthType Basic - Require valid-user - PerlAccessHandler Apache::Authn::SoundSoftware::access_handler - PerlAuthenHandler Apache::Authn::SoundSoftware::authen_handler - SoundSoftwareDSN "DBI:mysql:database=redmine;host=localhost" - SoundSoftwareDbUser "redmine" - SoundSoftwareDbPass "password" - Options +ExecCGI - AddHandler cgi-script .cgi - ## Optional where clause (fulltext search would be slow and - ## database dependant). - # SoundSoftwareDbWhereClause "and members.role_id IN (1,2)" - ## Optional prefix for local repository URLs - # SoundSoftwareRepoPrefix "/var/hg/" - </Location> - -See the original Redmine.pm for further configuration notes. - -=cut - -use strict; -use warnings FATAL => 'all', NONFATAL => 'redefine'; - -use DBI; -use Digest::SHA1; -use Authen::Simple::LDAP; -use Apache2::Module; -use Apache2::Access; -use Apache2::ServerRec qw(); -use Apache2::RequestRec qw(); -use Apache2::RequestUtil qw(); -use Apache2::Const qw(:common :override :cmd_how); -use APR::Pool (); -use APR::Table (); - -my @directives = ( - { - name => 'SoundSoftwareDSN', - req_override => OR_AUTHCFG, - args_how => TAKE1, - errmsg => 'Dsn in format used by Perl DBI. eg: "DBI:Pg:dbname=databasename;host=my.db.server"', - }, - { - name => 'SoundSoftwareDbUser', - req_override => OR_AUTHCFG, - args_how => TAKE1, - }, - { - name => 'SoundSoftwareDbPass', - req_override => OR_AUTHCFG, - args_how => TAKE1, - }, - { - name => 'SoundSoftwareDbWhereClause', - req_override => OR_AUTHCFG, - args_how => TAKE1, - }, - { - name => 'SoundSoftwareRepoPrefix', - req_override => OR_AUTHCFG, - args_how => TAKE1, - }, -); - -sub SoundSoftwareDSN { - my ($self, $parms, $arg) = @_; - $self->{SoundSoftwareDSN} = $arg; - my $query = "SELECT - hashed_password, auth_source_id, permissions - FROM members, projects, users, roles, member_roles - WHERE - projects.id=members.project_id - AND member_roles.member_id=members.id - AND users.id=members.user_id - AND roles.id=member_roles.role_id - AND users.status=1 - AND login=? - AND identifier=? "; - $self->{SoundSoftwareQuery} = trim($query); -} - -sub SoundSoftwareDbUser { set_val('SoundSoftwareDbUser', @_); } -sub SoundSoftwareDbPass { set_val('SoundSoftwareDbPass', @_); } -sub SoundSoftwareDbWhereClause { - my ($self, $parms, $arg) = @_; - $self->{SoundSoftwareQuery} = trim($self->{SoundSoftwareQuery}.($arg ? $arg : "")." "); -} - -sub SoundSoftwareRepoPrefix { - my ($self, $parms, $arg) = @_; - if ($arg) { - $self->{SoundSoftwareRepoPrefix} = $arg; - } -} - -sub trim { - my $string = shift; - $string =~ s/\s{2,}/ /g; - return $string; -} - -sub set_val { - my ($key, $self, $parms, $arg) = @_; - $self->{$key} = $arg; -} - -Apache2::Module::add(__PACKAGE__, \@directives); - - -my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/; - -sub access_handler { - my $r = shift; - - print STDERR "SoundSoftware.pm: In access handler\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;
--- a/extra/svn/reposman-soundsoftware.rb Thu Mar 03 12:02:03 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 -
--- a/public/javascripts/application.js Thu Mar 03 12:02:03 2011 +0000 +++ b/public/javascripts/application.js Thu Mar 03 12:11:53 2011 +0000 @@ -320,3 +320,7 @@ } Event.observe(window, 'load', hideOnLoad); + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/public/javascripts/ssamr_institutions.js Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,32 @@ + + +/* SSAMR specific functions */ + +/* institution related functions */ +Event.observe(window, 'load', + function() { + $('ssamr_user_details_institution_type_true').observe('click', function(e, el) { + $('ssamr_user_details_other_institution').disable(); + $('ssamr_user_details_institution_id').enable(); + }); + + $('ssamr_user_details_institution_type_false').observe('click', function(e, el) { + $('ssamr_user_details_other_institution').enable(); + $('ssamr_user_details_institution_id').disable(); + }); + + if($('ssamr_user_details_institution_type_true').checked) + $('ssamr_user_details_other_institution').disable(); + else if($('ssamr_user_details_institution_type_false').checked) + $('ssamr_user_details_institution_id').disable(); + else { + $('ssamr_user_details_other_institution').disable(); + $('ssamr_user_details_institution_id').enable(); + $('ssamr_user_details_institution_type_true').checked = true; + $('ssamr_user_details_institution_type_false').checked = false; + } +} +); + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/public/javascripts/ssamr_registration.js Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,19 @@ + + +/* SSAMR specific functions */ + +/* institution related functions */ +Event.observe(window, 'load', + function() { + + if(!$('ssamr_user_details_institution_type_true').checked && $('ssamr_user_details_institution_type_true').checked){ + $('ssamr_user_details_other_institution').disable(); + $('ssamr_user_details_institution_id').enable(); + $('ssamr_user_details_institution_type_true').checked = true; + $('ssamr_user_details_institution_type_false').checked = false; + } + } +); + + +
--- a/public/stylesheets/application.css Thu Mar 03 12:02:03 2011 +0000 +++ b/public/stylesheets/application.css Thu Mar 03 12:11:53 2011 +0000 @@ -27,7 +27,9 @@ #header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;} #header a {color:#f8f8f8;} #header h1 a.ancestor { font-size: 80%; } -#quick-search {float:right;} + +#project-search-jump {float:right; } + #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;} #main-menu ul {margin: 0; padding: 0;}
--- a/public/themes/soundsoftware/stylesheets/application.css Thu Mar 03 12:02:03 2011 +0000 +++ b/public/themes/soundsoftware/stylesheets/application.css Thu Mar 03 12:11:53 2011 +0000 @@ -26,7 +26,7 @@ color: #000; margin: 0; margin-bottom: 40px; -/* font-size: 95%; */ + min-width: 620px; } h1 { @@ -36,7 +36,7 @@ } body,p,h2,h3,h4,li,table,.wiki h1 { - font-family: DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; */ + font-family: DroidSans, 'Liberation Sans', tahoma, verdana, sans-serif; } h2,h3,h4,.wiki h1 { @@ -69,6 +69,9 @@ table.projects th { text-align: left; } table.projects th.managers { color: #3e442c; } +table.projects .root .name { font-size: 1.2em; padding-top: 0.3em; } +table.projects .description { padding-bottom: 0.5em; } +table.projects .no_description { padding-bottom: 0.5em; } table.projects .hosted_here { font-weight: bold; } table.projects .child .name { font-weight: normal; } table.projects .child .description { font-size: 0.95em; } @@ -85,12 +88,22 @@ #header a { color: #be5700; } #header h1 { color: #525a38; margin-top: 25px; font-size: 3em; font-weight: normal; margin-left: 10px; } .header-general h1 { - background: url('soundsoftware-logo-title-only-transparent.png') no-repeat 0 0; + background: url('soundsoftware-logo-title-only-transparent-beta.png') no-repeat 0 0; text-indent: -9999px; - width: 446px; + width: 500px; height: 34px; } + #quick-search { margin-right: 6px; margin-top: 1em; color: #000; } +#project-jump-box { float: right; margin-right: 6px; margin-top: 5px; color: #000; } +#project-ancestors-title { + margin-bottom: 0px; + margin-left: 10px; + margin-top: 6px; + font-family: GilliusADFNo2, 'Gill Sans', Tahoma, sans-serif; + font-weight: normal; +} + #main-menu { position: absolute; top: 100px; /* background-color: #be5700; */ left: 0; border-top: 0; width: 100%;/* height: 1.82em; */ padding: 0; margin: 0; border: 0; } #main-menu li { margin: 0; padding: 0; } #main-menu li a { background-color: #fdfbf5; color: #be5700; border-right: 1px solid #a9b680; font-size: 97%; padding: 0em 8px 0.2em 10px; font-weight: normal; } @@ -127,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 */ + + +
Binary file public/themes/soundsoftware/stylesheets/soundsoftware-logo-title-only-transparent-beta.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/fixtures/institutions.yml Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,7 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html + +one: + name: MyString + +two: + name: MyString
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/unit/institution_test.rb Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,8 @@ +require 'test_helper' + +class InstitutionTest < ActiveSupport::TestCase + # Replace this with your real tests. + test "the truth" do + assert true + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/COPYING Thu Mar 03 12:11:53 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/README Thu Mar 03 12:11:53 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/RUNNING_TESTS Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,1 @@ +rake test:plugins PLUGIN=embedded
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/app/controllers/embedded_controller.rb Thu Mar 03 12:11:53 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{<title>([^<]*)</title>}mi + @title = $1.strip + end + + # Keep html body only + @content.gsub!(%r{^.*<body[^>]*>(.*)</body>.*$}mi, '\\1') + + # Re-encode content if needed + source_encoding = Setting.plugin_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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/app/helpers/embedded_helper.rb Thu Mar 03 12:11:53 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/app/views/embedded/index.rhtml Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,9 @@ +<!-- Embedded page --> +<div class="embedded"> +<%= @content %> +</div> +<!-- Embedded page end --> + +<% html_title(@title) if @title %> + +<% asset_include_tags(@doc_template) %>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/app/views/settings/_embedded.rhtml Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,22 @@ +<p><label>HTML directory</label> +<%= text_field_tag 'settings[path]', @settings['path'], :size => 80 %> +<br /><em>Use {PROJECT} to include the project identifier in the path</em></p> + +<p><label>Index files</label> +<%= text_area_tag 'settings[index]', @settings['index'], :cols => 60, :rows => 3 %> +<br /><em>Space separated list of index files by priority</em></p> + +<p><label>Valid extensions</label> +<%= text_area_tag 'settings[extensions]', @settings['extensions'], :cols => 60, :rows => 3 %> +<br /><em>Space separated list of file extensions that can be viewed (case insensitive)</em></p> + +<p><label>Default template</label> +<%= select_tag 'settings[template]', options_for_select([''] + Redmine::Plugins::Embedded.available_templates, @settings['template']) %></p> + +<p><label>Files encoding</label> +<%= text_field_tag 'settings[encoding]', @settings['encoding'] %> +<br /><em>Eg. ISO-8859-1<br />Leave this field empty if HTML files are UTF-8 encoded</em></p> + +<p><label>Menu caption</label> +<%= text_field_tag 'settings[menu]', @settings['menu'], :size => 30 %> +<br /><em>Clear this field if you don't want to add a tab to the project menu</em></p>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/assets/javascripts/rcov.js Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,18 @@ +function toggleCode( id ) { + if ( document.getElementById ) + elem = document.getElementById( id ); + else if ( document.all ) + elem = eval( "document.all." + id ); + else + return false; + + elemStyle = elem.style; + + if ( elemStyle.display != "block" ) { + elemStyle.display = "block" + } else { + elemStyle.display = "none" + } + + return true; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/assets/stylesheets/doxygen.css Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,174 @@ +#content { + font-family: Geneva, Arial, Helvetica, sans-serif; +} +#content h1, h2, h3 {border: 0;} +#content h1 {margin-bottom: 1em;} +#content CAPTION { font-weight: bold } +#content DIV.qindex { + line-height: 120%; + margin-bottom: 1em; +} +#content A.qindex { + text-decoration: none; + font-weight: bold; + padding: 2px; +} +#content A.qindexHL { + text-decoration: underline; + font-weight: bold; + padding: 2px; +} +#content A.el { text-decoration: none; font-weight: bold } +#content A.elRef { font-weight: bold } +#content A.code { text-decoration: none; font-weight: normal; color: #1A419D} +#content A.codeRef { font-weight: normal; color: #1A419D} +#content DL.el { margin-left: -1cm } +#content PRE.fragment { + border: 1px solid #CCCCCC; + background-color: #f5f5f5; + margin-top: 4px; + margin-bottom: 4px; + margin-left: 2px; + margin-right: 8px; + padding-left: 6px; + padding-right: 6px; + padding-top: 4px; + padding-bottom: 4px; +} +#content DIV.fragment { + border: 1px solid #CCCCCC; + background-color: #f5f5f5; + padding: 6px; +} +#content DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px } +#content TD.md { background-color: #F4F4FB; font-weight: bold; } +#content TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; } +#content TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; } +#content DIV.groupHeader { + margin-left: 16px; + margin-top: 12px; + margin-bottom: 6px; + font-weight: bold; + font-family: Geneva, Arial, Helvetica, sans-serif; +} +#content DIV.groupText { margin-left: 16px; font-style: italic; font-size: 14px } +#content TD.indexkey { + background-color: #eeeeff; + font-weight: bold; + padding-right : 10px; + padding-top : 2px; + padding-left : 10px; + padding-bottom : 2px; + margin-left : 0px; + margin-right : 0px; + margin-top : 2px; + margin-bottom : 2px; + border: 1px solid #CCCCCC; +} +#content TD.indexvalue { + background-color: #eeeeff; + font-style: italic; + padding-right : 10px; + padding-top : 2px; + padding-left : 10px; + padding-bottom : 2px; + margin-left : 0px; + margin-right : 0px; + margin-top : 2px; + margin-bottom : 2px; + border: 1px solid #CCCCCC; +} +#content TR.memlist { + background-color: #f0f0f0; +} +#content P.formulaDsp { text-align: center; } +#content IMG.formulaDsp { } +#content IMG.formulaInl { vertical-align: middle; } +#content SPAN.keyword { color: #008000 } +#content SPAN.keywordtype { color: #604020 } +#content SPAN.keywordflow { color: #e08000 } +#content SPAN.comment { color: #800000 } +#content SPAN.preprocessor { color: #806020 } +#content SPAN.stringliteral { color: #002080 } +#content SPAN.charliteral { color: #008080 } +#content .mdTable { + border: 1px solid #868686; + background-color: #F4F4FB; +} +#content .mdRow { + padding: 8px 10px; +} +#content .mdescLeft { + padding: 0px 8px 4px 8px; + font-size: 14px; + font-style: italic; + background-color: #FAFAFA; + border-top: 1px none #E0E0E0; + border-right: 1px none #E0E0E0; + border-bottom: 1px none #E0E0E0; + border-left: 1px none #E0E0E0; + margin: 0px; +} +#content .mdescRight { + padding: 0px 8px 4px 8px; + font-size: 14px; + font-style: italic; + background-color: #FAFAFA; + border-top: 1px none #E0E0E0; + border-right: 1px none #E0E0E0; + border-bottom: 1px none #E0E0E0; + border-left: 1px none #E0E0E0; + margin: 0px; +} +#content .memItemLeft { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-style: solid; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-family: Geneva, Arial, Helvetica, sans-serif; + font-size: 12px; +} +#content .memItemRight { + padding: 1px 8px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-style: solid; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-family: Geneva, Arial, Helvetica, sans-serif; + font-size: 13px; +} +#content .search { color: #003399; + font-weight: bold; +} +#content FORM.search { + margin-bottom: 0px; + margin-top: 0px; +} +#content INPUT.search { font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #eeeeff; +} +#content TD.tiny { font-size: 75%; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/assets/stylesheets/javadoc.css Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,23 @@ +/* Javadoc style sheet */ + +/* Table colors */ +.TableHeadingColor { background: #eef; } /* Dark mauve */ +.TableHeadingColor th b { font-size: 70%; } +.TableSubHeadingColor { background: #EEEEee; } /* Light mauve */ +.TableRowColor { background: #FFFFFF; } /* White */ + +/* Font used in left-hand frame lists */ +.FrameTitleFont { font-size: 100%; font-family: Helvetica, Arial, sans-serif; color:#000000 } +.FrameHeadingFont { font-size: 90%; font-family: Helvetica, Arial, sans-serif; color:#000000 } +.FrameItemFont { font-size: 90%; font-family: Helvetica, Arial, sans-serif; color:#000000 } + +/* Navigation bar fonts and colors */ +.NavBarCell1 { background-color: inherit; } /* Light mauve */ +.NavBarCell1Rev { background-color: inherit; } /* Dark Blue */ +.NavBarFont1 { font-family: Arial, Helvetica, sans-serif;} +.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; font-style: underline; } + +.NavBarCell2 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF; color:#000000; display: none;} +.NavBarCell3 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF; color:#000000} + +#content table { border-collapse: collapse; border-color: #ddd; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/assets/stylesheets/rcov.css Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,525 @@ +span.cross-ref-title { + font-size: 140%; +} +span.cross-ref a { + text-decoration: none; +} +span.cross-ref { + background-color:#f3f7fa; + border: 1px dashed #333; + margin: 1em; + padding: 0.5em; + overflow: hidden; +} +a.crossref-toggle { + text-decoration: none; +} +span.marked0 { + background-color: rgb(185, 210, 200); + display: block; +} +span.marked1 { + background-color: rgb(190, 215, 205); + display: block; +} +span.inferred0 { + background-color: rgb(175, 200, 200); + display: block; +} +span.inferred1 { + background-color: rgb(180, 205, 205); + display: block; +} +span.uncovered0 { + background-color: rgb(225, 110, 110); + display: block; +} +span.uncovered1 { + background-color: rgb(235, 120, 120); + display: block; +} +span.overview { + border-bottom: 8px solid black; +} +div.overview { + border-bottom: 8px solid black; +} +#content div.footer { + font-size: 68%; + margin-top: 1.5em; +} +#content h1, h2, h3, h4, h5, h6 { + margin-bottom: 0.5em; +} +h5 { + margin-top: 0.5em; +} +.hidden { + display: none; +} +div.separator { + height: 10px; +} +/* Commented out for better readability, esp. on IE */ +/* +table tr td, table tr th { + font-size: 68%; +} +td.value table tr td { + font-size: 11px; +} +*/ +table.percent_graph { + height: 12px; + border: #808080 1px solid; + empty-cells: show; +} +table.percent_graph td.covered { + height: 10px; + background: #00f000; +} +table.percent_graph td.uncovered { + height: 10px; + background: #e00000; +} +table.percent_graph td.NA { + height: 10px; + background: #eaeaea; +} +table.report { + border-collapse: collapse; + width: 100%; +} +table.report td.heading { + background: #dcecff; + border: #d0d0d0 1px solid; + font-weight: bold; + text-align: center; +} +table.report td.heading:hover { + background: #c0ffc0; +} +table.report td.text { + border: #d0d0d0 1px solid; +} +table.report td.value, +table.report td.lines_total, +table.report td.lines_code { + text-align: right; + border: #d0d0d0 1px solid; +} +table.report tr.light { + background-color: #f6f7f8; +} +table.report tr.dark { + background-color: #fff; +} +span.run0 { + background-color: rgb(178, 204, 255); + display: block; +} +span.run1 { + background-color: rgb(178, 206, 255); + display: block; +} +span.run2 { + background-color: rgb(178, 209, 255); + display: block; +} +span.run3 { + background-color: rgb(178, 211, 255); + display: block; +} +span.run4 { + background-color: rgb(178, 214, 255); + display: block; +} +span.run5 { + background-color: rgb(178, 218, 255); + display: block; +} +span.run6 { + background-color: rgb(178, 220, 255); + display: block; +} +span.run7 { + background-color: rgb(178, 223, 255); + display: block; +} +span.run8 { + background-color: rgb(178, 225, 255); + display: block; +} +span.run9 { + background-color: rgb(178, 228, 255); + display: block; +} +span.run10 { + background-color: rgb(178, 232, 255); + display: block; +} +span.run11 { + background-color: rgb(178, 234, 255); + display: block; +} +span.run12 { + background-color: rgb(178, 237, 255); + display: block; +} +span.run13 { + background-color: rgb(178, 239, 255); + display: block; +} +span.run14 { + background-color: rgb(178, 242, 255); + display: block; +} +span.run15 { + background-color: rgb(178, 246, 255); + display: block; +} +span.run16 { + background-color: rgb(178, 248, 255); + display: block; +} +span.run17 { + background-color: rgb(178, 251, 255); + display: block; +} +span.run18 { + background-color: rgb(178, 253, 255); + display: block; +} +span.run19 { + background-color: rgb(178, 255, 253); + display: block; +} +span.run20 { + background-color: rgb(178, 255, 249); + display: block; +} +span.run21 { + background-color: rgb(178, 255, 247); + display: block; +} +span.run22 { + background-color: rgb(178, 255, 244); + display: block; +} +span.run23 { + background-color: rgb(178, 255, 242); + display: block; +} +span.run24 { + background-color: rgb(178, 255, 239); + display: block; +} +span.run25 { + background-color: rgb(178, 255, 235); + display: block; +} +span.run26 { + background-color: rgb(178, 255, 233); + display: block; +} +span.run27 { + background-color: rgb(178, 255, 230); + display: block; +} +span.run28 { + background-color: rgb(178, 255, 228); + display: block; +} +span.run29 { + background-color: rgb(178, 255, 225); + display: block; +} +span.run30 { + background-color: rgb(178, 255, 221); + display: block; +} +span.run31 { + background-color: rgb(178, 255, 219); + display: block; +} +span.run32 { + background-color: rgb(178, 255, 216); + display: block; +} +span.run33 { + background-color: rgb(178, 255, 214); + display: block; +} +span.run34 { + background-color: rgb(178, 255, 211); + display: block; +} +span.run35 { + background-color: rgb(178, 255, 207); + display: block; +} +span.run36 { + background-color: rgb(178, 255, 205); + display: block; +} +span.run37 { + background-color: rgb(178, 255, 202); + display: block; +} +span.run38 { + background-color: rgb(178, 255, 200); + display: block; +} +span.run39 { + background-color: rgb(178, 255, 197); + display: block; +} +span.run40 { + background-color: rgb(178, 255, 193); + display: block; +} +span.run41 { + background-color: rgb(178, 255, 191); + display: block; +} +span.run42 { + background-color: rgb(178, 255, 188); + display: block; +} +span.run43 { + background-color: rgb(178, 255, 186); + display: block; +} +span.run44 { + background-color: rgb(178, 255, 183); + display: block; +} +span.run45 { + background-color: rgb(178, 255, 179); + display: block; +} +span.run46 { + background-color: rgb(179, 255, 178); + display: block; +} +span.run47 { + background-color: rgb(182, 255, 178); + display: block; +} +span.run48 { + background-color: rgb(184, 255, 178); + display: block; +} +span.run49 { + background-color: rgb(187, 255, 178); + display: block; +} +span.run50 { + background-color: rgb(191, 255, 178); + display: block; +} +span.run51 { + background-color: rgb(193, 255, 178); + display: block; +} +span.run52 { + background-color: rgb(196, 255, 178); + display: block; +} +span.run53 { + background-color: rgb(198, 255, 178); + display: block; +} +span.run54 { + background-color: rgb(201, 255, 178); + display: block; +} +span.run55 { + background-color: rgb(205, 255, 178); + display: block; +} +span.run56 { + background-color: rgb(207, 255, 178); + display: block; +} +span.run57 { + background-color: rgb(210, 255, 178); + display: block; +} +span.run58 { + background-color: rgb(212, 255, 178); + display: block; +} +span.run59 { + background-color: rgb(215, 255, 178); + display: block; +} +span.run60 { + background-color: rgb(219, 255, 178); + display: block; +} +span.run61 { + background-color: rgb(221, 255, 178); + display: block; +} +span.run62 { + background-color: rgb(224, 255, 178); + display: block; +} +span.run63 { + background-color: rgb(226, 255, 178); + display: block; +} +span.run64 { + background-color: rgb(229, 255, 178); + display: block; +} +span.run65 { + background-color: rgb(233, 255, 178); + display: block; +} +span.run66 { + background-color: rgb(235, 255, 178); + display: block; +} +span.run67 { + background-color: rgb(238, 255, 178); + display: block; +} +span.run68 { + background-color: rgb(240, 255, 178); + display: block; +} +span.run69 { + background-color: rgb(243, 255, 178); + display: block; +} +span.run70 { + background-color: rgb(247, 255, 178); + display: block; +} +span.run71 { + background-color: rgb(249, 255, 178); + display: block; +} +span.run72 { + background-color: rgb(252, 255, 178); + display: block; +} +span.run73 { + background-color: rgb(255, 255, 178); + display: block; +} +span.run74 { + background-color: rgb(255, 252, 178); + display: block; +} +span.run75 { + background-color: rgb(255, 248, 178); + display: block; +} +span.run76 { + background-color: rgb(255, 246, 178); + display: block; +} +span.run77 { + background-color: rgb(255, 243, 178); + display: block; +} +span.run78 { + background-color: rgb(255, 240, 178); + display: block; +} +span.run79 { + background-color: rgb(255, 238, 178); + display: block; +} +span.run80 { + background-color: rgb(255, 234, 178); + display: block; +} +span.run81 { + background-color: rgb(255, 232, 178); + display: block; +} +span.run82 { + background-color: rgb(255, 229, 178); + display: block; +} +span.run83 { + background-color: rgb(255, 226, 178); + display: block; +} +span.run84 { + background-color: rgb(255, 224, 178); + display: block; +} +span.run85 { + background-color: rgb(255, 220, 178); + display: block; +} +span.run86 { + background-color: rgb(255, 218, 178); + display: block; +} +span.run87 { + background-color: rgb(255, 215, 178); + display: block; +} +span.run88 { + background-color: rgb(255, 212, 178); + display: block; +} +span.run89 { + background-color: rgb(255, 210, 178); + display: block; +} +span.run90 { + background-color: rgb(255, 206, 178); + display: block; +} +span.run91 { + background-color: rgb(255, 204, 178); + display: block; +} +span.run92 { + background-color: rgb(255, 201, 178); + display: block; +} +span.run93 { + background-color: rgb(255, 198, 178); + display: block; +} +span.run94 { + background-color: rgb(255, 196, 178); + display: block; +} +span.run95 { + background-color: rgb(255, 192, 178); + display: block; +} +span.run96 { + background-color: rgb(255, 189, 178); + display: block; +} +span.run97 { + background-color: rgb(255, 187, 178); + display: block; +} +span.run98 { + background-color: rgb(255, 184, 178); + display: block; +} +span.run99 { + background-color: rgb(255, 182, 178); + display: block; +} +span.run100 { + background-color: rgb(255, 178, 178); + display: block; +} +pre { + white-space: pre-wrap; /* CSS2.1 compliant */ + white-space: -moz-pre-wrap; /* Mozilla-based browsers */ + white-space: -o-pre-wrap; /* Opera 7+ */ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/init.rb Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,33 @@ +require 'redmine' +require 'embedded' + +Redmine::Plugin.register :embedded do + name 'Embedded' + author 'Jean-Philippe Lang' + description 'Embed various documentations in your projects' + version '0.0.1' + settings :default => { 'path' => '/var/doc/{PROJECT}/html', + 'index' => 'main.html overview-summary.html index.html', + 'extensions' => 'html png gif', + 'template' => '', + 'encoding' => '', + 'menu' => 'Embedded' }, + :partial => 'settings/embedded' + + project_module :embedded do + permission :view_embedded_doc, {:embedded => :index} + end + + menu :project_menu, :embedded, { :controller => 'embedded', :action => 'index', :path => nil }, + :caption => Proc.new { Setting.plugin_embedded['menu'] }, + :if => Proc.new { !Setting.plugin_embedded['menu'].blank? } +end + +# Routes +class << ActionController::Routing::Routes;self;end.class_eval do + define_method :clear!, lambda {} +end + +ActionController::Routing::Routes.draw do |map| + map.connect 'embedded/:id/*path', :controller => 'embedded', :action => 'index' +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/lib/embedded.rb Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,64 @@ +# Redmine - project management software +# Copyright (C) 2008 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module Redmine + module Plugins + module Embedded + class << self + + # Returns an Array of available templates + def available_templates + assets_by_template.keys.sort + end + + # Returns the assets for a given template + def assets(template) + assets_by_template.has_key?(template) ? assets_by_template[template] : [] + end + + def detect_template_from_path(path) + t = path.to_s.split(%r{[/\\]}) & available_templates + t.empty? ? Setting.plugin_embedded['template'].to_s : t.first + end + + def valid_extension?(path) + extensions = Setting.plugin_embedded['extensions'].to_s.split.each(&:downcase) + extensions.include?(File.extname(path).downcase[1..-1]) + end + + private + + # A Hash of available assets by template + def assets_by_template + @@assets_by_template ||= scan_assets + end + + # Scans assets directory for templates + # and returns a Hash of available assets by template + def scan_assets + a = Hash.new {|h,k| h[k] = [] } + Dir.glob(File.join(File.dirname(__FILE__), '../assets/*/*.{css,js}')).each do |asset| + asset = File.basename(asset) + template = asset.gsub(%r{\.(js|css)$}, '') + a[template] << asset + end + a + end + end + end + end +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/test/fixtures/html/app-controllers-account_controller_rb.html Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,783 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'><head><title>app/controllers/account_controller.rb - C0 code coverage information</title> + <style type='text/css'>body { background-color: rgb(240, 240, 245); }</style> + <style type='text/css'>span.cross-ref-title { + font-size: 140%; +} +span.cross-ref a { + text-decoration: none; +} +span.cross-ref { + background-color:#f3f7fa; + border: 1px dashed #333; + margin: 1em; + padding: 0.5em; + overflow: hidden; +} +a.crossref-toggle { + text-decoration: none; +} +span.marked0 { + background-color: rgb(185, 210, 200); + display: block; +} +span.marked1 { + background-color: rgb(190, 215, 205); + display: block; +} +span.inferred0 { + background-color: rgb(175, 200, 200); + display: block; +} +span.inferred1 { + background-color: rgb(180, 205, 205); + display: block; +} +span.uncovered0 { + background-color: rgb(225, 110, 110); + display: block; +} +span.uncovered1 { + background-color: rgb(235, 120, 120); + display: block; +} +span.overview { + border-bottom: 8px solid black; +} +div.overview { + border-bottom: 8px solid black; +} +body { + font-family: verdana, arial, helvetica; +} +div.footer { + font-size: 68%; + margin-top: 1.5em; +} +h1, h2, h3, h4, h5, h6 { + margin-bottom: 0.5em; +} +h5 { + margin-top: 0.5em; +} +.hidden { + display: none; +} +div.separator { + height: 10px; +} +/* Commented out for better readability, esp. on IE */ +/* +table tr td, table tr th { + font-size: 68%; +} +td.value table tr td { + font-size: 11px; +} +*/ +table.percent_graph { + height: 12px; + border: #808080 1px solid; + empty-cells: show; +} +table.percent_graph td.covered { + height: 10px; + background: #00f000; +} +table.percent_graph td.uncovered { + height: 10px; + background: #e00000; +} +table.percent_graph td.NA { + height: 10px; + background: #eaeaea; +} +table.report { + border-collapse: collapse; + width: 100%; +} +table.report td.heading { + background: #dcecff; + border: #d0d0d0 1px solid; + font-weight: bold; + text-align: center; +} +table.report td.heading:hover { + background: #c0ffc0; +} +table.report td.text { + border: #d0d0d0 1px solid; +} +table.report td.value, +table.report td.lines_total, +table.report td.lines_code { + text-align: right; + border: #d0d0d0 1px solid; +} +table.report tr.light { + background-color: rgb(240, 240, 245); +} +table.report tr.dark { + background-color: rgb(230, 230, 235); +} +</style> + <script type='text/javascript'> +// <![CDATA[ + function toggleCode( id ) { + if ( document.getElementById ) + elem = document.getElementById( id ); + else if ( document.all ) + elem = eval( "document.all." + id ); + else + return false; + + elemStyle = elem.style; + + if ( elemStyle.display != "block" ) { + elemStyle.display = "block" + } else { + elemStyle.display = "none" + } + + return true; + } + + // Make cross-references hidden by default + document.writeln( "<style type=\"text/css\">span.cross-ref { display: none }</style>" ) + // ]]> +</script> + <style type='text/css'>span.run0 { + background-color: rgb(178, 204, 255); + display: block; +} +span.run1 { + background-color: rgb(178, 206, 255); + display: block; +} +span.run2 { + background-color: rgb(178, 209, 255); + display: block; +} +span.run3 { + background-color: rgb(178, 211, 255); + display: block; +} +span.run4 { + background-color: rgb(178, 214, 255); + display: block; +} +span.run5 { + background-color: rgb(178, 218, 255); + display: block; +} +span.run6 { + background-color: rgb(178, 220, 255); + display: block; +} +span.run7 { + background-color: rgb(178, 223, 255); + display: block; +} +span.run8 { + background-color: rgb(178, 225, 255); + display: block; +} +span.run9 { + background-color: rgb(178, 228, 255); + display: block; +} +span.run10 { + background-color: rgb(178, 232, 255); + display: block; +} +span.run11 { + background-color: rgb(178, 234, 255); + display: block; +} +span.run12 { + background-color: rgb(178, 237, 255); + display: block; +} +span.run13 { + background-color: rgb(178, 239, 255); + display: block; +} +span.run14 { + background-color: rgb(178, 242, 255); + display: block; +} +span.run15 { + background-color: rgb(178, 246, 255); + display: block; +} +span.run16 { + background-color: rgb(178, 248, 255); + display: block; +} +span.run17 { + background-color: rgb(178, 251, 255); + display: block; +} +span.run18 { + background-color: rgb(178, 253, 255); + display: block; +} +span.run19 { + background-color: rgb(178, 255, 253); + display: block; +} +span.run20 { + background-color: rgb(178, 255, 249); + display: block; +} +span.run21 { + background-color: rgb(178, 255, 247); + display: block; +} +span.run22 { + background-color: rgb(178, 255, 244); + display: block; +} +span.run23 { + background-color: rgb(178, 255, 242); + display: block; +} +span.run24 { + background-color: rgb(178, 255, 239); + display: block; +} +span.run25 { + background-color: rgb(178, 255, 235); + display: block; +} +span.run26 { + background-color: rgb(178, 255, 233); + display: block; +} +span.run27 { + background-color: rgb(178, 255, 230); + display: block; +} +span.run28 { + background-color: rgb(178, 255, 228); + display: block; +} +span.run29 { + background-color: rgb(178, 255, 225); + display: block; +} +span.run30 { + background-color: rgb(178, 255, 221); + display: block; +} +span.run31 { + background-color: rgb(178, 255, 219); + display: block; +} +span.run32 { + background-color: rgb(178, 255, 216); + display: block; +} +span.run33 { + background-color: rgb(178, 255, 214); + display: block; +} +span.run34 { + background-color: rgb(178, 255, 211); + display: block; +} +span.run35 { + background-color: rgb(178, 255, 207); + display: block; +} +span.run36 { + background-color: rgb(178, 255, 205); + display: block; +} +span.run37 { + background-color: rgb(178, 255, 202); + display: block; +} +span.run38 { + background-color: rgb(178, 255, 200); + display: block; +} +span.run39 { + background-color: rgb(178, 255, 197); + display: block; +} +span.run40 { + background-color: rgb(178, 255, 193); + display: block; +} +span.run41 { + background-color: rgb(178, 255, 191); + display: block; +} +span.run42 { + background-color: rgb(178, 255, 188); + display: block; +} +span.run43 { + background-color: rgb(178, 255, 186); + display: block; +} +span.run44 { + background-color: rgb(178, 255, 183); + display: block; +} +span.run45 { + background-color: rgb(178, 255, 179); + display: block; +} +span.run46 { + background-color: rgb(179, 255, 178); + display: block; +} +span.run47 { + background-color: rgb(182, 255, 178); + display: block; +} +span.run48 { + background-color: rgb(184, 255, 178); + display: block; +} +span.run49 { + background-color: rgb(187, 255, 178); + display: block; +} +span.run50 { + background-color: rgb(191, 255, 178); + display: block; +} +span.run51 { + background-color: rgb(193, 255, 178); + display: block; +} +span.run52 { + background-color: rgb(196, 255, 178); + display: block; +} +span.run53 { + background-color: rgb(198, 255, 178); + display: block; +} +span.run54 { + background-color: rgb(201, 255, 178); + display: block; +} +span.run55 { + background-color: rgb(205, 255, 178); + display: block; +} +span.run56 { + background-color: rgb(207, 255, 178); + display: block; +} +span.run57 { + background-color: rgb(210, 255, 178); + display: block; +} +span.run58 { + background-color: rgb(212, 255, 178); + display: block; +} +span.run59 { + background-color: rgb(215, 255, 178); + display: block; +} +span.run60 { + background-color: rgb(219, 255, 178); + display: block; +} +span.run61 { + background-color: rgb(221, 255, 178); + display: block; +} +span.run62 { + background-color: rgb(224, 255, 178); + display: block; +} +span.run63 { + background-color: rgb(226, 255, 178); + display: block; +} +span.run64 { + background-color: rgb(229, 255, 178); + display: block; +} +span.run65 { + background-color: rgb(233, 255, 178); + display: block; +} +span.run66 { + background-color: rgb(235, 255, 178); + display: block; +} +span.run67 { + background-color: rgb(238, 255, 178); + display: block; +} +span.run68 { + background-color: rgb(240, 255, 178); + display: block; +} +span.run69 { + background-color: rgb(243, 255, 178); + display: block; +} +span.run70 { + background-color: rgb(247, 255, 178); + display: block; +} +span.run71 { + background-color: rgb(249, 255, 178); + display: block; +} +span.run72 { + background-color: rgb(252, 255, 178); + display: block; +} +span.run73 { + background-color: rgb(255, 255, 178); + display: block; +} +span.run74 { + background-color: rgb(255, 252, 178); + display: block; +} +span.run75 { + background-color: rgb(255, 248, 178); + display: block; +} +span.run76 { + background-color: rgb(255, 246, 178); + display: block; +} +span.run77 { + background-color: rgb(255, 243, 178); + display: block; +} +span.run78 { + background-color: rgb(255, 240, 178); + display: block; +} +span.run79 { + background-color: rgb(255, 238, 178); + display: block; +} +span.run80 { + background-color: rgb(255, 234, 178); + display: block; +} +span.run81 { + background-color: rgb(255, 232, 178); + display: block; +} +span.run82 { + background-color: rgb(255, 229, 178); + display: block; +} +span.run83 { + background-color: rgb(255, 226, 178); + display: block; +} +span.run84 { + background-color: rgb(255, 224, 178); + display: block; +} +span.run85 { + background-color: rgb(255, 220, 178); + display: block; +} +span.run86 { + background-color: rgb(255, 218, 178); + display: block; +} +span.run87 { + background-color: rgb(255, 215, 178); + display: block; +} +span.run88 { + background-color: rgb(255, 212, 178); + display: block; +} +span.run89 { + background-color: rgb(255, 210, 178); + display: block; +} +span.run90 { + background-color: rgb(255, 206, 178); + display: block; +} +span.run91 { + background-color: rgb(255, 204, 178); + display: block; +} +span.run92 { + background-color: rgb(255, 201, 178); + display: block; +} +span.run93 { + background-color: rgb(255, 198, 178); + display: block; +} +span.run94 { + background-color: rgb(255, 196, 178); + display: block; +} +span.run95 { + background-color: rgb(255, 192, 178); + display: block; +} +span.run96 { + background-color: rgb(255, 189, 178); + display: block; +} +span.run97 { + background-color: rgb(255, 187, 178); + display: block; +} +span.run98 { + background-color: rgb(255, 184, 178); + display: block; +} +span.run99 { + background-color: rgb(255, 182, 178); + display: block; +} +span.run100 { + background-color: rgb(255, 178, 178); + display: block; +} +</style> + </head> + <body><h3>C0 code coverage information</h3> + <p>Generated on Tue Jun 24 21:43:53 +0200 2008 with <a href='http://eigenclass.org/hiki/rcov'>rcov 0.8.1.2</a> + </p> + <hr/> + <pre><span class='marked0'>Code reported as executed by Ruby looks like this... +</span><span class='marked1'>and this: this line is also marked as covered. +</span><span class='inferred0'>Lines considered as run by rcov, but not reported by Ruby, look like this, +</span><span class='inferred1'>and this: these lines were inferred by rcov (using simple heuristics). +</span><span class='uncovered0'>Finally, here's a line marked as not executed. +</span></pre> +<table class='report'><thead><tr><td class='heading'>Name</td> + <td class='heading'>Total lines</td> + <td class='heading'>Lines of code</td> + <td class='heading'>Total coverage</td> + <td class='heading'>Code coverage</td> + </tr> + </thead> + <tbody><tr class='light'><td><a href='app-controllers-account_controller_rb.html'>app/controllers/account_controller.rb</a> + </td> + <td class='lines_total'><tt>173</tt> + </td> + <td class='lines_code'><tt>129</tt> + </td> + <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_total'>98.8%</tt> + </td> + <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='99'/> + <td class='uncovered' width='1'/> + </tr> + </table> + </td> + </tr> + </table> + </td> + <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_code'>98.4%</tt> + </td> + <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='98'/> + <td class='uncovered' width='2'/> + </tr> + </table> + </td> + </tr> + </table> + </td> + </tr> + </tbody> + </table> +<pre><span class="inferred0"><a name="line1"></a> 1 # redMine - project management software +</span><span class="inferred1"><a name="line2"></a> 2 # Copyright (C) 2006-2007 Jean-Philippe Lang +</span><span class="inferred0"><a name="line3"></a> 3 # +</span><span class="inferred1"><a name="line4"></a> 4 # This program is free software; you can redistribute it and/or +</span><span class="inferred0"><a name="line5"></a> 5 # modify it under the terms of the GNU General Public License +</span><span class="inferred1"><a name="line6"></a> 6 # as published by the Free Software Foundation; either version 2 +</span><span class="inferred0"><a name="line7"></a> 7 # of the License, or (at your option) any later version. +</span><span class="inferred1"><a name="line8"></a> 8 # +</span><span class="inferred0"><a name="line9"></a> 9 # This program is distributed in the hope that it will be useful, +</span><span class="inferred1"><a name="line10"></a> 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of +</span><span class="inferred0"><a name="line11"></a> 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +</span><span class="inferred1"><a name="line12"></a> 12 # GNU General Public License for more details. +</span><span class="inferred0"><a name="line13"></a> 13 # +</span><span class="inferred1"><a name="line14"></a> 14 # You should have received a copy of the GNU General Public License +</span><span class="inferred0"><a name="line15"></a> 15 # along with this program; if not, write to the Free Software +</span><span class="inferred1"><a name="line16"></a> 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +</span><span class="inferred0"><a name="line17"></a> 17 +</span><span class="marked1"><a name="line18"></a> 18 class AccountController < ApplicationController +</span><span class="marked0"><a name="line19"></a> 19 layout 'base' +</span><span class="marked1"><a name="line20"></a> 20 helper :custom_fields +</span><span class="marked0"><a name="line21"></a> 21 include CustomFieldsHelper +</span><span class="inferred1"><a name="line22"></a> 22 +</span><span class="inferred0"><a name="line23"></a> 23 # prevents login action to be filtered by check_if_login_required application scope filter +</span><span class="marked1"><a name="line24"></a> 24 skip_before_filter :check_if_login_required, :only => [:login, :lost_password, :register, :activate] +</span><span class="inferred0"><a name="line25"></a> 25 +</span><span class="inferred1"><a name="line26"></a> 26 # Show user's account +</span><span class="marked0"><a name="line27"></a> 27 def show +</span><span class="marked1"><a name="line28"></a> 28 @user = User.find_active(params[:id]) +</span><span class="marked0"><a name="line29"></a> 29 @custom_values = @user.custom_values.find(:all, :include => :custom_field) +</span><span class="inferred1"><a name="line30"></a> 30 +</span><span class="inferred0"><a name="line31"></a> 31 # show only public projects and private projects that the logged in user is also a member of +</span><span class="marked1"><a name="line32"></a> 32 @memberships = @user.memberships.select do |membership| +</span><span class="marked0"><a name="line33"></a> 33 membership.project.is_public? || (User.current.member_of?(membership.project)) +</span><span class="inferred1"><a name="line34"></a> 34 end +</span><span class="inferred0"><a name="line35"></a> 35 rescue ActiveRecord::RecordNotFound +</span><span class="marked1"><a name="line36"></a> 36 render_404 +</span><span class="marked0"><a name="line37"></a> 37 end +</span><span class="inferred1"><a name="line38"></a> 38 +</span><span class="inferred0"><a name="line39"></a> 39 # Login request and validation +</span><span class="marked1"><a name="line40"></a> 40 def login +</span><span class="marked0"><a name="line41"></a> 41 if request.get? +</span><span class="inferred1"><a name="line42"></a> 42 # Logout user +</span><span class="marked0"><a name="line43"></a> 43 self.logged_user = nil +</span><span class="inferred1"><a name="line44"></a> 44 else +</span><span class="inferred0"><a name="line45"></a> 45 # Authenticate user +</span><span class="marked1"><a name="line46"></a> 46 user = User.try_to_login(params[:username], params[:password]) +</span><span class="marked0"><a name="line47"></a> 47 if user +</span><span class="marked1"><a name="line48"></a> 48 self.logged_user = user +</span><span class="inferred0"><a name="line49"></a> 49 # generate a key and set cookie if autologin +</span><span class="marked1"><a name="line50"></a> 50 if params[:autologin] && Setting.autologin? +</span><span class="marked0"><a name="line51"></a> 51 token = Token.create(:user => user, :action => 'autologin') +</span><span class="marked1"><a name="line52"></a> 52 cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now } +</span><span class="inferred0"><a name="line53"></a> 53 end +</span><span class="marked1"><a name="line54"></a> 54 redirect_back_or_default :controller => 'my', :action => 'page' +</span><span class="inferred0"><a name="line55"></a> 55 else +</span><span class="marked1"><a name="line56"></a> 56 flash.now[:error] = l(:notice_account_invalid_creditentials) +</span><span class="inferred0"><a name="line57"></a> 57 end +</span><span class="inferred1"><a name="line58"></a> 58 end +</span><span class="uncovered0"><a name="line59"></a> 59 rescue User::OnTheFlyCreationFailure +</span><span class="uncovered1"><a name="line60"></a> 60 flash.now[:error] = 'Redmine could not retrieve the required information from the LDAP to create your account. Please, contact your Redmine administrator.' +</span><span class="marked0"><a name="line61"></a> 61 end +</span><span class="inferred1"><a name="line62"></a> 62 +</span><span class="inferred0"><a name="line63"></a> 63 # Log out current user and redirect to welcome page +</span><span class="marked1"><a name="line64"></a> 64 def logout +</span><span class="marked0"><a name="line65"></a> 65 cookies.delete :autologin +</span><span class="marked1"><a name="line66"></a> 66 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) if User.current.logged? +</span><span class="marked0"><a name="line67"></a> 67 self.logged_user = nil +</span><span class="marked1"><a name="line68"></a> 68 redirect_to home_url +</span><span class="inferred0"><a name="line69"></a> 69 end +</span><span class="inferred1"><a name="line70"></a> 70 +</span><span class="inferred0"><a name="line71"></a> 71 # Enable user to choose a new password +</span><span class="marked1"><a name="line72"></a> 72 def lost_password +</span><span class="marked0"><a name="line73"></a> 73 redirect_to(home_url) && return unless Setting.lost_password? +</span><span class="marked1"><a name="line74"></a> 74 if params[:token] +</span><span class="marked0"><a name="line75"></a> 75 @token = Token.find_by_action_and_value("recovery", params[:token]) +</span><span class="marked1"><a name="line76"></a> 76 redirect_to(home_url) && return unless @token and !@token.expired? +</span><span class="marked0"><a name="line77"></a> 77 @user = @token.user +</span><span class="marked1"><a name="line78"></a> 78 if request.post? +</span><span class="marked0"><a name="line79"></a> 79 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation] +</span><span class="marked1"><a name="line80"></a> 80 if @user.save +</span><span class="marked0"><a name="line81"></a> 81 @token.destroy +</span><span class="marked1"><a name="line82"></a> 82 flash[:notice] = l(:notice_account_password_updated) +</span><span class="marked0"><a name="line83"></a> 83 redirect_to :action => 'login' +</span><span class="marked1"><a name="line84"></a> 84 return +</span><span class="inferred0"><a name="line85"></a> 85 end +</span><span class="inferred1"><a name="line86"></a> 86 end +</span><span class="marked0"><a name="line87"></a> 87 render :template => "account/password_recovery" +</span><span class="marked1"><a name="line88"></a> 88 return +</span><span class="inferred0"><a name="line89"></a> 89 else +</span><span class="marked1"><a name="line90"></a> 90 if request.post? +</span><span class="marked0"><a name="line91"></a> 91 user = User.find_by_mail(params[:mail]) +</span><span class="inferred1"><a name="line92"></a> 92 # user not found in db +</span><span class="marked0"><a name="line93"></a> 93 flash.now[:error] = l(:notice_account_unknown_email) and return unless user +</span><span class="inferred1"><a name="line94"></a> 94 # user uses an external authentification +</span><span class="marked0"><a name="line95"></a> 95 flash.now[:error] = l(:notice_can_t_change_password) and return if user.auth_source_id +</span><span class="inferred1"><a name="line96"></a> 96 # create a new token for password recovery +</span><span class="marked0"><a name="line97"></a> 97 token = Token.new(:user => user, :action => "recovery") +</span><span class="marked1"><a name="line98"></a> 98 if token.save +</span><span class="marked0"><a name="line99"></a> 99 Mailer.deliver_lost_password(token) +</span><span class="marked1"><a name="line100"></a>100 flash[:notice] = l(:notice_account_lost_email_sent) +</span><span class="marked0"><a name="line101"></a>101 redirect_to :action => 'login' +</span><span class="marked1"><a name="line102"></a>102 return +</span><span class="inferred0"><a name="line103"></a>103 end +</span><span class="inferred1"><a name="line104"></a>104 end +</span><span class="inferred0"><a name="line105"></a>105 end +</span><span class="inferred1"><a name="line106"></a>106 end +</span><span class="inferred0"><a name="line107"></a>107 +</span><span class="inferred1"><a name="line108"></a>108 # User self-registration +</span><span class="marked0"><a name="line109"></a>109 def register +</span><span class="marked1"><a name="line110"></a>110 redirect_to(home_url) && return unless Setting.self_registration? +</span><span class="marked0"><a name="line111"></a>111 if request.get? +</span><span class="marked1"><a name="line112"></a>112 @user = User.new(:language => Setting.default_language) +</span><span class="inferred0"><a name="line113"></a>113 else +</span><span class="marked1"><a name="line114"></a>114 @user = User.new(params[:user]) +</span><span class="marked0"><a name="line115"></a>115 @user.admin = false +</span><span class="marked1"><a name="line116"></a>116 @user.login = params[:user][:login] +</span><span class="marked0"><a name="line117"></a>117 @user.status = User::STATUS_REGISTERED +</span><span class="marked1"><a name="line118"></a>118 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] +</span><span class="marked0"><a name="line119"></a>119 case Setting.self_registration +</span><span class="marked1"><a name="line120"></a>120 when '1' +</span><span class="inferred0"><a name="line121"></a>121 # Email activation +</span><span class="marked1"><a name="line122"></a>122 token = Token.new(:user => @user, :action => "register") +</span><span class="marked0"><a name="line123"></a>123 if @user.save and token.save +</span><span class="marked1"><a name="line124"></a>124 Mailer.deliver_register(token) +</span><span class="marked0"><a name="line125"></a>125 flash[:notice] = l(:notice_account_register_done) +</span><span class="marked1"><a name="line126"></a>126 redirect_to :action => 'login' +</span><span class="inferred0"><a name="line127"></a>127 end +</span><span class="marked1"><a name="line128"></a>128 when '3' +</span><span class="inferred0"><a name="line129"></a>129 # Automatic activation +</span><span class="marked1"><a name="line130"></a>130 @user.status = User::STATUS_ACTIVE +</span><span class="marked0"><a name="line131"></a>131 if @user.save +</span><span class="marked1"><a name="line132"></a>132 self.logged_user = @user +</span><span class="marked0"><a name="line133"></a>133 flash[:notice] = l(:notice_account_activated) +</span><span class="marked1"><a name="line134"></a>134 redirect_to :controller => 'my', :action => 'account' +</span><span class="inferred0"><a name="line135"></a>135 end +</span><span class="inferred1"><a name="line136"></a>136 else +</span><span class="inferred0"><a name="line137"></a>137 # Manual activation by the administrator +</span><span class="marked1"><a name="line138"></a>138 if @user.save +</span><span class="inferred0"><a name="line139"></a>139 # Sends an email to the administrators +</span><span class="marked1"><a name="line140"></a>140 Mailer.deliver_account_activation_request(@user) +</span><span class="marked0"><a name="line141"></a>141 flash[:notice] = l(:notice_account_pending) +</span><span class="marked1"><a name="line142"></a>142 redirect_to :action => 'login' +</span><span class="inferred0"><a name="line143"></a>143 end +</span><span class="inferred1"><a name="line144"></a>144 end +</span><span class="inferred0"><a name="line145"></a>145 end +</span><span class="inferred1"><a name="line146"></a>146 end +</span><span class="inferred0"><a name="line147"></a>147 +</span><span class="inferred1"><a name="line148"></a>148 # Token based account activation +</span><span class="marked0"><a name="line149"></a>149 def activate +</span><span class="marked1"><a name="line150"></a>150 redirect_to(home_url) && return unless Setting.self_registration? && params[:token] +</span><span class="marked0"><a name="line151"></a>151 token = Token.find_by_action_and_value('register', params[:token]) +</span><span class="marked1"><a name="line152"></a>152 redirect_to(home_url) && return unless token and !token.expired? +</span><span class="marked0"><a name="line153"></a>153 user = token.user +</span><span class="marked1"><a name="line154"></a>154 redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED +</span><span class="marked0"><a name="line155"></a>155 user.status = User::STATUS_ACTIVE +</span><span class="marked1"><a name="line156"></a>156 if user.save +</span><span class="marked0"><a name="line157"></a>157 token.destroy +</span><span class="marked1"><a name="line158"></a>158 flash[:notice] = l(:notice_account_activated) +</span><span class="inferred0"><a name="line159"></a>159 end +</span><span class="marked1"><a name="line160"></a>160 redirect_to :action => 'login' +</span><span class="inferred0"><a name="line161"></a>161 end +</span><span class="inferred1"><a name="line162"></a>162 +</span><span class="marked0"><a name="line163"></a>163 private +</span><span class="marked1"><a name="line164"></a>164 def logged_user=(user) +</span><span class="marked0"><a name="line165"></a>165 if user && user.is_a?(User) +</span><span class="marked1"><a name="line166"></a>166 User.current = user +</span><span class="marked0"><a name="line167"></a>167 session[:user_id] = user.id +</span><span class="inferred1"><a name="line168"></a>168 else +</span><span class="marked0"><a name="line169"></a>169 User.current = User.anonymous +</span><span class="marked1"><a name="line170"></a>170 session[:user_id] = nil +</span><span class="inferred0"><a name="line171"></a>171 end +</span><span class="marked1"><a name="line172"></a>172 end +</span><span class="inferred0"><a name="line173"></a>173 end +</span></pre><hr/> + <p>Generated using the <a href='http://eigenclass.org/hiki.rb?rcov'>rcov code coverage analysis tool for Ruby</a> + version 0.8.1.2.</p> +<p><a href='http://validator.w3.org/check/referer'><img src='http://www.w3.org/Icons/valid-xhtml10' height='31' alt='Valid XHTML 1.0!' width='88'/> + </a> + <a href='http://jigsaw.w3.org/css-validator/check/referer'><img src='http://jigsaw.w3.org/css-validator/images/vcss' alt='Valid CSS!' style='border:0;width:88px;height:31px'/> + </a> + </p> + </body> + </html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/test/fixtures/html/index.html Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,306 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'><head><title>C0 code coverage information</title> + <style type='text/css'>body { background-color: rgb(240, 240, 245); }</style> + <style type='text/css'>span.cross-ref-title { + font-size: 140%; +} +span.cross-ref a { + text-decoration: none; +} +span.cross-ref { + background-color:#f3f7fa; + border: 1px dashed #333; + margin: 1em; + padding: 0.5em; + overflow: hidden; +} +a.crossref-toggle { + text-decoration: none; +} +span.marked0 { + background-color: rgb(185, 210, 200); + display: block; +} +span.marked1 { + background-color: rgb(190, 215, 205); + display: block; +} +span.inferred0 { + background-color: rgb(175, 200, 200); + display: block; +} +span.inferred1 { + background-color: rgb(180, 205, 205); + display: block; +} +span.uncovered0 { + background-color: rgb(225, 110, 110); + display: block; +} +span.uncovered1 { + background-color: rgb(235, 120, 120); + display: block; +} +span.overview { + border-bottom: 8px solid black; +} +div.overview { + border-bottom: 8px solid black; +} +body { + font-family: verdana, arial, helvetica; +} +div.footer { + font-size: 68%; + margin-top: 1.5em; +} +h1, h2, h3, h4, h5, h6 { + margin-bottom: 0.5em; +} +h5 { + margin-top: 0.5em; +} +.hidden { + display: none; +} +div.separator { + height: 10px; +} +/* Commented out for better readability, esp. on IE */ +/* +table tr td, table tr th { + font-size: 68%; +} +td.value table tr td { + font-size: 11px; +} +*/ +table.percent_graph { + height: 12px; + border: #808080 1px solid; + empty-cells: show; +} +table.percent_graph td.covered { + height: 10px; + background: #00f000; +} +table.percent_graph td.uncovered { + height: 10px; + background: #e00000; +} +table.percent_graph td.NA { + height: 10px; + background: #eaeaea; +} +table.report { + border-collapse: collapse; + width: 100%; +} +table.report td.heading { + background: #dcecff; + border: #d0d0d0 1px solid; + font-weight: bold; + text-align: center; +} +table.report td.heading:hover { + background: #c0ffc0; +} +table.report td.text { + border: #d0d0d0 1px solid; +} +table.report td.value, +table.report td.lines_total, +table.report td.lines_code { + text-align: right; + border: #d0d0d0 1px solid; +} +table.report tr.light { + background-color: rgb(240, 240, 245); +} +table.report tr.dark { + background-color: rgb(230, 230, 235); +} +</style> + <script type='text/javascript'> +// <![CDATA[ + function toggleCode( id ) { + if ( document.getElementById ) + elem = document.getElementById( id ); + else if ( document.all ) + elem = eval( "document.all." + id ); + else + return false; + + elemStyle = elem.style; + + if ( elemStyle.display != "block" ) { + elemStyle.display = "block" + } else { + elemStyle.display = "none" + } + + return true; + } + + // Make cross-references hidden by default + document.writeln( "<style type=\"text/css\">span.cross-ref { display: none }</style>" ) + // ]]> +</script> + </head> + <body><h3>C0 code coverage information</h3> + <p>Generated on Tue Jun 24 21:43:50 +0200 2008 with <a href='http://eigenclass.org/hiki/rcov'>rcov 0.8.1.2</a> + </p> + <hr/> + <table class='report'><thead><tr><td class='heading'>Name</td> + <td class='heading'>Total lines</td> + <td class='heading'>Lines of code</td> + <td class='heading'>Total coverage</td> + <td class='heading'>Code coverage</td> + </tr> + </thead> + <tbody><tr class='light'><td>TOTAL</td> + <td class='lines_total'><tt>13757</tt> + </td> + <td class='lines_code'><tt>9314</tt> + </td> + <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_total'>90.7%</tt> + </td> + <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='91'/> + <td class='uncovered' width='9'/> + </tr> + </table> + </td> + </tr> + </table> + </td> + <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_code'>86.9%</tt> + </td> + <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='87'/> + <td class='uncovered' width='13'/> + </tr> + </table> + </td> + </tr> + </table> + </td> + </tr> + <tr class='dark'><td><a href='app-apis-sys_api_rb.html'>app/apis/sys_api.rb</a> + </td> + <td class='lines_total'><tt>25</tt> + </td> + <td class='lines_code'><tt>8</tt> + </td> + <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_total'>100.0%</tt> + </td> + <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='100'/> + <td class='uncovered' width='0'/> + </tr> + </table> + </td> + </tr> + </table> + </td> + <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_code'>100.0%</tt> + </td> + <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='100'/> + <td class='uncovered' width='0'/> + </tr> + </table> + </td> + </tr> + </table> + </td> + </tr> + <tr class='light'><td><a href='app-controllers-account_controller_rb.html'>app/controllers/account_controller.rb</a> + </td> + <td class='lines_total'><tt>173</tt> + </td> + <td class='lines_code'><tt>129</tt> + </td> + <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_total'>98.8%</tt> + </td> + <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='99'/> + <td class='uncovered' width='1'/> + </tr> + </table> + </td> + </tr> + </table> + </td> + <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_code'>98.4%</tt> + </td> + <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='98'/> + <td class='uncovered' width='2'/> + </tr> + </table> + </td> + </tr> + </table> + </td> + </tr> + <tr class='dark'><td><a href='app-controllers-admin_controller_rb.html'>app/controllers/admin_controller.rb</a> + </td> + <td class='lines_total'><tt>86</tt> + </td> + <td class='lines_code'><tt>57</tt> + </td> + <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_total'>91.9%</tt> + </td> + <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='92'/> + <td class='uncovered' width='8'/> + </tr> + </table> + </td> + </tr> + </table> + </td> + <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_code'>87.7%</tt> + </td> + <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='88'/> + <td class='uncovered' width='12'/> + </tr> + </table> + </td> + </tr> + </table> + </td> + </tr> + <tr class='light'><td><a href='app-controllers-application_rb.html'>app/controllers/application.rb</a> + </td> + <td class='lines_total'><tt>222</tt> + </td> + <td class='lines_code'><tt>162</tt> + </td> + <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_total'>89.6%</tt> + </td> + <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='90'/> + <td class='uncovered' width='10'/> + </tr> + </table> + </td> + </tr> + </table> + </td> + <td><table cellspacing='0' cellpadding='0' align='right'><tr><td><tt class='coverage_code'>87.0%</tt> + </td> + <td><table cellspacing='0' class='percent_graph' cellpadding='0' width='100'><tr><td class='covered' width='87'/> + <td class='uncovered' width='13'/> + </tr> + </table> + </td> + </tr> + </table> + </td> + </tr> + </tbody> + </table> +<hr/> + <p>Generated using the <a href='http://eigenclass.org/hiki.rb?rcov'>rcov code coverage analysis tool for Ruby</a> + version 0.8.1.2.</p> +<p><a href='http://validator.w3.org/check/referer'><img src='http://www.w3.org/Icons/valid-xhtml11' height='31' alt='Valid XHTML 1.1!' width='88'/> + </a> + <a href='http://jigsaw.w3.org/css-validator/check/referer'><img src='http://jigsaw.w3.org/css-validator/images/vcss' alt='Valid CSS!' style='border:0;width:88px;height:31px'/> + </a> + </p> + </body> + </html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/test/fixtures/html/misc/misc.html Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,1 @@ +<b>Misc file</b>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/test/fixtures/html/misc/misc.txt Thu Mar 03 12:11:53 2011 +0000 @@ -0,0 +1,1 @@ +Misc file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/test/functional/embedded_controller_test.rb Thu Mar 03 12:11:53 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/test/test_helper.rb Thu Mar 03 12:11:53 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/plugins/embedded/test/unit/embedded_test.rb Thu Mar 03 12:11:53 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
--- a/vendor/plugins/redmine_checkout/config/locales/en-GB.yml Thu Mar 03 12:02:03 2011 +0000 +++ b/vendor/plugins/redmine_checkout/config/locales/en-GB.yml Thu Mar 03 12:11:53 2011 +0000 @@ -1,4 +1,4 @@ -en: +en-GB: label_checkout: "Checkout" setting_checkout_display_checkout_info: "Display checkout information"