changeset 412:0964cbb51753 cannam-pre-20110113-merge

Merge from branch "feature_113"
author Chris Cannam
date Thu, 28 Apr 2011 13:36:40 +0100
parents c4a0942f2897 (diff) e7ba81c8dc5a (current diff)
children 576a1dca2ee2 405dd646a6cc
files extra/soundsoftware/extract-docs.sh public/themes/soundsoftware/stylesheets/application.css
diffstat 24 files changed, 273 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/app/controllers/application_controller.rb	Thu Apr 28 13:29:15 2011 +0100
+++ b/app/controllers/application_controller.rb	Thu Apr 28 13:36:40 2011 +0100
@@ -263,12 +263,17 @@
         uri = URI.parse(back_url)
         # do not redirect user to another host or to the login or register page
         if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
+          # soundsoftware: if back_url is the home page,
+          # change it to My Page (#125)
+          if (uri.path == home_path)
+            uri.path = uri.path + "/my"
+          end
           # soundsoftware: if login page is https but back_url http,
           # switch back_url to https to ensure cookie validity (#83)
           if (uri.scheme == "http") && (URI.parse(request.url).scheme == "https")
             uri.scheme = "https"
-            back_url = uri.to_s
           end
+          back_url = uri.to_s
           redirect_to(back_url)
           return
         end
--- a/app/controllers/my_controller.rb	Thu Apr 28 13:29:15 2011 +0100
+++ b/app/controllers/my_controller.rb	Thu Apr 28 13:36:40 2011 +0100
@@ -24,14 +24,16 @@
   BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
              'issuesreportedbyme' => :label_reported_issues,
              'issueswatched' => :label_watched_issues,
+             'activitymyprojects' => :label_activity_my_recent,
              'news' => :label_news_latest,
+             'tipoftheday' => :label_tipoftheday,
              'calendar' => :label_calendar,
              'documents' => :label_document_plural,
              'timelog' => :label_spent_time
            }.merge(Redmine::Views::MyPage::Block.additional_blocks).freeze
 
