Index: test/unit/helpers/issue_schedules_helper_test.rb
===================================================================
--- test/unit/helpers/issue_schedules_helper_test.rb	(revision 0)
+++ test/unit/helpers/issue_schedules_helper_test.rb	(revision 0)
@@ -0,0 +1,4 @@
+require 'test_helper'
+
+class IssueSchedulesHelperTest < ActionView::TestCase
+end
Index: test/unit/issue_schedule_test.rb
===================================================================
--- test/unit/issue_schedule_test.rb	(revision 0)
+++ test/unit/issue_schedule_test.rb	(revision 0)
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class IssueScheduleTest < ActiveSupport::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end
Index: test/functional/issue_schedules_controller_test.rb
===================================================================
--- test/functional/issue_schedules_controller_test.rb	(revision 0)
+++ test/functional/issue_schedules_controller_test.rb	(revision 0)
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class IssueSchedulesControllerTest < ActionController::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end
Index: test/fixtures/issue_schedules.yml
===================================================================
--- test/fixtures/issue_schedules.yml	(revision 0)
+++ test/fixtures/issue_schedules.yml	(revision 0)
@@ -0,0 +1,23 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+one:
+  project_id: 1
+  issue_id: 1
+  assigned_to_id: 1
+  author_id: 1
+  subject: MyString
+  interval_number: 1
+  interval_units: MyString
+  last_assigned_date: 2012-02-14 14:26:40
+  next_run_date: 2012-02-14 14:26:40
+
+two:
+  project_id: 1
+  issue_id: 1
+  assigned_to_id: 1
+  author_id: 1
+  subject: MyString
+  interval_number: 1
+  interval_units: MyString
+  last_assigned_date: 2012-02-14 14:26:40
+  next_run_date: 2012-02-14 14:26:40
Index: app/helpers/issue_schedules_helper.rb
===================================================================
--- app/helpers/issue_schedules_helper.rb	(revision 0)
+++ app/helpers/issue_schedules_helper.rb	(revision 0)
@@ -0,0 +1,2 @@
+module IssueSchedulesHelper
+end
Index: app/models/journal.rb
===================================================================
--- app/models/journal.rb	(revision 8667)
+++ app/models/journal.rb	(working copy)
@@ -85,4 +85,16 @@
   def notify=(arg)
     @notify = arg
   end
+  # copies a set of journal notes  from one object to another
+  def self.copy_notes(objToId, objFromId)
+   transaction do
+      connection.insert "INSERT INTO #{Journal.table_name} (journalized_id,journalized_type,user_id,notes,created_on)" +
+                              " SELECT #{objToId}, journalized_type,user_id,notes,created_on" +
+                              " FROM #{Journal.table_name}" +
+                              " WHERE journalized_id = #{objFromId}"
+       
+     
+    end
+    true
+  end
 end
Index: app/models/issue_schedule.rb
===================================================================
--- app/models/issue_schedule.rb	(revision 0)
+++ app/models/issue_schedule.rb	(revision 0)
@@ -0,0 +1,4 @@
+class IssueSchedule < ActiveRecord::Base
+  belongs_to :issue
+  
+end
Index: app/models/issue.rb
===================================================================
--- app/models/issue.rb	(revision 8667)
+++ app/models/issue.rb	(working copy)
@@ -29,6 +29,7 @@
 
   has_many :journals, :as => :journalized, :dependent => :destroy
   has_many :time_entries, :dependent => :delete_all
+  has_many :issue_schedules, :dependent=> :delete_all
   has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
 
   has_many :relations_from, :class_name => 'IssueRelation', :foreign_key => 'issue_from_id', :dependent => :delete_all
@@ -50,6 +51,7 @@
                             :author_key => :author_id
 
   DONE_RATIO_OPTIONS = %w(issue_field issue_status)
+  INTERVAL_UNITS = ['day','week', 'month']
 
   attr_reader :current_journal
 
@@ -86,7 +88,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 a SQL conditions string used to find all issues visible by the specified user
   def self.visible_condition(user, options={})
     Project.allowed_to_condition(user, :view_issues, options) do |role, user|
@@ -141,7 +143,50 @@
     self.status = issue.status
     self
   end
