diff options
Diffstat (limited to 'engines/support')
40 files changed, 1362 insertions, 0 deletions
| diff --git a/engines/support/Gemfile b/engines/support/Gemfile new file mode 100644 index 0000000..ad7d29b --- /dev/null +++ b/engines/support/Gemfile @@ -0,0 +1,15 @@ +source "https://rubygems.org" + +eval(File.read(File.dirname(__FILE__) + '/../common_dependencies.rb')) +eval(File.read(File.dirname(__FILE__) + '/..//ui_dependencies.rb')) + +# We require leap_web_core from here so we can use the path option. +gem "leap_web_core", :path => '../core' + +# Declare your gem's dependencies in leap_web_users.gemspec. +# Bundler will treat runtime dependencies like base dependencies, and +# development dependencies will be added by default to the :development group. +gemspec + +# To use debugger +# gem 'ruby-debug' diff --git a/engines/support/README.md b/engines/support/README.md new file mode 100644 index 0000000..c9573e6 --- /dev/null +++ b/engines/support/README.md @@ -0,0 +1 @@ +Implements a simple, clean, and easy to use help ticketing system.
\ No newline at end of file diff --git a/engines/support/Rakefile b/engines/support/Rakefile new file mode 100644 index 0000000..0e73163 --- /dev/null +++ b/engines/support/Rakefile @@ -0,0 +1,44 @@ +#!/usr/bin/env rake + +require 'rake/packagetask' +require 'rubygems/package_task' + +begin +  require 'bundler/setup' +rescue LoadError +  puts 'You must `gem install bundler` and `bundle install` to run rake tasks' +end +begin +  require 'rdoc/task' +rescue LoadError +  require 'rdoc/rdoc' +  require 'rake/rdoctask' +  RDoc::Task = Rake::RDocTask +end + +RDoc::Task.new(:rdoc) do |rdoc| +  rdoc.rdoc_dir = 'rdoc' +  rdoc.title    = 'LeapWebHelp' +  rdoc.options << '--line-numbers' +  rdoc.rdoc_files.include('README.rdoc') +  rdoc.rdoc_files.include('lib/**/*.rb') +end + +spec = eval(File.read('leap_web_help.gemspec')) +Gem::PackageTask.new(spec) do |p| +    p.gem_spec = spec +end + +Bundler::GemHelper.install_tasks + +require 'rake/testtask' + +Rake::TestTask.new(:test) do |t| +  t.libs << 'lib' +  t.libs << 'test' +  t.pattern = 'test/**/*_test.rb' +  t.verbose = false +end + + +task :default => :test diff --git a/engines/support/app/assets/javascripts/tickets.js b/engines/support/app/assets/javascripts/tickets.js new file mode 100644 index 0000000..18537aa --- /dev/null +++ b/engines/support/app/assets/javascripts/tickets.js @@ -0,0 +1,4 @@ +//$(document).ready(function () { +//  $.fn.editable.defaults.mode = 'inline'; +//  $('#subject').editable(); +//});
\ No newline at end of file diff --git a/engines/support/app/controllers/tickets_controller.rb b/engines/support/app/controllers/tickets_controller.rb new file mode 100644 index 0000000..d65ee43 --- /dev/null +++ b/engines/support/app/controllers/tickets_controller.rb @@ -0,0 +1,153 @@ +class TicketsController < ApplicationController +  include AutoTicketsPathHelper + +  respond_to :html, :json +  #has_scope :open, :type => boolean + +  before_filter :require_login, :only => [:index] +  before_filter :fetch_ticket, :only => [:show, :update, :destroy] # don't now have an edit method +  before_filter :fetch_user +  before_filter :set_title + +  def new +    @ticket = Ticket.new +    @ticket.comments.build +  end + +  def create +    @ticket = Ticket.new(params[:ticket]) + +    @ticket.comments.last.posted_by = (logged_in? ? current_user.id : nil) #protecting posted_by isn't working, so this should protect it. +    @ticket.comments.last.private = false unless admin? +    @ticket.created_by = current_user.id if logged_in? +    @ticket.email = current_user.email_address if logged_in? and current_user.email_address + +    if @ticket.save +      flash[:notice] = t(:thing_was_successfully_created, :thing => t(:ticket)) +    end + +    # cannot set this until ticket has been saved, as @ticket.id will not be set +    if !logged_in? and flash[:notice] +      flash[:notice] += " " + t(:access_ticket_text, :full_url => ticket_url(@ticket.id)) +    end +    respond_with(@ticket, :location => auto_ticket_path(@ticket)) +  end + +  def show +    @comment = TicketComment.new +    if !@ticket +      redirect_to auto_tickets_path, :alert => t(:no_such_thing, :thing => t(:ticket)) +      return +    end +  end + +  def update +    if params[:commit] == 'close' +      @ticket.is_open = false +      @ticket.save +      redirect_to_tickets +    elsif params[:commit] == 'open' +      @ticket.is_open = true +      @ticket.save +      redirect_to auto_ticket_path(@ticket) +    else +      @ticket.attributes = cleanup_ticket_params(params[:ticket]) + +      if params[:commit] == 'reply_and_close' +        @ticket.close +      end + +      if @ticket.comments_changed? +        @ticket.comments.last.posted_by = (current_user ? current_user.id : nil) +        @ticket.comments.last.private = false unless admin? +      end + +      if @ticket.changed? and @ticket.save +        flash[:notice] = t(:changes_saved) +        redirect_to_tickets +      else +        flash[:error] = @ticket.errors.full_messages.join(". ") if @ticket.changed? +        redirect_to auto_ticket_path(@ticket) +      end +    end +  end + +  def index +    @all_tickets = Ticket.search(search_options(params)) +    @tickets = @all_tickets.page(params[:page]).per(APP_CONFIG[:pagination_size]) +  end + +  def destroy +    # should we allow non-admins to delete their own tickets? i don't think necessary. +    @ticket.destroy if admin? +    redirect_to auto_tickets_path +  end + +  protected + +  def set_title +    @title = t(:tickets) +  end + +  private + +  # +  # redirects to ticket index, if appropriate. +  # otherwise, just redirects to @ticket +  # +  def redirect_to_tickets +    if logged_in? +      if params[:commit] == t(:reply_and_close) +        redirect_to auto_tickets_path +      else +        redirect_to auto_ticket_path(@ticket) +      end +    else +      # if we are not logged in, there is no index to view +      redirect_to auto_ticket_path(@ticket) +    end +  end + +  # +  # unset comments hash if no new comment was typed +  # +  def cleanup_ticket_params(ticket) +    if ticket && ticket[:comments_attributes] +      if ticket[:comments_attributes].values.first[:body].blank? +        ticket[:comments_attributes] = nil +      end +    end +    return ticket +  end + +  def ticket_access? +    @ticket and (admin? or !@ticket.created_by or (current_user and current_user.id == @ticket.created_by)) +  end + +  def fetch_ticket +    @ticket = Ticket.find(params[:id]) +    if !@ticket and admin? +      redirect_to auto_tickets_path, :alert => t(:no_such_thing, :thing => 'ticket') +      return +    end +    access_denied unless ticket_access? +  end + +  def fetch_user +    if params[:user_id] +      @user = User.find(params[:user_id]) +    end +  end + +  # +  # clean up params for ticket search +  # +  def search_options(params) +    params.merge( +      :admin_status => params[:user_id] ? 'mine' : 'all', +      :user_id      => @user ? @user.id : current_user.id, +      :is_admin     => admin? +    ) +  end + +end diff --git a/engines/support/app/designs/ticket/by_includes_post_by.js b/engines/support/app/designs/ticket/by_includes_post_by.js new file mode 100644 index 0000000..2eeac89 --- /dev/null +++ b/engines/support/app/designs/ticket/by_includes_post_by.js @@ -0,0 +1,13 @@ +// TODO: This view is only used in tests--should we keep it? +function(doc) { +  var arr = {} +  if (doc['type'] == 'Ticket' && doc.comments) { +    doc.comments.forEach(function(comment){ +      if (comment.posted_by && !arr[comment.posted_by]) { +        //don't add duplicates +        arr[comment.posted_by] = true; +        emit(comment.posted_by, 1); +      } +    }); +  } +} diff --git a/engines/support/app/designs/ticket/by_includes_post_by_and_created_at.js b/engines/support/app/designs/ticket/by_includes_post_by_and_created_at.js new file mode 100644 index 0000000..72169b0 --- /dev/null +++ b/engines/support/app/designs/ticket/by_includes_post_by_and_created_at.js @@ -0,0 +1,12 @@ +function(doc) { +  var arr = {} +  if (doc['type'] == 'Ticket' && doc.comments) { +    doc.comments.forEach(function(comment){ +      if (comment.posted_by && !arr[comment.posted_by]) { +        //don't add duplicates +        arr[comment.posted_by] = true; +        emit([comment.posted_by, doc.created_at], 1); +      } +    }); +  } +} diff --git a/engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_created_at.js b/engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_created_at.js new file mode 100644 index 0000000..33dfe0b --- /dev/null +++ b/engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_created_at.js @@ -0,0 +1,12 @@ +function(doc) { +  var arr = {} +  if (doc['type'] == 'Ticket' && doc.comments) { +    doc.comments.forEach(function(comment){ +      if (comment.posted_by && !arr[comment.posted_by]) { +        //don't add duplicates +        arr[comment.posted_by] = true; +        emit([comment.posted_by, doc.is_open, doc.created_at], 1); +      } +    }); +  } +} diff --git a/engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_updated_at.js b/engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_updated_at.js new file mode 100644 index 0000000..3bd2a74 --- /dev/null +++ b/engines/support/app/designs/ticket/by_includes_post_by_and_is_open_and_updated_at.js @@ -0,0 +1,12 @@ +function(doc) { +  var arr = {} +  if (doc['type'] == 'Ticket' && doc.comments) { +    doc.comments.forEach(function(comment){ +      if (comment.posted_by && !arr[comment.posted_by]) { +        //don't add duplicates +        arr[comment.posted_by] = true; +        emit([comment.posted_by, doc.is_open, doc.updated_at], 1); +      } +    }); +  } +} diff --git a/engines/support/app/designs/ticket/by_includes_post_by_and_updated_at.js b/engines/support/app/designs/ticket/by_includes_post_by_and_updated_at.js new file mode 100644 index 0000000..2b4304f --- /dev/null +++ b/engines/support/app/designs/ticket/by_includes_post_by_and_updated_at.js @@ -0,0 +1,12 @@ +function(doc) { +  var arr = {} +  if (doc['type'] == 'Ticket' && doc.comments) { +    doc.comments.forEach(function(comment){ +      if (comment.posted_by && !arr[comment.posted_by]) { +        //don't add duplicates +        arr[comment.posted_by] = true; +        emit([comment.posted_by, doc.updated_at], 1); +      } +    }); +  } +} diff --git a/engines/support/app/helpers/auto_tickets_path_helper.rb b/engines/support/app/helpers/auto_tickets_path_helper.rb new file mode 100644 index 0000000..93f3cb9 --- /dev/null +++ b/engines/support/app/helpers/auto_tickets_path_helper.rb @@ -0,0 +1,53 @@ +# +# These "auto" forms of the normal ticket path route helpers allow us to do two things automatically: +# +# (1) include the user in the path if appropriate. +# (2) retain the sort params, if appropriate. +# +# Tickets views with a user_id are limited to that user. For admins, they don't need a user_id for any ticket action. +# +# This is available both to the views and the tickets_controller. +# +module AutoTicketsPathHelper + +  protected + +  def auto_tickets_path(options={}) +    return unless options.class == Hash +    options = ticket_view_options.merge options +    if @user +      user_tickets_path(@user, options) +    else +      tickets_path(options) +    end +  end + +  def auto_ticket_path(ticket, options={}) +    options = ticket_view_options.merge options +    if @user +      user_ticket_path(@user, ticket, options) +    else +      ticket_path(ticket, options) +    end +  end + +  def auto_new_ticket_path(options={}) +    return unless options.class == Hash +    options = ticket_view_options.merge options +    if @user +      new_user_ticket_path(@user, options) +    else +      new_ticket_path(options) +    end +  end + +  private + +  def ticket_view_options +    hsh = {} +    hsh[:open_status] = params[:open_status] if params[:open_status] && !params[:open_status].empty? +    hsh[:sort_order]  = params[:sort_order]  if params[:sort_order]  && !params[:sort_order].empty? +    hsh +  end + +end
\ No newline at end of file diff --git a/engines/support/app/helpers/tickets_helper.rb b/engines/support/app/helpers/tickets_helper.rb new file mode 100644 index 0000000..7af50d6 --- /dev/null +++ b/engines/support/app/helpers/tickets_helper.rb @@ -0,0 +1,76 @@ +module TicketsHelper +  # +  # FORM HELPERS +  # + +  # +  # hidden fields that should be added to ever ticket form. +  # these are use for proper redirection after successful actions. +  # +  def hidden_ticket_fields +    haml_concat hidden_field_tag('open_status', params[:open_status]) +    haml_concat hidden_field_tag('sort_order', params[:sort_order]) +    haml_concat hidden_field_tag('user_id', params[:user_id]) +    "" +  end + +  # +  # PARAM HELPERS +  # + +  def search_status +    if action?(:index) +      params[:open_status] || 'open' +    else +      nil +    end +  end + +  def search_order +    params[:sort_order] || 'updated_at_desc' +  end + +  # +  # LINK HELPERS +  # + +  def link_to_status(new_status) +    if new_status == "open" +      label = t(:open_tickets) +    elsif new_status == "closed" +      label = t(:closed_tickets) +    elsif new_status == "all" +      label = t(:all_tickets) +    end +    link_to label, auto_tickets_path(:open_status => new_status, :sort_order => search_order) +  end + +  def link_to_order(order_field) +    if search_order.start_with?(order_field) +      # link for currently-filtered field. Link to other direction of this field. +      if search_order.end_with? 'asc' +        direction = 'desc' +        icon_direction = 'up' +      else +        direction = 'asc' +        icon_direction = 'down' +      end +      arrow = content_tag(:i, '', class: 'icon-arrow-'+ icon_direction) +    else +      # for not-currently-filtered field, don't display an arrow, and link to descending direction +      arrow = '' +      direction = 'desc' +    end + +    if order_field == 'updated' +      label = t(:updated) +    elsif order_field == 'created' +      label = t(:created) +    end + +    link_to auto_tickets_path(:sort_order => order_field + '_at_' + direction, :open_status => search_status) do +      arrow + label +    end +  end + +end diff --git a/engines/support/app/models/account_extension/tickets.rb b/engines/support/app/models/account_extension/tickets.rb new file mode 100644 index 0000000..f898b56 --- /dev/null +++ b/engines/support/app/models/account_extension/tickets.rb @@ -0,0 +1,13 @@ +module AccountExtension::Tickets +  extend ActiveSupport::Concern + +  def destroy_with_tickets +    Ticket.destroy_all_from(self.user) +    destroy_without_tickets +  end + +  included do +    alias_method_chain :destroy, :tickets +  end + +end diff --git a/engines/support/app/models/ticket.rb b/engines/support/app/models/ticket.rb new file mode 100644 index 0000000..cd22758 --- /dev/null +++ b/engines/support/app/models/ticket.rb @@ -0,0 +1,105 @@ +# +# TODO: thought i should reverse keys for descending, but that didn't work. +# look into whether that should be tweaked, and whether it works okay with +# pagination (seems to now...) +# +# TODO: better validation of email +# +# TODO: don't hardcode strings 'unknown user' and 'unauthenticated user' +# +class Ticket < CouchRest::Model::Base +  use_database "tickets" + +  property :created_by,     String, :protected => true  # nil for anonymous tickets, should never be changed +  property :regarding_user, String                      # may be nil or valid username +  property :subject,        String +  property :email,          String +  property :is_open,        TrueClass, :default => true +  property :comments,       [TicketComment] + +  timestamps! + +  before_validation :set_email, :set_regarding_user, :on => :create + +  design do +    view :by_updated_at +    view :by_created_at +    view :by_created_by + +    view :by_is_open_and_created_at +    view :by_is_open_and_updated_at + +    own_path = Pathname.new(File.dirname(__FILE__)) +    load_views(own_path.join('..', 'designs', 'ticket')) +  end + +  validates :subject, :presence => true +  validates :email, :allow_blank => true, :format => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ + +  def self.search(options = {}) +    @selection = TicketSelection.new(options) +    @selection.tickets +  end + +  def self.destroy_all_from(user) +    self.by_created_by.key(user.id).each do |ticket| +      ticket.destroy +    end +  end + +  def is_creator_validated? +    !!created_by +  end + +  def set_email +    self.email = nil if self.email == "" +  end + +  def set_regarding_user +    self.regarding_user = nil if self.regarding_user == "" +  end + +  def close +    self.is_open = false +  end + +  def reopen +    self.is_open = true +  end + +  def commenters +    commenters = [] +    self.comments.each do |comment| +      if comment.posted_by +        if user = User.find(comment.posted_by) +          commenters << user.login if user and !commenters.include?(user.login) +        else +          commenters << 'unknown user' if !commenters.include?('unknown user') +        end +      else +        commenters << 'unauthenticated user' if !commenters.include?('unauthenticated user') +      end +    end +    commenters.join(', ') +  end + +  # +  # update comments. User should be set by controller. +  # +  def comments_attributes=(attributes) +    if attributes +      comment = TicketComment.new(attributes.values.first) +      comment.posted_at = Time.now +      comments << comment +    end +  end + +  def created_by_user +    User.find(self.created_by) +  end + +  def regarding_user_actual_user +    User.find_by_login(self.regarding_user) +  end + +end diff --git a/engines/support/app/models/ticket_comment.rb b/engines/support/app/models/ticket_comment.rb new file mode 100644 index 0000000..bed5237 --- /dev/null +++ b/engines/support/app/models/ticket_comment.rb @@ -0,0 +1,43 @@ +class TicketComment +  include CouchRest::Model::Embeddable + +  #belongs_to :ticket #is this best way to do it? will want to access all of a tickets comments, so maybe this isn't the way? +  property :posted_by, String#, :protected => true #Integer#this should be current_user if that is set, meaning the user is logged in  #cannot have it be protected and set via comments_attributes=. also, if it is protected and we set in the tickets_controller, it gets unset. TODO---is this okay to have it not protected and manually check it? We do not users to be able to set this. +  # if the current user is not set, then we could just say the comment comes from an 'unauthenticated user', which would be somebody with the secret URL +  property :posted_at, Time#, :protected => true +  #property :posted_verified, TrueClass, :protected => true #should be true if current_user is set when the comment is created +  property :body, String +  property :private, TrueClass # private comments are only viewable by admins  #this is checked when set, to make sure it was set by an admin + +  # ? timestamps! +  validates :body, :presence => true +  #before_validation :set_time#, :set_posted_by + +  #design do +  #  view :by_posted_at +  #  view :by_body +  #end + +  def is_comment_validated? +    !!posted_by +  end + +  def posted_by_user +    User.find(posted_by) if posted_by +  end + +=begin +  #TODO. +  #this is resetting all comments associated with the ticket: +  def set_time +    self.posted_at = Time.now +  end +=end + +=begin +  def set_posted_by +    self.posted_by = User.current if User.current +  end +=end + +end diff --git a/engines/support/app/models/ticket_selection.rb b/engines/support/app/models/ticket_selection.rb new file mode 100644 index 0000000..74d5b78 --- /dev/null +++ b/engines/support/app/models/ticket_selection.rb @@ -0,0 +1,71 @@ +class TicketSelection + +  # +  # supported options: +  # +  # user_id:      id of the user (uuid string) +  # open_status:  open | closed | all +  # sort_order:   updated_at_desc | updated_at_asc | created_at_desc | created_at_asc +  # admin_status: mine | all +  # is_admin:     true | false +  # +  def initialize(options = {}) +    @user_id      = options[:user_id].gsub /[^a-z0-9]/, '' +    @open_status  = allow options[:open_status],  'open', 'closed', 'all' +    @sort_order   = allow options[:sort_order],   'updated_at_desc', 'updated_at_asc', 'created_at_desc', 'created_at_asc' +    @admin_status = allow options[:admin_status], 'mine', 'all' +    @is_admin     = allow options[:is_admin],     false, true +  end + +  def tickets +    Ticket.send(finder_method).startkey(startkey).endkey(endkey).send(order) +  end + +  protected + +  def allow(source, *allowed) +    if allowed.include?(source) +      source +    else +      allowed.first +    end +  end + +  def finder_method +    method = 'by_' +    method += 'includes_post_by_and_' if only_mine? +    method += 'is_open_and_' if @open_status != 'all' +    method += @sort_order.sub(/_(de|a)sc$/, '') +  end + +  def startkey +    startkeys = [] +    startkeys << @user_id if only_mine? +    startkeys << (@open_status == 'open') if @open_status != 'all' +    startkeys << 0 +    startkeys = startkeys.join if startkeys.length == 1 # want string not array if just one thing in array +    startkeys +  end + +  def endkey +    endtime = Time.now + 2.days # TODO. this obviously isn't ideal +    if self.startkey.is_a?(Array) +      endkeys = self.startkey +      endkeys.pop +      endkeys << endtime +    else +      endtime +    end +  end + +  def order +    # we have defined the ascending method to return the view itself: +    (@sort_order.end_with? 'desc') ? 'descending' : 'ascending' +  end + + +  def only_mine? +    !@is_admin || @admin_status == 'mine' +  end + +end diff --git a/engines/support/app/views/tickets/_comment.html.haml b/engines/support/app/views/tickets/_comment.html.haml new file mode 100644 index 0000000..778ca13 --- /dev/null +++ b/engines/support/app/views/tickets/_comment.html.haml @@ -0,0 +1,20 @@ +- if admin? or !comment.private # only show comment if user is admin or comment is not private +  %tr +    %td.user +      %div +        %strong +          - if comment.posted_by_user +            = comment.posted_by_user.login +          - else +            = t(:anonymous) +      %div= comment.posted_at.to_s(:short) +      - if comment.posted_by_user && comment.posted_by_user.is_admin? +        %div +          %span.label.label-inverse +            = t(:admin) +      - if comment.private +        %div +          %span.label.label-important +            = t(:private) +    %td.comment +      = simple_format(comment.body)
\ No newline at end of file diff --git a/engines/support/app/views/tickets/_edit_form.html.haml b/engines/support/app/views/tickets/_edit_form.html.haml new file mode 100644 index 0000000..714f8ff --- /dev/null +++ b/engines/support/app/views/tickets/_edit_form.html.haml @@ -0,0 +1,48 @@ +:ruby +  # created by user link +  if @ticket.created_by_user +    created_by = link_to @ticket.created_by_user.login, @ticket.created_by_user +  else +    created_by = t(:anonymous) +  end + +  # regarding user link +  if admin? +    if @ticket.regarding_user_actual_user +      regarding_user_link = link_to @ticket.regarding_user_actual_user.login, @ticket.regarding_user_actual_user +    else +      regarding_user_link =  "(#{t(:unknown)})" +    end +  else +    regarding_user_link = '' +  end + += form_for @ticket do |f| +  = hidden_ticket_fields +  %p.first +    - if @ticket.is_open? +      %span.label.label-info= t(:open) +    - else +      %span.label.label-success= t(:closed) +    %span.label.label-clear= t(:created_by_on, :user => created_by, :time => @ticket.created_at.to_s(:short)).html_safe +  %div= t(:subject) +  = f.text_field :subject, :class => 'large full-width' +  .row-fluid +    .span4 +      %div= t(:status) +      = f.select :is_open, [[t(:open), "true"], [t(:closed), "false"]] +    .span4 +      %div= t(:email) +      = f.text_field :email +    .span4 +      %div +        = t(:regarding_account) +        = regarding_user_link +      = f.text_field :regarding_user +  = f.button t(:save), :name => 'commit', :class => 'btn', :type => 'submit', :value => 'save' +  - if @ticket.is_open? +    = f.button t(:close), :name => 'commit', :class => 'btn', :type => 'submit', :value => 'close' +  - else +    = f.button t(:open), :name => 'commit', :class => 'btn', :type => 'submit', :value => 'open' +  - if admin? +    = link_to t(:destroy), auto_ticket_path(@ticket),  :confirm => t(:are_you_sure), :method => :delete, :class => 'btn' diff --git a/engines/support/app/views/tickets/_new_comment_form.html.haml b/engines/support/app/views/tickets/_new_comment_form.html.haml new file mode 100644 index 0000000..8418e01 --- /dev/null +++ b/engines/support/app/views/tickets/_new_comment_form.html.haml @@ -0,0 +1,13 @@ +-# +-# for posting a new comment to an existing ticket. +-# += simple_form_for @ticket, :html => {:class => 'slim'}  do |f| +  = hidden_ticket_fields +  = f.simple_fields_for :comments, @comment, :wrapper => :none, :html => {:class => 'slim'} do |c| +    = c.input :body, :label => false, :as => :text, :input_html => {:class => "full-width", :rows=> 5} +    - if admin? +      = c.input :private, :as => :boolean, :label => false, :inline_label => true +  = f.button :button, t(:post_reply), :name => 'commit', :class => 'btn-primary', :type => 'submit', :value => 'post_reply' +  - if logged_in? && @ticket.is_open +    = f.button :button, t(:reply_and_close), :name => 'commit', :class => 'btn', :type => 'submit', :value => 'reply_and_close' +    = link_to t(:cancel), auto_tickets_path, :class => :btn diff --git a/engines/support/app/views/tickets/_tabs.html.haml b/engines/support/app/views/tickets/_tabs.html.haml new file mode 100644 index 0000000..b7b5d3a --- /dev/null +++ b/engines/support/app/views/tickets/_tabs.html.haml @@ -0,0 +1,23 @@ +-# +-# SORT ORDER TABS +-# +- unless action?(:new) +  %ul.nav.nav-pills.pull-right.slim +    %li{:class=> ("active" if search_order.start_with? 'created_at')} +      = link_to_order('created') +    %li{:class=> ("active" if search_order.start_with? 'updated_at')} +      = link_to_order('updated') + +-# +-# STATUS FILTER TABS +-# +%ul.nav.nav-tabs +  - if logged_in? +    %li{:class => ("active" if search_status == 'open')} +      = link_to_status 'open' +    %li{:class => ("active" if search_status == 'closed')} +      = link_to_status 'closed' +    %li{:class => ("active" if search_status == 'all')} +      = link_to_status 'all' +  %li{:class => ("active" if action?(:new))} +    = link_to icon(:plus, :black) + t(:new_ticket), auto_new_ticket_path diff --git a/engines/support/app/views/tickets/_ticket.html.haml b/engines/support/app/views/tickets/_ticket.html.haml new file mode 100644 index 0000000..5bc33c8 --- /dev/null +++ b/engines/support/app/views/tickets/_ticket.html.haml @@ -0,0 +1,6 @@ +- url = auto_ticket_path(ticket) +%tr +  %td= link_to ticket.subject, url +  %td= link_to ticket.created_at.to_s(:short), url +  %td= link_to ticket.updated_at.to_s(:short), url +  %td= ticket.commenters diff --git a/engines/support/app/views/tickets/index.html.haml b/engines/support/app/views/tickets/index.html.haml new file mode 100644 index 0000000..c02a326 --- /dev/null +++ b/engines/support/app/views/tickets/index.html.haml @@ -0,0 +1,19 @@ +- @show_navigation = !params[:user_id].nil? + += render 'tickets/tabs' + +%table.table.table-striped.table-bordered +  %thead +    %tr +      %th= t(:subject) +      %th= t(:created) +      %th= t(:updated) +      %th= t(:voices) +  %tbody +    - if @tickets.any? +      = render @tickets.all +    - else +      %tr +        %td{:colspan=>4}= t(:none) + += paginate @tickets diff --git a/engines/support/app/views/tickets/new.html.haml b/engines/support/app/views/tickets/new.html.haml new file mode 100644 index 0000000..8f217a5 --- /dev/null +++ b/engines/support/app/views/tickets/new.html.haml @@ -0,0 +1,30 @@ +- @show_navigation = !params[:user_id].nil? + += render 'tickets/tabs' + +- if admin? && @user +  - email = @user.email_address +  - regarding = @user.login +- elsif logged_in? +  - email = current_user.email_address +  - regarding = current_user.login + += simple_form_for @ticket, :validate => true, :html => {:class => 'form-horizontal'} do |f| +  = hidden_ticket_fields +  = f.input :subject +  - if logged_in? +    = f.input :email, input_html: {value: email} +    = f.input :regarding_user, input_html: {value: regarding} +  - else +    = f.input :email +    = f.input :regarding_user +  = f.simple_fields_for :comments, @comment do |c| +    = c.input :body, :label => t(:description), :as => :text, :input_html => {:class => "full-width", :rows=> 5} +    - if admin? +      = c.input :private, :as => :boolean, :label => false, :inline_label => true +  .form-actions +    = f.button :submit, :class => 'btn-primary', :value => t(:create_thing, :thing => t(:ticket)) +    - if logged_in? +      = link_to t(:cancel), auto_tickets_path, :class => :btn +    - else +      = link_to t(:cancel), home_path, :class => 'btn'
\ No newline at end of file diff --git a/engines/support/app/views/tickets/show.html.haml b/engines/support/app/views/tickets/show.html.haml new file mode 100644 index 0000000..bfdb773 --- /dev/null +++ b/engines/support/app/views/tickets/show.html.haml @@ -0,0 +1,12 @@ +- @show_navigation = !params[:user_id].nil? + +.ticket +  = render 'tickets/edit_form' +  %table.table.table-striped.table-bordered +    %tbody +      = render :partial => 'tickets/comment', :collection => @ticket.comments +      %tr +        %td.user +          = logged_in? ? current_user.login : t(:anonymous) +        %td.comment +          = render 'tickets/new_comment_form'
\ No newline at end of file diff --git a/engines/support/config/initializers/account_lifecycle.rb b/engines/support/config/initializers/account_lifecycle.rb new file mode 100644 index 0000000..d9f04c1 --- /dev/null +++ b/engines/support/config/initializers/account_lifecycle.rb @@ -0,0 +1,3 @@ +ActiveSupport.on_load(:account) do +  include AccountExtension::Tickets +end diff --git a/engines/support/config/locales/en.yml b/engines/support/config/locales/en.yml new file mode 100644 index 0000000..342adea --- /dev/null +++ b/engines/support/config/locales/en.yml @@ -0,0 +1,22 @@ +en: +  access_ticket_text: > +    You can later access this ticket at the URL %{full_url}. You might want to bookmark this page to find it again. +    Anybody with this URL will be able to access this ticket, so if you are on a shared computer you might want to +    remove it from the browser history. +  support_tickets: "Support Tickets" +  all_tickets: "All Tickets" +  my_tickets: "My Tickets" +  open_tickets: "Open Tickets" +  closed_tickets: "Closed Tickets" +  new_ticket: "New Ticket" +  tickets: "Tickets" +  subject: "Subject" +  destroy: "Destroy" +  open: "Open" +  closed: "Closed" +  close: "Close" +  post_reply: "Post Reply" +  reply_and_close: "Reply and Close" +  description: "Description" +  ticket: "Ticket" +  regarding_account: "Regarding Account"
\ No newline at end of file diff --git a/engines/support/config/routes.rb b/engines/support/config/routes.rb new file mode 100644 index 0000000..23e0c11 --- /dev/null +++ b/engines/support/config/routes.rb @@ -0,0 +1,8 @@ +Rails.application.routes.draw do +  scope "(:locale)", :locale => MATCH_LOCALE do +    resources :tickets, :except => :edit +    resources :users do +      resources :tickets, :except => :edit +    end +  end +end diff --git a/engines/support/leap_web_help.gemspec b/engines/support/leap_web_help.gemspec new file mode 100644 index 0000000..7b668d5 --- /dev/null +++ b/engines/support/leap_web_help.gemspec @@ -0,0 +1,18 @@ +$:.push File.expand_path("../../lib", __FILE__) + +require File.expand_path('../../../lib/leap_web/version.rb', __FILE__) + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| +  s.name        = "leap_web_help" +  s.version     = LeapWeb::VERSION +  s.authors     = ["Jessib"] +  s.email       = ["jessib@leap.se"] +  s.homepage    = "http://www.leap.se" +  s.summary     = "Help Desk for LeapWeb" +  s.description = "Managing Tickets for a Leap provider" + +  s.files = Dir["{app,config,lib}/**/*"] + ["Rakefile", "README.md"] +  s.test_files = Dir["test/**/*"] + +end diff --git a/engines/support/lib/leap_web_help.rb b/engines/support/lib/leap_web_help.rb new file mode 100644 index 0000000..f5b04aa --- /dev/null +++ b/engines/support/lib/leap_web_help.rb @@ -0,0 +1,4 @@ +require "leap_web_help/engine" + +module LeapWebHelp +end diff --git a/engines/support/lib/leap_web_help/engine.rb b/engines/support/lib/leap_web_help/engine.rb new file mode 100644 index 0000000..dfa763f --- /dev/null +++ b/engines/support/lib/leap_web_help/engine.rb @@ -0,0 +1,4 @@ +module LeapWebHelp +  class Engine < ::Rails::Engine +  end +end diff --git a/engines/support/lib/tasks/leap_web_help_tasks.rake b/engines/support/lib/tasks/leap_web_help_tasks.rake new file mode 100644 index 0000000..1f38982 --- /dev/null +++ b/engines/support/lib/tasks/leap_web_help_tasks.rake @@ -0,0 +1,4 @@ +# desc "Explaining what the task does" +# task :leap_web_help do +#   # Task goes here +# end diff --git a/engines/support/script/rails b/engines/support/script/rails new file mode 100755 index 0000000..5676ab9 --- /dev/null +++ b/engines/support/script/rails @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. + +ENGINE_ROOT = File.expand_path('../..', __FILE__) +ENGINE_PATH = File.expand_path('../../lib/leap_web_help/engine', __FILE__) + +require 'rails/all' +require 'rails/engine/commands' diff --git a/engines/support/test/factories.rb b/engines/support/test/factories.rb new file mode 100644 index 0000000..be04f15 --- /dev/null +++ b/engines/support/test/factories.rb @@ -0,0 +1,18 @@ +FactoryGirl.define do + +  factory :ticket do +    subject { Faker::Lorem.sentence } +    email { Faker::Internet.email } + +    factory :ticket_with_comment do +      comments_attributes do +        { "0" => { "body" => Faker::Lorem.sentences.join(" ") } } +      end +    end + +    factory :ticket_with_creator do +      created_by { FactoryGirl.create(:user).id } +    end +  end + +end diff --git a/engines/support/test/functional/tickets_controller_test.rb b/engines/support/test/functional/tickets_controller_test.rb new file mode 100644 index 0000000..416fb73 --- /dev/null +++ b/engines/support/test/functional/tickets_controller_test.rb @@ -0,0 +1,273 @@ +require 'test_helper' + +class TicketsControllerTest < ActionController::TestCase + +  teardown do +    # destroy all tickets that were created during the test +    Ticket.all.each{|t| t.destroy} +  end + +  test "should get index if logged in" do +    login +    get :index +    assert_response :success +    assert_not_nil assigns(:tickets) +  end + +  test "no index if not logged in" do +    get :index +    assert_response :redirect +    assert_nil assigns(:tickets) +  end + +  test "should get new" do +    get :new +    assert_equal Ticket, assigns(:ticket).class +    assert_response :success +  end + +  test "unauthenticated tickets are visible" do +    ticket = find_record :ticket, :created_by => nil +    get :show, :id => ticket.id +    assert_response :success +  end + +  test "user tickets are not visible without login" do +    user = find_record :user +    ticket = find_record :ticket, :created_by => user.id +    get :show, :id => ticket.id +    assert_response :redirect +    assert_redirected_to login_url +  end + +  test "user tickets are visible to creator" do +    user = find_record :user +    ticket = find_record :ticket, :created_by => user.id +    login user +    get :show, :id => ticket.id +    assert_response :success +  end + +  test "other users tickets are not visible" do +    other_user = find_record :user +    ticket = find_record :ticket, :created_by => other_user.id +    login +    get :show, :id => ticket.id +    assert_response :redirect +    assert_redirected_to home_url +  end + +  test "should create unauthenticated ticket" do +    params = {:subject => "unauth ticket test subject", :comments_attributes => {"0" => {"body" =>"body of test ticket"}}} + +    assert_difference('Ticket.count') do +      post :create, :ticket => params +    end + +    assert_response :redirect +    assert_nil assigns(:ticket).created_by + +    assert_equal 1, assigns(:ticket).comments.count +    assert_nil assigns(:ticket).comments.first.posted_by + +  end + +  test "should create authenticated ticket" do + +    params = {:subject => "auth ticket test subject", :comments_attributes => {"0" => {"body" =>"body of test ticket"}}} + +    login + +    assert_difference('Ticket.count') do +      post :create, :ticket => params +    end + +    assert_response :redirect + +    assert_not_nil assigns(:ticket).created_by +    assert_equal assigns(:ticket).created_by, @current_user.id +    assert_equal assigns(:ticket).email, @current_user.email_address + +    assert_equal 1, assigns(:ticket).comments.count +    assert_not_nil assigns(:ticket).comments.first.posted_by +    assert_equal assigns(:ticket).comments.first.posted_by, @current_user.id +  end + +  test "add comment to unauthenticated ticket" do +    ticket = FactoryGirl.create :ticket, :created_by => nil + +    assert_difference('Ticket.find(ticket.id).comments.count') do +      put :update, :id => ticket.id, +        :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}} } +    end + +    assert_equal ticket, assigns(:ticket) # still same ticket, with different comments +    assert_not_equal ticket.comments, assigns(:ticket).comments # ticket == assigns(:ticket), but they have different comments (which we want) + +  end + + +  test "add comment to own authenticated ticket" do + +    login +    ticket = FactoryGirl.create :ticket, :created_by => @current_user.id + +    #they should be able to comment if it is their ticket: +    assert_difference('Ticket.find(ticket.id).comments.count') do +      put :update, :id => ticket.id, +        :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}} } +    end +    assert_not_equal ticket.comments, assigns(:ticket).comments +    assert_not_nil assigns(:ticket).comments.last.posted_by +    assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id + +  end + + +  test "cannot comment if it is not your ticket" do + +    other_user = find_record :user +    login :is_admin? => false, :email => nil +    ticket = FactoryGirl.create :ticket, :created_by => other_user.id +    # they should *not* be able to comment if it is not their ticket +    put :update, :id => ticket.id, :ticket => {:comments_attributes => {"0" => {"body" =>"not allowed comment"}} } +    assert_response :redirect +    assert_access_denied + +    assert_equal ticket.comments.map(&:body), assigns(:ticket).comments.map(&:body) + +  end + + +  test "admin add comment to authenticated ticket" do + +    other_user = find_record :user +    login :is_admin? => true + +    ticket = FactoryGirl.create :ticket, :created_by => other_user.id + +    #admin should be able to comment: +    assert_difference('Ticket.find(ticket.id).comments.count') do +      put :update, :id => ticket.id, +        :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}} } +    end +    assert_not_equal ticket.comments, assigns(:ticket).comments +    assert_not_nil assigns(:ticket).comments.last.posted_by +    assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id +  end + +  test "tickets by admin" do +      other_user = find_record :user +      ticket = FactoryGirl.create :ticket, :created_by => other_user.id + +      login :is_admin? => true + +      get :index, {:admin_status => "all", :open_status => "open"} +      assert assigns(:all_tickets).count > 0 + +      # if we close one ticket, the admin should have 1 less open ticket +      assert_difference('assigns[:all_tickets].count', -1) do +        assigns(:tickets).first.close +        assigns(:tickets).first.save +        get :index, {:admin_status => "all", :open_status => "open"} +      end +  end + + +  test "admin_status mine vs all" do +    testticket = FactoryGirl.create :ticket +    user = find_record :user +    login :is_admin? => true, :email => nil + +    get :index, {:open_status => "open"} +    assert assigns(:all_tickets).include?(testticket) +    get :index, {:user_id => user.id, :open_status => "open"} +    assert !assigns(:all_tickets).include?(testticket) +  end + +  test "commenting on a ticket adds to tickets that are mine" do +    testticket = FactoryGirl.create :ticket +    user = find_record :admin_user +    login user +    get :index, {:user_id => user.id, :open_status => "open"} +    assert_difference('assigns[:all_tickets].count') do +      put :update, :id => testticket.id, :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}}} +      get :index, {:user_id => user.id, :open_status => "open"} +    end + +    assert assigns(:all_tickets).include?(assigns(:ticket)) +    assert_not_nil assigns(:ticket).comments.last.posted_by +    assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id +  end + +  test "admin ticket ordering" do +    tickets = FactoryGirl.create_list :ticket, 2 + +    login :is_admin? => true, :email => nil +    get :index, {:admin_status => "all", :open_status => "open", :sort_order => 'created_at_desc'} + +    # this will consider all tickets, not just those on first page +    first_tick = assigns(:all_tickets).all.first +    last_tick = assigns(:all_tickets).all.last +    assert first_tick.created_at > last_tick.created_at + +    # and now reverse order: +    get :index, {:admin_status => "all", :open_status => "open", :sort_order => 'created_at_asc'} + +    assert_equal first_tick, assigns(:all_tickets).last +    assert_equal last_tick, assigns(:all_tickets).first + +    assert_not_equal first_tick, assigns(:all_tickets).first +    assert_not_equal last_tick, assigns(:all_tickets).last + +  end + +  test "tickets for regular user" do +    login +    ticket = FactoryGirl.create :ticket +    other_ticket = FactoryGirl.create :ticket + +    put :update, :id => ticket.id, +      :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}} } +    assert_not_nil assigns(:ticket).comments.last.posted_by +    assert_equal assigns(:ticket).comments.last.posted_by, @current_user.id + +    get :index, {:open_status => "open"} +    assert assigns(:all_tickets).count > 0 +    assert assigns(:all_tickets).include?(ticket) +    assert !assigns(:all_tickets).include?(other_ticket) + +    # user should have one more ticket if a new tick gets a comment by this user +    assert_difference('assigns[:all_tickets].count') do +      put :update, :id => other_ticket.id, :ticket => {:comments_attributes => {"0" => {"body" =>"NEWER comment"}}} +      get :index, {:open_status => "open"} +    end +    assert assigns(:all_tickets).include?(other_ticket) + +   # if we close one ticket, the user should have 1 less open ticket +    assert_difference('assigns[:all_tickets].count', -1) do +      other_ticket.reload +      other_ticket.close +      other_ticket.save +      get :index, {:open_status => "open"} +    end + +    number_open_tickets = assigns(:all_tickets).count + +    # look at closed tickets: +    get :index, {:open_status => "closed"} +    assert !assigns(:all_tickets).include?(ticket) +    assert assigns(:all_tickets).include?(other_ticket) +    number_closed_tickets = assigns(:all_tickets).count + +    # all tickets should equal closed + open +    get :index, {:open_status => "all"} +    assert assigns(:all_tickets).include?(ticket) +    assert assigns(:all_tickets).include?(other_ticket) +    assert_equal assigns(:all_tickets).count, number_closed_tickets + number_open_tickets + + +  end + +end + diff --git a/engines/support/test/integration/navigation_test.rb b/engines/support/test/integration/navigation_test.rb new file mode 100644 index 0000000..eec8c0e --- /dev/null +++ b/engines/support/test/integration/navigation_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +class NavigationTest < ActionDispatch::IntegrationTest + +  # test "the truth" do +  #   assert true +  # end +end + diff --git a/engines/support/test/leap_web_help_test.rb b/engines/support/test/leap_web_help_test.rb new file mode 100644 index 0000000..d74c087 --- /dev/null +++ b/engines/support/test/leap_web_help_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class LeapWebHelpTest < ActiveSupport::TestCase +  test "truth" do +    assert_kind_of Module, LeapWebHelp +  end +end diff --git a/engines/support/test/test_helper.rb b/engines/support/test/test_helper.rb new file mode 100644 index 0000000..fff9173 --- /dev/null +++ b/engines/support/test/test_helper.rb @@ -0,0 +1,15 @@ +# Configure Rails Environment +ENV["RAILS_ENV"] = "test" + +require File.expand_path('../../../../test/dummy/config/environment', __FILE__) +require "rails/test_help" + +Rails.backtrace_cleaner.remove_silencers! + +# Load support files +Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } + +# Load fixtures from the engine +if ActiveSupport::TestCase.method_defined?(:fixture_path=) +  ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__) +end diff --git a/engines/support/test/unit/account_extension_test.rb b/engines/support/test/unit/account_extension_test.rb new file mode 100644 index 0000000..aba162c --- /dev/null +++ b/engines/support/test/unit/account_extension_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +class AccountExtensionTest < ActiveSupport::TestCase + +  test "destroying an account triggers ticket destruction" do +    t = FactoryGirl.create :ticket_with_creator +    u = t.created_by_user +    Account.new(u).destroy +    assert_equal nil, Ticket.find(t.id) +  end + +end diff --git a/engines/support/test/unit/ticket_comment_test.rb b/engines/support/test/unit/ticket_comment_test.rb new file mode 100644 index 0000000..fe8cc95 --- /dev/null +++ b/engines/support/test/unit/ticket_comment_test.rb @@ -0,0 +1,59 @@ +require 'test_helper' + +class TicketCommentTest < ActiveSupport::TestCase +  # test "the truth" do +  #   assert true +  # end + +=begin +  setup do +    @sample_ticket = Ticket.create :title => 'test ticket' +    @sample_ticket.save +  end +=end + +  test "create" do + +    comment2 = TicketComment.new :body => "help my email is broken!" +    assert comment2.valid? +    #assert_not_nil comment2.posted_at #? +    #assert_nil comment2.posted_by #if not logged in #TODO + +    #comment.ticket = testticket #Ticket.find_by_title("testing") +    #assert_equal testticket.title, comment.ticket.title + +    #tc.ticket = Ticket.find_by_title("test title") +    #tc.ticket.title +  end + +=begin +  test "create authenticated comment" do +    User.current = 4 +    comment2 = TicketComment.new :body => "help my email is broken!" +    comment2.valid? #save # should not save comment +    assert_not_nil comment2.posted_by +  end +=end + +  test "add comments" do +    testticket = Ticket.create :subject => "testing" +    assert_equal testticket.comments.count, 0 +    comment = TicketComment.new :body => "my email broke" +    #assert comment.valid? #validating or saving necessary for setting posted_at +    #assert_not_nil comment.posted_at + +    testticket.comments << comment +    assert_equal testticket.comments.count, 1 +    sleep(1) # so first comment has earlier posted_at time +    comment2 = TicketComment.new :body => "my email broke" +    testticket.comments << comment2 #this should validate comment2 +    testticket.valid? +    assert_equal testticket.comments.count, 2 +    testticket.reload.destroy +    # where should posted_at be set? +    #assert_not_nil comment.posted_at +    #assert_not_nil testticket.comments.last.posted_at +    #assert testticket.comments.first.posted_at < testticket.comments.last.posted_at +  end + +end diff --git a/engines/support/test/unit/ticket_test.rb b/engines/support/test/unit/ticket_test.rb new file mode 100644 index 0000000..f5e6ea7 --- /dev/null +++ b/engines/support/test/unit/ticket_test.rb @@ -0,0 +1,88 @@ +require 'test_helper' + +class TicketTest < ActiveSupport::TestCase + +  test "ticket with default attribs is valid" do +    t = FactoryGirl.build :ticket +    assert t.valid? +  end + +  test "ticket without email is valid" do +    t = FactoryGirl.build :ticket, email: "" +    assert t.valid? +  end + +  test "ticket validates email format" do +    t = FactoryGirl.build :ticket, email: "aswerssfd" +    assert !t.valid? +  end + +  test "ticket open states" do +    t = FactoryGirl.build :ticket +    assert t.is_open +    t.close +    assert !t.is_open +    t.reopen +    assert t.is_open +  end + +  test "creation validated" do +    @sample = Ticket.new +    assert !@sample.is_creator_validated? +    #p current_user +    @sample.created_by = 22 #current_user +    assert @sample.is_creator_validated? +  end + +  test "destroy all tickets from a user" do +    t = FactoryGirl.create :ticket_with_creator +    u = t.created_by_user +    Ticket.destroy_all_from(u) +    assert_equal nil, Ticket.find(t.id) +  end +=begin +# TODO: do once have current_user stuff in order +  test "code if & only if not creator-validated" do +    User.current_test = nil +    t1 = Ticket.create :subject => 'test title' +    assert_not_nil t1.code +    assert_nil t1.created_by + +    User.current_test = 4 +    t2 = Ticket.create :subject => 'test title' +    assert_nil t2.code +    assert_not_nil t2.created_by +  end +=end + + +  test "find tickets user commented on" do + +    # clear old tickets just in case +    # this will cause RestClient::ResourceNotFound errors if there are multiple copies of the same ticket returned +    Ticket.by_includes_post_by.key('123').each {|t| t.destroy} +    # TODO: the by_includes_post_by view is only used for tests. Maybe we should get rid of it and change the test to including ordering? + + +    testticket = Ticket.create :subject => "test retrieving commented tickets" +    comment = TicketComment.new :body => "my email broke", :posted_by => "123" +    assert_equal 0, testticket.comments.count +    assert_equal [], Ticket.by_includes_post_by.key('123').all + +    testticket.comments << comment +    testticket.save +    assert_equal 1, testticket.reload.comments.count +    assert_equal [testticket], Ticket.by_includes_post_by.key('123').all + +    comment = TicketComment.new :body => "another comment", :posted_by => "123" +    testticket.comments << comment +    testticket.save + +    # this will ensure that the ticket is only included once, even though the user has commented on the ticket twice: +    assert_equal [testticket], Ticket.by_includes_post_by.key('123').all + +    testticket.destroy +    assert_equal [], Ticket.by_includes_post_by.key('123').all; +  end + +end | 