-  DEFAULT_LAYOUT = {  'left' => ['issuesassignedtome'], 
-                      'right' => ['issuesreportedbyme'] 
+  DEFAULT_LAYOUT = {  'left' => ['tipoftheday', 'activitymyprojects'], 
+                      'right' => ['issueswatched','calendar'] 
                    }.freeze
 
   verify :xhr => true,
--- a/app/controllers/projects_controller.rb	Thu Apr 28 13:29:15 2011 +0100
+++ b/app/controllers/projects_controller.rb	Thu Apr 28 13:36:40 2011 +0100
@@ -217,7 +217,15 @@
       end
     end
   end
-  
+
+  def overview
+    @project.has_welcome_page = params[:has_welcome_page]
+    if @project.save
+      flash[:notice] = l(:notice_successful_update)
+    end
+    redirect_to :action => 'settings', :id => @project, :tab => 'overview'
+  end
+
   def modules
     @project.enabled_module_names = params[:enabled_modules]
     flash[:notice] = l(:notice_successful_update)
--- a/app/controllers/welcome_controller.rb	Thu Apr 28 13:29:15 2011 +0100
+++ b/app/controllers/welcome_controller.rb	Thu Apr 28 13:36:40 2011 +0100
@@ -18,11 +18,16 @@
 class WelcomeController < ApplicationController
   caches_action :robots
 
+  include ProjectsHelper
+  helper :projects
+
   def index
-    @news = News.latest User.current
+    @site_project = Project.find_by_identifier "soundsoftware-site"
+    @site_news = []
+    @site_news = News.latest_for @site_project if @site_project
     @projects = Project.latest User.current
     
-    # tests if user is logged in to gfenerate the tips of the day list
+    # tests if user is logged in to generate the tips of the day list
     if User.current.logged?
       @tipsoftheday = Setting.tipoftheday_text
     else
--- a/app/helpers/projects_helper.rb	Thu Apr 28 13:29:15 2011 +0100
+++ b/app/helpers/projects_helper.rb	Thu Apr 28 13:36:40 2011 +0100
@@ -23,6 +23,7 @@
   
   def project_settings_tabs
     tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
+            {:name => 'overview', :action => :edit_project, :partial => 'projects/settings/overview', :label => :label_welcome_page},
             {:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
             {:name => 'members', :action => :manage_members, :partial => 'projects/settings/members', :label => :label_member_plural},
             {:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural},
@@ -48,6 +49,16 @@
     options << project_tree_options_for_select(project.allowed_parents.compact, :selected => selected)
     content_tag('select', options, :name => 'project[parent_id]', :id => 'project_parent_id')
   end
+
+  def render_project_short_description(project)
+    s = ''
+    if (project.short_description)
+      s << "<div class='description'>"
+      s << textilizable(project.short_description, :project => project).gsub(/<[^>]+>/, '')
+      s << "</div>"
+    end
+    s
+  end
   
   # Renders a tree of projects as a nested set of unordered lists
   # The given collection may be a subset of the whole project tree
@@ -73,7 +84,7 @@
         classes = (ancestors.empty? ? 'root' : 'child')
         s << "<li class='#{classes}'><div class='#{classes}'>" +
                link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
-        s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
+        s << render_project_short_description(project)
         s << "</div>\n"
         ancestors << project
       end
@@ -127,7 +138,7 @@
           else
             s << " <span class='private'>" << l("field_is_private") << "</span>"
           end
-          s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
+          s << render_project_short_description(project)
           s << "</div>\n"
           ancestors << project
         end
@@ -198,11 +209,7 @@
     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
+    s << render_project_short_description(project)
       
     s << "<td class='managers' align=top>"
 
--- a/app/models/news.rb	Thu Apr 28 13:29:15 2011 +0100
+++ b/app/models/news.rb	Thu Apr 28 13:36:40 2011 +0100
@@ -37,4 +37,9 @@
   def self.latest(user = User.current, count = 5)
     find(:all, :limit => count, :conditions => Project.allowed_to_condition(user, :view_news), :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")	
   end
+
+  # returns latest news for a specific project
+  def self.latest_for(project, count = 5)
+    find(:all, :limit => count, :conditions => [ "#{News.table_name}.project_id = #{project.id}", Project.allowed_to_condition(User.current, :view_news) ], :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC") 
+  end
 end
--- a/app/models/project.rb	Thu Apr 28 13:29:15 2011 +0100
+++ b/app/models/project.rb	Thu Apr 28 13:36:40 2011 +0100
@@ -423,13 +423,20 @@
   
   # Returns a short description of the projects (first lines)
   def short_description(length = 255)
+
+    ## The short description is used in lists, e.g. Latest projects,
+    ## My projects etc.  It should be no more than a line or two with
+    ## no text formatting.
+
     ## Original Redmine code: this truncates to the CR that is more
     ## than "length" characters from the start.
     # description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
-    ## That's too much for us, and also we want to omit images and the
-    ## like.  Truncate instead to the first CR that follows _any_
-    ## non-blank text, and to the next word break beyond "length"
-    ## characters if the result is still longer than that.
+
+    ## That can leave too much text for us, and also we want to omit
+    ## images and the like.  Truncate instead to the first CR that
+    ## follows _any_ non-blank text, and to the next word break beyond
+    ## "length" characters if the result is still longer than that.
+    ##
     description.gsub(/![^\s]+!/, '').gsub(/^(\s*[^\n\r]*).*$/m, '\1').gsub(/^(.{#{length}}\b).*$/m, '\1 ...').strip if description
   end
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/activities/_recent.rhtml	Thu Apr 28 13:36:40 2011 +0100
@@ -0,0 +1,65 @@
+<% events = @events_by_day %>
+<% max = 5 %>
+<% if (events.nil?) 
+     activity = Redmine::Activity::Fetcher.new(User.current, :project => @project)
+     
+     if @project
+        # Don't show news (duplicated with News box) or wiki edits (too
+	# tedious) in project front page
+        activity.scope = [ "changesets", "files", "issues", "documents" ]
+     end
+     
+     events = activity.events(Date.today - 28, Date.today + 1)
+     
+     if defined? user
+       events = events.select { |e| user.member_of? e.project }
+     end
+     
+     events = events.first(max)
+
+   end
+%>
+
+<div id="activity">
+
+<% if @project.nil? %>
+   <%= content_tag('h3', l(:label_activity_my_recent)) %>
+   <div class="activity box">
+<% end %>
+
+<% if events.empty? %>
+
+   <% if @project.nil? %>
+     <p><%= l(:label_activity_my_recent_none) %></p>
+   <% end %>
+
+<% else %>
+
+   <% if !@project.nil? %>
+     <div class="activity box">
+     <%= content_tag('h3', l(:label_activity_recent)) %>
+   <% end %>
+
+   <dl>
+   <% events.sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| -%>
+     <dt class="<%= User.current.logged? && e.respond_to?(:event_author) && User.current == e.event_author ? 'me' : nil %>">
+	<%= avatar(e.event_author, :size => "24") if e.respond_to?(:event_author) %>
+     <span class="time"><%= format_time(e.event_datetime) %></span>
+     <%= content_tag('span', link_to_project(e.project), :class => 'project') if @project.nil? || @project != e.project %>
+     <% if e.respond_to?(:event_author) %>
+       <span class="author"><%= e.event_author %></span>
+     <% end %>
+     </dt>
+     <dd><%= link_to format_activity_title(e.event_title), e.event_url %>
+     <span class="description"><%= format_activity_description(e.event_description) %></span>
+     </dd>
+   <% end -%>
+   </dl>
+
+   </div>
+
+<% end %>
+
+<% if events.empty? and @project.nil? %></div><% end %>
+
+</div>
--- a/app/views/layouts/base.rhtml	Thu Apr 28 13:29:15 2011 +0100
+++ b/app/views/layouts/base.rhtml	Thu Apr 28 13:36:40 2011 +0100
@@ -52,11 +52,16 @@
       <h3 id="project-ancestors-title"><%= page_header_title[1] %></h3>
     <% end %>  
 
-    <h1  id="project-title"
+    <h1 id="project-title"
       <% unless page_header_title[1].empty? %>
         style="margin-top: 0px; "
       <% end %>  
-    ><%= page_header_title[0] %></h1>
+    ><% if display_main_menu?(@project) %>
+       <%= link_to_project(@project) %>
+     <% else %> 
+       <%= page_header_title[0] %>
+     <% end %>
+    </h1>
     
     <% if display_main_menu?(@project) %>
     <div id="main-menu">
@@ -83,7 +88,7 @@
 	
 <div id="footer">
   <div class="bgl"><div class="bgr">
-    Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2010 Jean-Philippe Lang
+    <small>Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %><br>&copy; 2006-2011 Jean-Philippe Lang</small>
   </div></div>
 </div>
 </div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/my/blocks/_activitymyprojects.rhtml	Thu Apr 28 13:36:40 2011 +0100
@@ -0,0 +1,4 @@
+
+<%= render :partial => 'activities/recent', :locals => { :user => User.current } %>
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/my/blocks/_tipoftheday.rhtml	Thu Apr 28 13:36:40 2011 +0100
@@ -0,0 +1,4 @@
+    <h3><%=l(:label_tipoftheday)%></h3>
+    <div class="tipoftheday box">
+          <div class="tip"><%= textilizable Setting.tipoftheday_text %></div>
+    </div>
--- a/app/views/news/_news.rhtml	Thu Apr 28 13:29:15 2011 +0100
+++ b/app/views/news/_news.rhtml	Thu Apr 28 13:36:40 2011 +0100
@@ -1,6 +1,13 @@
-<p><%= link_to_project(news.project) + ': ' unless @project %>
-<%= link_to h(news.title), news_path(news) %>
-<%= "(#{l(:label_x_comments, :count => news.comments_count)})" if news.comments_count > 0 %>
-<br />
+<div id="news">
+<dt>
+<span class="time"><%= format_time(news.created_on) %></span>
+<% project ||= @project %>
+<% if !project %>
+<span class="project"><%= link_to_project(news.project) %></span>
+<% end %>
+<span class="headline"><%= link_to h(news.title), news_path(news) %></span>
+<span class="comments"><%= "(#{l(:label_x_comments, :count => news.comments_count)})" if news.comments_count > 0 %></span>
+</dt><dd>
 <% unless news.summary.blank? %><span class="summary"><%=h news.summary %></span><br /><% end %>
-<span class="author"><%= authoring news.created_on, news.author %></span></p>
+</dd>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/views/projects/settings/_overview.rhtml	Thu Apr 28 13:36:40 2011 +0100
@@ -0,0 +1,30 @@
+
+<% form_for :project, @project,
+            :url => { :action => 'overview', :id => @project },
+            :html => {:id => 'overview-form'} do |f| %>
+
+<div class="box tabular">
+
+<p><%= l(:text_has_welcome_page_info, { :overview_link => link_to(l(:label_overview), { :controller => 'projects', :action => 'show' } ) } ) %></p>
+
+<% if @project.module_enabled? :wiki %>
+
+<p><%= link_to(l(:button_welcome_page_edit), {:controller => 'wiki', :action => 'edit', :project_id => @project, :id => Wiki.titleize("Overview")}, :class => 'icon icon-edit') %>
+
+<% else %>
+
+<p><%= l(:text_has_welcome_page_wiki_disabled, { :modules_link => link_to(l(:label_module_plural), { :controller => 'projects', :action => 'settings', :tab => 'modules' } ) } ) %></p>
+
+<% end %>
+
+<p><label for="has_welcome_page"><%= l(:label_has_welcome_page) %></label>
+<%= check_box_tag 'has_welcome_page', 1, @project.has_welcome_page? -%>
+<br/><em><%= l(:setting_has_welcome_page) %></em>
+
+</p>
+
+</div>
+
+<%= submit_tag l(:button_save) %>
+
+<% end %>
--- a/app/views/projects/show.rhtml	Thu Apr 28 13:29:15 2011 +0100
+++ b/app/views/projects/show.rhtml	Thu Apr 28 13:36:40 2011 +0100
@@ -4,6 +4,29 @@
 	<% end %>
 </div>
 
+<% if @project.has_welcome_page %>
+<% page = @project.wiki.find_page("Overview") %>
+<% end %>
+
+<% if page %>
+
+<div class="contextual" style="clear: right">
+<ul>
+<% @users_by_role.keys.sort.each do |role| %>
+<li><%=h role %>: <%= @users_by_role[role].sort.collect{|u| link_to_user u}.join(", ") %></li>
+<% end %>
+<% unless @project.homepage.blank? %><li><%=l(:field_homepage)%>: <%= auto_link(h(@project.homepage)) %></li><% end %>
+<% if @subprojects.any? %>
+	<li><%=l(:label_subproject_plural)%>:
+	    <%= @subprojects.collect{|p| link_to(h(p), :action => 'show', :id => p)}.join(", ") %></li>
+<% end %>
+</ul>
+</div>
+
+<%= render(:partial => "wiki/content", :locals => {:content => page.content_for_version()}) %>
+
+<% else %>
+
 <h2><%=l(:label_overview)%></h2> 
 	
 <div class="splitcontentleft">
@@ -23,7 +46,8 @@
 	<% end %>
 	</ul>	
 
-  <% if User.current.allowed_to?(:view_issues, @project) %>
+  <% if User.current.allowed_to?(:view_issues, @project) and @open_issues_by_tracker.values.any? %>
+
   <div class="issues box">    
     <h3><%=l(:label_issue_tracking)%></h3>
     <ul>
@@ -46,7 +70,9 @@
 			<% end %>
 		</p>
   </div>
+
   <% end %>
+
   <%= call_hook(:view_projects_show_left, :project => @project) %>
 </div>
 
@@ -60,6 +86,9 @@
     <p><%= link_to l(:label_news_view_all), :controller => 'news', :action => 'index', :project_id => @project %></p>
   </div>  
   <% end %>
+
+  <%= render :partial => 'activities/recent' %>
+
   <%= call_hook(:view_projects_show_right, :project => @project) %>
 </div>
 
@@ -73,6 +102,8 @@
     <%= call_hook(:view_projects_show_sidebar_bottom, :project => @project) %>
 <% end %>
 
+<% end %>
+
 <% content_for :header_tags do %>
 <%= auto_discovery_link_tag(:atom, {:controller => 'activities', :action => 'index', :id => @project, :format => 'atom', :key => User.current.rss_key}) %>
 <% end %>
--- a/app/views/welcome/index.rhtml	Thu Apr 28 13:29:15 2011 +0100
+++ b/app/views/welcome/index.rhtml	Thu Apr 28 13:36:40 2011 +0100
@@ -9,37 +9,33 @@
 <div class="splitcontentleft">
   <%= textilizable Setting.welcome_text %>
   
-  <% if @news.any? %>
-  <div class="news box">
-	<h3><%=l(:label_news_latest)%></h3>
-		<%= render :partial => 'news/news', :collection => @news %>
-		<%= link_to l(:label_news_view_all), :controller => 'news' %>
-  </div>
-  <% end %>
   <%= call_hook(:view_welcome_index_left, :projects => @projects) %>
 </div>
 
 <div class="splitcontentright">
-  <% if not @tipsoftheday.empty? %>
-    <div class="newsoftheday box">
-          <h3><%=l(:label_tipoftheday)%></h3>
-          <%= textilizable @tipsoftheday %>
-    </div>
+  <% if @site_news.any? %>
+  <div class="news box">
+	<h3><%=l(:label_news_site_latest)%></h3>
+	<%= render :partial => 'news/news', :locals => { :project => @site_project }, :collection => @site_news %>
+	
+	<%= link_to l(:label_news_more), { :controller => 'projects', :action => @site_project.identifier, :id => 'news' } %>
+  </div>
   <% end %>
-
     <% if @projects.any? %>
 	<div class="projects box">
 	<h3><%=l(:label_project_latest)%></h3>
 		<ul>
 		<% for project in @projects %>
 		  <% @project = project %>
-			<li>
-			<%= link_to_project project %> (<%= format_time(project.created_on) %>)
-			<%= textilizable project.short_description, :project => project %>
+			<li class="latest">
+			<span class="title"><%= link_to_project project %></span>
+			<span class="time"><%= format_time(project.created_on)%></span>
+			<%= render_project_short_description project %>
 			</li>
 		<% end %>
     <% @project = nil %>
 		</ul>
+	<%= link_to l(:label_projects_more), :controller => 'projects' %>
 	</div>
 	<% end %>
     <%= call_hook(:view_welcome_index_right, :projects => @projects) %>
--- a/config/locales/en.yml	Thu Apr 28 13:29:15 2011 +0100
+++ b/config/locales/en.yml	Thu Apr 28 13:36:40 2011 +0100
@@ -373,6 +373,7 @@
   setting_rest_api_enabled: Enable REST web service
   setting_cache_formatted_text: Cache formatted text
   setting_default_notification_option: Default notification option
+  setting_has_welcome_page: Select this to replace the project overview page with your welcome page
   
   permission_add_project: Create project
   permission_add_subprojects: Create subprojects
@@ -463,6 +464,7 @@
     other: "{{count}} projects"
   label_project_all: All Projects
   label_project_latest: Latest projects
+  label_projects_more: More projects
   label_managers: Managed by
   label_issue: Issue
   label_issue_new: New issue
@@ -518,6 +520,9 @@
   label_last_login: Last connection
   label_registered_on: Registered on
   label_activity: Activity
+  label_activity_recent: Recent activity
+  label_activity_my_recent: Recent activity in my projects
+  label_activity_my_recent_none: No recent activity
   label_overall_activity: Overall activity
   label_user_activity: "{{value}}'s activity"
   label_new: New
@@ -556,6 +561,8 @@
   label_news_new: Add news
   label_news_plural: News
   label_news_latest: Latest news
+  label_news_site_latest: Site news
+  label_news_more: More news
   label_news_view_all: View all news
   label_news_added: News added
   label_settings: Settings
@@ -749,6 +756,7 @@
   label_added_time_by: "Added by {{author}} {{age}} ago"
   label_updated_time_by: "Updated by {{author}} {{age}} ago"
   label_updated_time: "Updated {{value}} ago"
+  label_time_ago: "{{age}} ago"
   label_jump_to_a_project: Jump to a project...
   label_file_plural: Downloads
   label_changeset_plural: Changesets
@@ -816,6 +824,8 @@
   label_project_copy_notifications: Send email notifications during the project copy
   label_principal_search: "Search by name:"
   label_user_search: "Search for user:"
+  label_welcome_page: "Welcome page"
+  label_has_welcome_page: "Use your own welcome page"
   
   button_login: Login
   button_submit: Submit
@@ -861,6 +871,7 @@
   button_quote: Quote
   button_duplicate: Duplicate
   button_show: Show
+  button_welcome_page_edit: Create or edit welcome page
   
   status_active: active
   status_registered: registered
@@ -892,7 +903,7 @@
   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_user_ssamr_description_info: 'Please describe your current research or development interests.<br/>This information will be used at registration to determine that you are a real person &ndash; so please be descriptive, or your application may be delayed or rejected.<br/>After registration, the description is publicly visible in your profile and you can edit it at any time.'
   text_issue_parent_issue_info: 'If this is a subtask, please insert its parent task number or write the main task name.'
   text_caracters_maximum: "{{count}} characters maximum."
   text_caracters_minimum: "Must be at least {{count}} characters long."
@@ -942,6 +953,8 @@
   text_settings_repo_is_internal: Currently the repository hosted at this site is the primary repository for this project.
   text_settings_repo_is_external: Currently the repository hosted at this site is a read-only copy of an external repository.
   text_settings_repo_need_help: Please <a href="/projects/soundsoftware-site/wiki/Help">contact us</a> if you need help deciding how best to set this up.<br>We can also import complete revision history from other systems into a new primary repository for you if you wish.
+  text_has_welcome_page_info: <b>Welcome page</b><p>You can replace the standard {{overview_link}} page for this project with your own welcome page.<br>This page will be editable using the project Wiki.
+  text_has_welcome_page_wiki_disabled: <b>Note:</b> You must enable the Wiki module in the {{modules_link}} tab before you can create or edit this page.
 
  
   default_role_manager: Manager
--- a/config/routes.rb	Thu Apr 28 13:29:15 2011 +0100
+++ b/config/routes.rb	Thu Apr 28 13:36:40 2011 +0100
@@ -138,6 +138,7 @@
     :copy => [:get, :post],
     :settings => :get,
     :modules => :post,
+    :overview => :post,
     :archive => :post,
     :unarchive => :post
   } do |project|
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/migrate/20110331152140_add_has_welcome_page_to_projects.rb	Thu Apr 28 13:36:40 2011 +0100
@@ -0,0 +1,9 @@
+class AddHasWelcomePageToProjects < ActiveRecord::Migration
+  def self.up
+    add_column :projects, :has_welcome_page, :boolean
+  end
+
+  def self.down
+    remove_column :projects, :has_welcome_page
+  end
+end
--- a/extra/soundsoftware/extract-docs.sh	Thu Apr 28 13:29:15 2011 +0100
+++ b/extra/soundsoftware/extract-docs.sh	Thu Apr 28 13:36:40 2011 +0100
@@ -12,6 +12,7 @@
 redgrp="redmine"
 
 apikey=""
+apischeme="https"
 apihost=""
 apiuser=""
 apipass=""
@@ -36,9 +37,9 @@
     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 ""
+	    sudo -u docgen curl -u "$apiuser":"$apipass" "$apischeme://$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 ""
+	    sudo -u docgen curl "$apischeme://$apihost/sys/projects/$p/embedded.xml?enable=1&key=$apikey" -d ""
 	fi
     else 
 	echo "Can't enable Embedded, API not configured" 1>&2
--- a/lib/redmine.rb	Thu Apr 28 13:29:15 2011 +0100
+++ b/lib/redmine.rb	Thu Apr 28 13:36:40 2011 +0100
@@ -48,7 +48,7 @@
   map.permission :view_project, {:projects => [:show], :activities => [:index]}, :public => true
   map.permission :search_project, {:search => :index}, :public => true
   map.permission :add_project, {:projects => [:new, :create]}, :require => :loggedin
-  map.permission :edit_project, {:projects => [:settings, :edit, :update]}, :require => :member
+  map.permission :edit_project, {:projects => [:settings, :edit, :update, :overview]}, :require => :member
   map.permission :select_project_modules, {:projects => :modules}, :require => :member
   map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy, :autocomplete_for_member]}, :require => :member
   map.permission :manage_versions, {:projects => :settings, :versions => [:new, :create, :edit, :update, :close_completed, :destroy]}, :require => :member
@@ -185,6 +185,7 @@
 
 Redmine::MenuManager.map :project_menu do |menu|
   menu.push :overview, { :controller => 'projects', :action => 'show' }
+  menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
   menu.push :activity, { :controller => 'activities', :action => 'index' }
   menu.push :roadmap, { :controller => 'versions', :action => 'index' }, :param => :project_id,
               :if => Proc.new { |p| p.shared_versions.any? }
@@ -193,7 +194,6 @@
               :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) }
   menu.push :gantt, { :controller => 'gantts', :action => 'show' }, :param => :project_id, :caption => :label_gantt
   menu.push :calendar, { :controller => 'calendars', :action => 'show' }, :param => :project_id, :caption => :label_calendar
-  menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
   menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
   menu.push :wiki, { :controller => 'wiki', :action => 'show', :id => nil }, :param => :project_id,
               :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
--- a/public/stylesheets/application.css	Thu Apr 28 13:29:15 2011 +0100
+++ b/public/stylesheets/application.css	Thu Apr 28 13:36:40 2011 +0100
@@ -216,6 +216,7 @@
 h3.version { background: url(../images/package.png) no-repeat 0% 50%; padding-left: 20px; }
 
 div.issues h3 { background: url(../images/ticket.png) no-repeat 0% 50%; padding-left: 20px; }
+div.activity h3 { background: url(../images/ticket.png) no-repeat 0% 50%; padding-left: 20px; }
 div.members h3 { background: url(../images/group.png) no-repeat 0% 50%; padding-left: 20px; }
 div.news h3 { background: url(../images/news.png) no-repeat 0% 50%; padding-left: 20px; }
 div.projects h3 { background: url(../images/projects.png) no-repeat 0% 50%; padding-left: 20px; }
@@ -293,15 +294,19 @@
 div#issue-changesets div.changeset { border-bottom: 1px solid #ddd; }
 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
 
-div#activity dl, #search-results { margin-left: 2em; }
-div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
-div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
-div#activity dt.me .time { border-bottom: 1px solid #999; }
-div#activity dt .time { color: #777; font-size: 80%; }
+div#activity dl, div#news dl, #search-results { margin-left: 2em; }
+div#activity .box dl { margin-left: 0; }
+div#activity dd, div#news dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
+div#activity dt, div#news dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
+div#activity dt.me .time, div#news dt.me .time { border-bottom: 1px solid #999; }
+div#activity dt .time, div#news dt .time, .projects .latest .time { color: #777; font-size: 80%; }
 div#activity dd .description, #search-results dd .description { font-style: italic; }
-div#activity span.project:after, #search-results span.project:after { content: " -"; }
+div#activity span.project:after, div#news span.project:after, #search-results span.project:after { content: " -"; }
 div#activity dd span.description, #search-results dd span.description { display:block; color: #808080; }
 
+.projects .latest .title { margin-right: 0.5em; }
+.tipoftheday .tip { margin-left: 2em; margin-top: 0.5em; }
+
 #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; }
 
 div#search-results-counts { display: block; padding-left: 0; margin-left: 0; }
@@ -352,6 +357,8 @@
 ul.projects div.root a.project { /* font-family: "Trebuchet MS", Verdana, sans-serif; */ font-weight: bold; font-size: 16px; margin: 0 0 10px 0; }
 /* .my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; } */
 
+li.latest { margin-bottom: 0.5em; }
+
 #tracker_project_ids ul { margin: 0;  padding-left: 1em; }
 #tracker_project_ids li { list-style-type:none; } 
 
--- a/public/themes/soundsoftware/stylesheets/application.css	Thu Apr 28 13:29:15 2011 +0100
+++ b/public/themes/soundsoftware/stylesheets/application.css	Thu Apr 28 13:36:40 2011 +0100
@@ -116,13 +116,14 @@
 #header {  position: absolute; z-index: 0; top: 0; width: 100%; background: #fdfbf5; border-bottom: 2px solid #a9b680; /* height:80px; */ padding: 20px 0 0.5em 0; margin-bottom: 0; }
 #header a { color: #be5700; }
 #header h1 { color: #525a38; margin-top: 25px; font-size: 3em; font-weight: normal; margin-left: 10px; }
+#header #project-title a, #header #project-title a:hover { color: #525a38; text-decoration: none; }
 .header-general h1 {
   background: url('soundsoftware-logo-title-only-transparent-beta.png') no-repeat 0 0;
   text-indent: -9999px;
   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 {
--- a/vendor/plugins/redmine_checkout/app/views/redmine_checkout_hooks/_view_repositories_show_contextual.rhtml	Thu Apr 28 13:29:15 2011 +0100
+++ b/vendor/plugins/redmine_checkout/app/views/redmine_checkout_hooks/_view_repositories_show_contextual.rhtml	Thu Apr 28 13:36:40 2011 +0100
@@ -20,7 +20,11 @@
 
     <p>
     <% if User.current.logged? %>
+      <% if repository.is_external? %>
+      <%=l :label_access_type_all, :type => l(:label_access_read_only) %>
+      <% else %>
       <% if default_protocol %><%=l :label_access_type, :type => l(default_protocol.access_label(User.current)) %><% end %>
+      <% end %>
     <% else %>
       &nbsp;
     <% end %>
--- a/vendor/plugins/redmine_checkout/config/locales/en.yml	Thu Apr 28 13:29:15 2011 +0100
+++ b/vendor/plugins/redmine_checkout/config/locales/en.yml	Thu Apr 28 13:36:40 2011 +0100
@@ -17,6 +17,7 @@
   button_add_protocol: "Add Protocol"
 
   label_access_type: 'You have <span id="checkout_access">{{type}}</span> access to this URL.'
+  label_access_type_all: 'All access to this URL is <span id="checkout_access">{{type}}</span>.'
   label_access_read_only: 'Read Only'
   label_access_read_write: "Read and Write"
   label_access_permission: "Depending on user's permissions"
@@ -38,5 +39,5 @@
   help_moved_settings: "The settings page has been moved to {{link}}."
   label_settings_location: "Administration -> Settings -> Checkout"
 
-  text_repository_external: "The primary repository for this project is hosted at <code>{{location}}</code> .<br>This repository is a read-only copy which is updated automatically."
+  text_repository_external: "The primary repository for this project is hosted at <code>{{location}}</code> .<br>This repository is a read-only copy which is updated automatically every hour."