-
+  def copy_attachment(arg)
+   
+    issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg)
+    
+    if issue.attachments.any?
+      Attachment.copy_files(id,issue.id)
+    end
+   
+  end
+  
+  def copy_notes(arg)
+    issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg)
+    
+    if issue.journals.present?
+      Journal.copy_notes(id,issue.id)
+    end
+    
+  end
+  
+   # repeats and schedule issues
+  def repeat_issue(params)
+    
+    Issue.transaction do
+       #if User.current.allowed_to?(:schedule_issue, project)
+          @schedule_entry = IssueSchedule.new
+          @schedule_entry.project_id = project
+          @schedule_entry.tracker_id = self.tracker_id
+          @schedule_entry.issue_id = self.id
+          @schedule_entry.assigned_to_id = self.assigned_to_id
+          @schedule_entry.author_id = self.author_id
+          @schedule_entry.subject = self.subject
+          @schedule_entry.interval_number = params[:issue][:issue_schedules][:interval_number]
+          @schedule_entry.interval_units = params[:issue][:issue_schedules][:interval_units]
+          @schedule_entry.last_assigned_date = User.current.today
+          @schedule_entry.next_run_date = params[:issue][:issue_schedules][:next_run_date]
+          self.issue_schedules << @schedule_entry
+       #end
+      end
+    true
+   
+  end
+  
+ 
+  
   # Moves/copies an issue to a new project and tracker
   # Returns the moved/copied issue on success, false on failure
   def move_to_project(*args)
Index: app/models/attachment.rb
===================================================================
--- app/models/attachment.rb	(revision 8667)
+++ app/models/attachment.rb	(working copy)
@@ -58,6 +58,7 @@
   def file=(incoming_file)
     unless incoming_file.nil?
       @temp_file = incoming_file
+      
       if @temp_file.size > 0
         self.filename = sanitize_filename(@temp_file.original_filename)
         self.disk_filename = Attachment.disk_filename(filename)
@@ -149,6 +150,7 @@
     if attachments && attachments.is_a?(Hash)
       attachments.each_value do |attachment|
         file = attachment['file']
+        logger.info("filename '#{file}'")
         next unless file && file.size > 0
         a = Attachment.create(:container => obj,
                               :file => file,
@@ -172,12 +174,24 @@
       |att| att.filename.downcase == filename.downcase
      }
   end
+  
+   # copies a set of files attached from one object to another
+  def self.copy_files(objToId, objFromId)
+   transaction do
+       connection.insert "INSERT INTO #{Attachment.table_name} (container_id,container_type,filename,disk_filename,filesize,content_type,digest,downloads,author_id,created_on,description)" +
+                              " SELECT #{objToId}, container_type,filename,disk_filename,filesize,content_type,digest,downloads,author_id,created_on,description" +
+                              " FROM #{Attachment.table_name}" +
+                              " WHERE container_id = #{objFromId}"
+    end
+    true
+  end
 
 private
   def sanitize_filename(value)
     # get only the filename, not the whole path
+   
     just_filename = value.gsub(/^.*(\\|\/)/, '')
