ari/jobsworth

View on GitHub
app/controllers/feeds_controller.rb

Summary

Maintainability
F
4 days
Test Coverage
# encoding: UTF-8
# Provide a RSS feed of Project WorkLog activities.

require 'rss/maker'
require 'icalendar'
require 'google_chart'

class FeedsController < ApplicationController
  include Icalendar
  include TaskFilterHelper

  # Get the RSS feed, based on the secret key passed on the url
  def rss
    return if params[:id].blank?

    headers['Content-Type'] = 'application/rss+xml'

    # Lookup user based on the secret key
    user = User.where('uuid = ?', params[:id]).first

    if user.nil?
      render :nothing => true, :layout => false
      return
    end

    content = nil
    if params[:widget].blank?
      # Find all Project ids this user has access to
      pids = user.projects

      # Find 50 last WorkLogs of the Projects
      if pids.nil? || pids.empty?
        @activities = []
      else
        pids = pids.collect { |p| p.id }
        @activities = WorkLog.accessed_by(user).order('work_logs.started_at DESC').limit(50).includes(:customer, :task)
      end

      # Create the RSS
      content = RSS::Maker.make('2.0') do |m|
        m.channel.title = "#{user.company.name} Activities"
        m.channel.link = "#{user.company.site_URL}/activities"
        m.channel.description = "Last changes for #{user.name}@#{user.company.name}."
        m.items.do_sort = true # sort items by date

        @activities.each do |log|
          action = get_action(log)

          i = m.items.new_item
          i.title = " #{action}: #{log.task.issue_name}" unless log.task.nil?
          i.title ||= "#{action}"
          i.link = "#{user.company.site_URL}/tasks/view/#{log.task.task_num}" unless log.task.nil?
          i.description = log.body unless log.body.blank?
          i.date = log.started_at.utc
          i.author = log.user.name unless log.user.nil?
          action = nil
        end
      end
      @activities = nil
    else
      widget = user.widgets.find(params[:widget]) rescue nil

      if widget
        filter = ''
        if widget.filter_by?
          filter = widget.filter_from_filter_by
        end
        pids = user.projects.collect { |p| p.id }

        if widget.mine?
          sql = ActiveRecord::Base.send(:sanitize_sql_array, (["tasks.project_id IN (?) #{filter} AND tasks.completed_at IS NULL AND (tasks.hide_until IS NULL OR tasks.hide_until < ?)", pids, user.tz.now.utc.to_s(:db)]))
          tasks = user.tasks.where(sql)
        else
          sql = ActiveRecord::Base.send(:sanitize_sql_array, (["tasks.completed_at IS NULL #{filter} AND (tasks.hide_until IS NULL OR tasks.hide_until < ?)", user.tz.now.utc.to_s(:db)]))
          tasks = TaskRecord.accessed_by(user).where(sql)
        end

        tasks = case widget.order_by
                  when 'priority' then
                    user.company.sort(tasks)[0, widget.number]
                  when 'date' then
                    tasks.sort_by { |t| t.created_at.to_i }[0, widget.number]
                end

        # Create the RSS
        content = RSS::Maker.make('2.0') do |m|
          m.channel.title = widget.name
          m.channel.link = "#{user.company.site_URL}/tasks"
          m.channel.description = widget.name
          m.items.do_sort = true # sort items by date
          tasks.each do |task|
            i = m.items.new_item
            i.title = "#{task.issue_name}"
            i.link = "#{user.company.site_URL}/tasks/view/#{task.task_num}"
            i.description = task.description unless task.description.blank?
            i.date = task.created_at.utc
            i.author = task.creator.name unless task.creator.nil?
          end
        end
      else
        content = '<?xml version="1.0" encoding="UTF-8"?>
        <rss version="2.0"
          xmlns:content="http://purl.org/rss/1.0/modules/content/"
          xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
          xmlns:dc="http://purl.org/dc/elements/1.1/"
          xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
          <channel>
            <title>No such widget</title>
            <link>#{user.company.site_URL}</link>
            <description>No such widget.</description>
          </channel>
        </rss>'
      end
    end
    # Render it inline
    render :text => content.to_s
    content = nil
    user = nil
  end

  def ical(mode = :personal)
    if params[:id].nil? || params[:id].empty?
      render :nothing => true
      return
    end

    I18n.locale = :en

    headers['Content-Type'] = 'text/calendar'

    # Lookup user based on the secret key
    user = User.where('uuid = ?', params[:id]).first

    if user.nil?
      render :nothing => true, :layout => false
      return
    end

    tz = TZInfo::Timezone.get(user.time_zone)

    cached = []

    # Find all Project ids this user has access to
    pids = user.projects

    # Find 50 last WorkLogs of the Projects
    unless pids.nil? || pids.empty?
      pids = pids.collect { |p| p.id }
      if mode == :all

        if params['mode'].nil? || params['mode'] == 'logs'
          logger.info('selecting logs')
          @activities = WorkLog.accessed_by(user).where('work_logs.task_id > 0 AND work_logs.duration > 0').includes({task: :tags}, :ical_entry)
        end

        if params['mode'].nil? || params['mode'] == 'tasks'
          logger.info('selecting tasks')
          @tasks = TaskRecord.accessed_by(user).includes(:milestone, :tags, :task_users, :ical_entry)
        end

      else

        if params['mode'].nil? || params['mode'] == 'logs'
          logger.info('selecting personal logs')
          @activities = WorkLog.accessed_by(user).where('work_logs.user_id = ? AND work_logs.task_id > 0 AND work_logs.duration > 0', user.id).includes({:task => :tags}, :ical_entry)
        end

        if params['mode'].nil? || params['mode'] == 'tasks'
          logger.info('selecting personal tasks')
          @tasks = user.tasks.where('tasks.project_id IN (?)', pids).includes(:milestone, :tags, :task_users, :users, :ical_entry)
        end
      end

      if params['mode'].nil? || params['mode'] == 'milestones'
        logger.info('selecting milestones')
        @milestones = Milestone.where('company_id = ? AND project_id IN (?) AND due_at IS NOT NULL', user.company_id, pids)
      end

    end

    @activities ||= []
    @tasks ||= []
    @milestones ||= []

    cal = Calendar.new

    @milestones.each do |m|
      event = cal.event

      if m.completed_at
        event.start = to_localtime(tz, m.completed_at).beginning_of_day + 8.hours
      else
        event.start = to_localtime(tz, m.due_at).beginning_of_day + 8.hours
      end
      event.duration = 'PT480M'
      event.uid = "m#{m.id}_#{event.created}@#{user.company.subdomain}.#{Setting.domain}"
      event.organizer = "MAILTO:#{m.user.nil? ? user.email : m.user.email}"
      event.url = user.company.site_URL + path_to_tasks_filtered_by(m)
      event.summary = "Milestone: #{m.name}"

      if m.description
        description = m.description.gsub(/<[^>]*>/, '')
        description.gsub!(/\r/, '') if description
        event.description = description if description && description.length > 0
      end
    end


    @tasks.each do |t|

      if t.ical_entry
        cached << [t.ical_entry.body]
        next
      end

      todo = cal.todo

      if t.completed_at
        todo.start = to_localtime(tz, t.completed_at)
      else
        if t.due_at
          todo.start = to_localtime(tz, t.due_at - 12.hours)
        elsif t.milestone && t.milestone.due_at
          todo.start = to_localtime(tz, t.milestone.due_at - 12.hours)
        else
          todo.start = to_localtime(tz, t.created_at)
        end
      end

      todo.created = to_localtime(tz, t.created_at)
      todo.uid = "t#{t.id}_#{todo.created}@#{user.company.subdomain}.#{Setting.domain}"
      todo.organizer = "MAILTO:#{t.users.first.email}" if t.users.size > 0
      todo.url = "#{user.company.site_URL}/tasks/view/#{t.task_num}"
      todo.summary = "#{t.issue_name}"

      description = t.description.gsub(/<[^>]*>/, '').gsub(/[\r]/, '') if t.description

      todo.description = description if description && description.length > 0
      todo.categories = t.tags.collect { |tag| tag.name.upcase } if (t.tags.size > 0)
      todo.percent = 100 if t.done?

      event = cal.event
      event.start = todo.start
      event.duration = 'PT1M'
      event.created = todo.created
      event.uid = "te#{t.id}_#{todo.created}@#{user.company.subdomain}.#{Setting.domain}"
      event.organizer = todo.organizer
      event.url = todo.url
      event.summary = "#{t.issue_name} - #{t.owners}" unless t.done?
      event.summary = "#{t.status_type} #{t.issue_name} (#{t.owners})" if t.done?
      event.description = todo.description
      event.categories = t.tags.collect { |tag| tag.name.upcase } if (t.tags.size > 0)


      unless t.ical_entry
        cache = IcalEntry.new(:body => "#{event.to_ical}#{todo.to_ical}", :task_id => t.id)
        cache.save
      end

    end


    @activities.each do |log|

      if log.ical_entry
        cached << [log.ical_entry.body]
        next
      end

      event = cal.event
      event.start = to_localtime(tz, log.started_at)
      event.duration = 'PT' + (log.duration > 0 ? to_duration(log.duration) : '1M')
      event.created = to_localtime(tz, log.task.created_at) unless log.task.nil?
      event.uid = "l#{log.id}_#{event.created}@#{user.company.subdomain}.#{Setting.domain}"
      event.organizer = "MAILTO:#{log.user.email}"

      event.url = "#{user.company.site_URL}/tasks/view/#{log.task.task_num}"

      action = get_action(log)

      event.summary = "#{action}: #{log.task.issue_name} - #{log.user.name}" unless log.task.nil?
      event.summary = "#{action} #{to_duration(log.duration).downcase}: #{log.task.issue_name} - #{log.user.name}" if log.duration > 0
      event.summary ||= "#{action} - #{log.user.name}"
      description = log.body.gsub(/<[^>]*>/, '').gsub(/[\r]/, '') if log.body
      event.description = description unless description.blank?

      event.categories = log.task.tags.collect { |t| t.name.upcase } if log.task.tags.size > 0

      unless log.ical_entry
        cache = IcalEntry.new(:body => "#{event.to_ical}", :work_log_id => log.id)
        cache.save
      end

    end

    ical_feed = cal.to_ical.gsub(/END:VCALENDAR/, "#{cached.join}END:VCALENDAR").gsub(/^PERCENT:/, 'PERCENT-COMPLETE:')
    render :text => ical_feed

    ical_feed = nil
    @activities = nil
    @tasks = nil
    @milestones = nil
    tz = nil
    cached = ''
    cal = nil

    GC.start
  end

  private

  def get_action(log)
    if log.task && log.task_id > 0
      action = 'Completed' if log.event_log.event_type == EventLog::TASK_COMPLETED
      action = 'Reverted' if log.event_log.event_type == EventLog::TASK_REVERTED
      action = 'Created' if log.event_log.event_type == EventLog::TASK_CREATED
      action = 'Modified' if log.event_log.event_type == EventLog::TASK_MODIFIED
      action = 'Commented' if log.event_log.event_type == EventLog::TASK_COMMENT
      action = 'Worked' if log.event_log.event_type == EventLog::TASK_WORK_ADDED
      action = 'Archived' if log.event_log.event_type == EventLog::TASK_ARCHIVED
      action = 'Restored' if log.event_log.event_type == EventLog::TASK_RESTORED
    else
      action = 'Note created' if log.event_log.event_type == EventLog::PAGE_CREATED
      action = 'Note deleted' if log.event_log.event_type == EventLog::PAGE_DELETED
      action = 'Note modified' if log.event_log.event_type == EventLog::PAGE_MODIFIED
      action = 'Deleted' if log.event_log.event_type == EventLog::TASK_DELETED
      action = 'Commit' if log.event_log.event_type == EventLog::SCM_COMMIT
    end
    action
  end

  def to_localtime(tz, time)
    DateTime.parse(tz.utc_to_local(time).to_s)
  end

  def to_duration(dur)
    TimeParser.format_duration(dur).upcase
  end

  def ical_all
    ical(:all)
  end


end