-
+    
     # Finally, replace invalid characters with underscore
     @filename = just_filename.gsub(/[\/\?\%\*\:\|\"\'<>]+/, '_')
   end

Index: app/controllers/issues_controller.rb
===================================================================
--- app/controllers/issues_controller.rb	(revision 8667)
+++ app/controllers/issues_controller.rb	(working copy)
@@ -123,6 +123,7 @@
     @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
     @priorities = IssuePriority.active
     @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
+   
     respond_to do |format|
       format.html { render :template => 'issues/show' }
       format.api
@@ -143,6 +144,7 @@
   def create
     call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
     if @issue.save
+      
       attachments = Attachment.attach_files(@issue, params[:attachments])
       call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
       respond_to do |format|
@@ -175,10 +177,12 @@
   end
 
   def update
+    @issue.repeat_issue(params) if params[:repeat]
     update_issue_from_params
 
     if @issue.save_issue_with_child_records(params, @time_entry)
       render_attachment_warning_if_needed(@issue)
+      
       flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
 
       respond_to do |format|
@@ -195,6 +199,7 @@
         format.api  { render_validation_errors(@issue) }
       end
     end
+    
   end
 
   # Bulk edit a set of issues
@@ -293,6 +298,7 @@
     @notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)
     @issue.init_journal(User.current, @notes)
     @issue.safe_attributes = params[:issue]
+   
   end
 
   # TODO: Refactor, lots of extra code in here
Index: app/controllers/issue_schedules_controller.rb
===================================================================
--- app/controllers/issue_schedules_controller.rb	(revision 0)
+++ app/controllers/issue_schedules_controller.rb	(revision 0)
@@ -0,0 +1,84 @@
+class IssueSchedulesController < ApplicationController
+   unloadable
+
+
+  before_filter :find_project
+  before_filter :find_issueschedule, :except => [:new, :index]
+  before_filter :load_users, :except => [:destroy]
+
+  def index
+    if !params[:project_id] then return end
+    @project_identifier = params[:project_id]
+    @project = Project.find(params[:project_id])
+    @issue_schedule = IssueSchedule.find_all_by_project_id(@project[:id])
+  end
+  def update
+    if !params[:project_id] then return end
+    @issue_schedule = IssueSchedule.find(params[:id])
+    @issue_schedule.update_attributes(params[:issue_schedules])
+   
+      if @issue_schedule.save
+        flash[:notice] = "Issue Schedule is saved"
+        redirect_to :controller => 'issue_schedules', :action => 'index', :project_id=>params[:project_id]
+      end
+  end
+  def new
+    if !params[:project_id] then return end
+    @project = Project.find(params[:project_id])
+    @issue_schedule = IssueSchedule.new(:project=>@project, :author_id=>User.current.id)
+    if request.post?
+      params[:issue_schedules][:project_id] = @project[:id]
+      @issue_schedule.attributes = params[:issue_schedules]
+      if @issue_schedule.save
+        flash[:notice] = "Issue Schedule is saved"
+        redirect_to :controller => 'issue_schedules', :action => 'index', :project_id=>params[:project_id]
+      end
+    end
+
+  end
+
+  def edit
+    if request.post?
+      @issue_schedule = IssueSchedule.find(params[:id])
+      params[:issue_schedules][:project_id] = params[:project_id]
+      if request.post?
+        if @issue_schedule.update_attributes(params[:issue_schedules])
+          flash[:notice] = "saved issue schedule"
+          redirect_to :action => 'index', :project_id => params[:project_id]
+        end
+      end
+    end
+  end
+
+  def destroy
+      @issue_schedule = IssueSchedule.find(params[:id])
+      @issue_schedule.destroy
+      redirect_to :action => 'index', :project_id => params[:project_id]
+  end
+private
+  def find_issueschedule
+    @issue_schedule = IssueSchedule.find(params[:id])
+  rescue ActiveRecord::RecordNotFound
+    render_404
+  end
+  def find_project
+    @project = Project.find(params[:project_id])
+  end
+  
+  def load_users
+    # Get the users that are members in the project
+    #
+    #@users = User.find_by_sql('SELECT users.id, CONCAT(users.firstname, \' \', users.lastname) fullname FROM members INNER JOIN users
+    #  ON members.user_id = users.id
+    #  WHERE project_id = ' + @project[:id].to_s + '
+    #  AND status = 1
+    #  ORDER BY firstname ASC')
+    @users = []
+    @project.members.each do |m|
+      @users << m.user
+    end
+
+  end
+  
+  
+end

Index: app/views/issue_schedules/edit.html.erb
===================================================================
--- app/views/issue_schedules/edit.html.erb	(revision 0)
+++ app/views/issue_schedules/edit.html.erb	(revision 0)
@@ -0,0 +1,9 @@
+<h2><%= l(:label_issue_schedules_edit) %></h2>
+<p>Use this form to edit this task.</p>
+<% labelled_tabular_form_for :issue_schedules, @issue_schedule, 
+  :url => { :controller => 'issue_schedules', :action => 'update', :id=>@issue_schedule.id, :project_id => @project },
+  :html => { :id => 'task-form' } do |f| %>
+<%= render :partial => 'issue_schedules/form', :locals => { :f => f } %>
+<%= submit_tag l(:button_update) %>
+<%= link_to l(:button_cancel), :controller => 'issue_schedules',:action => 'index', :project_id => @project%>
+<% end if @project %>
Index: app/views/issue_schedules/index.html.erb
===================================================================
--- app/views/issue_schedules/index.html.erb	(revision 0)
+++ app/views/issue_schedules/index.html.erb	(revision 0)
@@ -0,0 +1,34 @@
+
+<% if @project_identifier %>
+
+<h2>Issue Schedules</h2>
+<p>These are the currently scheduled issues</p>
+<table class="list">
+  <thead>
+    <tr>
+      <th width="40%" align="left"><%= l(:label_subject) %></th><th align="left" width="30%"><%= l(:label_next_run_date) %></th><th>&nbsp</th><th>&nbsp</th>
+    </tr>
+  </thead>
+  <tbody>
+    <% @issue_schedule.each do |a| %>
+    <tr class="<%= cycle('odd', 'even') %>">
+      <td align="left" width="40%"><%= a.subject %></td>
+      <td align="left" width="30%"><%= a.next_run_date %></td>
+      <td>
+  <%= link_to l(:button_edit), 
+                            {:controller => 'issue_schedules', :action => 'edit', :id => a.id, :project_id => @project},
+                            :class => 'icon icon-edit',
+                            :accesskey => accesskey(:edit),
+                            :onclick => 'Element.show("edit-task"); return false;' %>
+      </td>
+      <td>
+  <%= link_to l(:button_delete), {:controller => 'issue_schedules', :action => 'destroy', :id => a.id, :project_id => @project}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
+      </td>
+    </tr>
+    <% end %>
+  </tbody>
+</table>
+
+<% else %>
+  <p>No project ID supplied</p>
+<% end %>
Index: app/views/issue_schedules/destroy.html.erb
===================================================================
--- app/views/issue_schedules/destroy.html.erb	(revision 0)
+++ app/views/issue_schedules/destroy.html.erb	(revision 0)
@@ -0,0 +1 @@
+<h2>IssueSchedule#destroy</h2>
Index: app/views/issue_schedules/new.html.erb
===================================================================
--- app/views/issue_schedules/new.html.erb	(revision 0)
+++ app/views/issue_schedules/new.html.erb	(revision 0)
@@ -0,0 +1 @@
+<h2>IssueSchedule#new</h2>
Index: app/views/issue_schedules/_form.html.erb
===================================================================
--- app/views/issue_schedules/_form.html.erb	(revision 0)
+++ app/views/issue_schedules/_form.html.erb	(revision 0)
@@ -0,0 +1,21 @@
+<%= error_messages_for 'issue_schedules' %>
+<%= f.hidden_field :id %>
+<div class="box tabular">
+<p><%= f.text_field :subject, :required => true, :size => 60 %></p>
+<p>
+ 
+  <% if @users.length > 0 %>
+    <p><%= f.select :assigned_to_id, @users.collect {|t| [t.name, t.id]}, :required => true %></p>
+
+  <% else %>
+    <%= l(:no_members_in_project) %>
+  <% end %>
+</p>
+<p><%= f.text_field :interval_number, :required=> true, :size => 3 %> </p>
+<p><%= f.select :interval_units, Issue::INTERVAL_UNITS %></p>  
+<p><%= f.select :tracker_id, @project.trackers.collect {|t| [t.name, t.id]}, :required => true %></p>
+
+<P><%= f.text_field :next_run_date %><%=calendar_for('issue_schedules_next_run_date')%></p> 
+</div>
+
+
Index: app/views/issues/_sidebar.html.erb
===================================================================
--- app/views/issues/_sidebar.html.erb	(revision 8667)
+++ app/views/issues/_sidebar.html.erb	(working copy)
@@ -11,6 +11,7 @@
 <% if User.current.allowed_to?(:view_gantt, @project, :global => true) %>
   <%= link_to(l(:label_gantt), :controller => 'gantts', :action => 'show', :project_id => @project) %><br />
 <% end %>
+ <%= link_to l(:label_issue_schedules), { :controller => 'issue_schedules', :action => 'index', :project_id => @project} %><br />
 <%= call_hook(:view_issues_sidebar_planning_bottom) %>
 
 <%= render_sidebar_queries %>
Index: app/views/issues/show.html.erb
===================================================================
--- app/views/issues/show.html.erb	(revision 8667)
+++ app/views/issues/show.html.erb	(working copy)
@@ -108,6 +108,14 @@
   </div>
 <% end %>
 
+<div style="clear: both;"></div>
+<% if authorize_for('issues', 'edit') %>
+
+  <div id="repeat" style="display:none;">
+   <h3><%= l(:button_repeat) %></h3>
+  <%= render :partial => 'repeatschedule' %>
+  </div>
+<% end %>
 <% other_formats_links do |f| %>
   <%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
   <%= f.link_to 'PDF' %>
Index: app/views/issues/_edit.html.erb
===================================================================
--- app/views/issues/_edit.html.erb	(revision 8667)
+++ app/views/issues/_edit.html.erb	(working copy)
@@ -1,5 +1,5 @@
 <% labelled_tabular_form_for :issue, @issue,
-                             :url => {:action => 'update', :id => @issue},
+                             :url => {:action => 'update',:id => @issue},
                              :html => {:id => 'issue-form',
                                        :class => nil,
                                        :method => :put,
@@ -31,6 +31,7 @@
         <% end %>
     </fieldset>
     <% end %>
+  
 
     <fieldset><legend><%= l(:field_notes) %></legend>
     <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
Index: app/views/issues/_action_menu.html.erb
===================================================================
--- app/views/issues/_action_menu.html.erb	(revision 8667)
+++ app/views/issues/_action_menu.html.erb	(working copy)
@@ -1,8 +1,9 @@
 <div class="contextual">
-<%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'showAndScrollTo("update", "notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %>
+<%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'setVisible("repeat",false);showAndScrollTo("update", "notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %>
 <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue}, :class => 'icon icon-time-add' %>
 <%= watcher_tag(@issue, User.current) %>
 <%= link_to_if_authorized l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-duplicate' %>
+<%= link_to_if_authorized l(:button_repeat), {:controller => 'issues', :action => 'edit', :project_id => @project, :repeat => @issue },:onclick => 'setVisible("update",false);showAndScrollTo("repeat", "notes"); return false;', :class => 'icon icon-duplicate' %>
 <%= link_to_if_authorized l(:button_copy), {:controller => 'issue_moves', :action => 'new', :id => @issue, :copy_options => {:copy => 't'}}, :class => 'icon icon-copy' %>
 <%= link_to_if_authorized l(:button_move), {:controller => 'issue_moves', :action => 'new', :id => @issue}, :class => 'icon icon-move' %>
 <%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => issues_destroy_confirmation_message(@issue), :method => :post, :class => 'icon icon-del' %>
Index: config/locales/en.yml
===================================================================
--- config/locales/en.yml	(revision 8667)
+++ config/locales/en.yml	(working copy)
@@ -317,6 +317,7 @@
   field_root_directory: Root directory
   field_cvsroot: CVSROOT
   field_cvs_module: Module
+  field_interval_units: Interval Units
 
   setting_app_title: Application title
   setting_app_subtitle: Application subtitle
@@ -464,6 +465,8 @@
   label_issue_new: New issue
   label_issue_plural: Issues
   label_issue_view_all: View all issues
+  label_issue_schedules: View issue schedule
+  label_issue_schedules_edit: Edit issue schedule
   label_issues_by: "Issues by %{value}"
   label_issue_added: Issue added
   label_issue_updated: Issue updated
@@ -689,6 +692,8 @@
   label_changes_details: Details of all changes
   label_issue_tracking: Issue tracking
   label_spent_time: Spent time
+  label_interval_number: Interval Number
+  label_next_run_date: Next Run Date
   label_overall_spent_time: Overall spent time
   label_f_hour: "%{value} hour"
   label_f_hour_plural: "%{value} hours"
@@ -859,6 +864,7 @@
   button_activate: Activate
   button_sort: Sort
   button_log_time: Log time
+  button_repeat_detail: Schedule Issue
   button_rollback: Rollback to this version
   button_watch: Watch
   button_unwatch: Unwatch
@@ -875,6 +881,7 @@
   button_configure: Configure
   button_quote: Quote
   button_duplicate: Duplicate
+  button_repeat: Repeat
   button_show: Show
   button_edit_section: Edit this section
   button_export: Export
Index: lib/tasks/task_scheduler.rake
===================================================================
--- lib/tasks/task_scheduler.rake	(revision 8667)
+++ lib/tasks/task_scheduler.rake	(working copy)
@@ -1,9 +1,7 @@
 namespace :redmine do
-  desc "List all permissions and the actions registered with them"
-  task :permissions => :environment do
-    puts "Permission Name - controller/action pairs"
-    Redmine::AccessControl.permissions.sort {|a,b| a.name.to_s <=> b.name.to_s }.each do |permission|
-      puts ":#{permission.name} - #{permission.actions.join(', ')}"
-    end
+  desc "Schedule issues"
+  task :check_issueschedule => :environment do
+      #puts "Scheduling Issues"
+      ScheduledTasksChecker.checktasks!
   end
 end
Index: lib/scheduled_tasks_checker.rb
===================================================================
--- lib/scheduled_tasks_checker.rb	(revision 0)
+++ lib/scheduled_tasks_checker.rb	(revision 0)
@@ -0,0 +1,18 @@
+class ScheduledTasksChecker
+  def self.checktasks!
+   IssueSchedule.all(:conditions=> ["next_run_date <= ? ", Time.now.to_date]).each do |task| 
+        # print "assigning #{task.subject}"
+         issue = Issue.new(:project_id=>task.project_id,:tracker_id=>task.tracker_id, :assigned_to_id=>task.assigned_to_id, :author_id=>task.author_id, :subject=>task.subject);
+         issue.save
+          
+         copy_issue = Issue.find_by_id(task.issue_id)
+         issue.copy_from(copy_issue)
+         issue.copy_attachment(copy_issue)
+         issue.copy_notes(copy_issue)
+         interval = task.interval_number
+         units = task.interval_units
+         task.next_run_date =  interval.send(units.downcase).from_now
+         task.save
+     end
+  end
+end
