@@ -0,0 +1,62 @@
// Chosen CSS Tweaks
//------------------------------------------------------------------------------
.chzn-container
margin-top: 3px
margin-left: -1px

.chzn-container .chzn-results li
line-height: 15px
padding: 4px 7px 4px
border-bottom: none


// Multiselect
//------------------------------------------------------------------------------
.chzn-container-multi .chzn-choices li
clear: none

.chzn-container-multi .chzn-choices .search-field input
height: auto


// Single select
//------------------------------------------------------------------------------
.chzn-container-single .chzn-single
height: 21px
line-height: 21px

.chzn-container-single .chzn-single div b
background-position-y: -1px

.chzn-container-single .chzn-single abbr
top: 5px


// Basic styling for standard selects and other inputs
//------------------------------------------------------------------------------
input
height: 17px
font-size: 13px

input[type=submit]
height: auto

select
height: 23px
color: #444
background-color: #fff
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white))
background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%)
background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%)
background-image: -o-linear-gradient(top, #eeeeee 0%, white 50%)
background-image: -ms-linear-gradient(top, #eeeeee 0%, white 50%)
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 )
background-image: linear-gradient(top, #eeeeee 0%, white 50%)
-webkit-border-radius: 4px
-moz-border-radius: 4px
border-radius: 4px
-moz-background-clip: padding
-webkit-background-clip: padding-box
background-clip: padding-box
border: 1px solid #aaa
@@ -0,0 +1,8 @@
.field_group .list
padding-bottom: 30px

.fields .subtitle
cursor: move

.fields .title
margin-bottom: 12px
@@ -0,0 +1,3 @@
// Place all the styles related to the Lists controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
@@ -25,7 +25,7 @@ class Admin::ApplicationController < BaseController
#----------------------------------------------------------------------------
def auto_complete
@query = params[:auto_complete_query]
@auto_complete = klass.search(@query).limit(10)
@auto_complete = klass.text_search(@query).limit(10)
render "shared/auto_complete", :layout => nil
end

@@ -18,8 +18,8 @@
class Admin::FieldGroupsController < Admin::ApplicationController
before_filter :require_user

# GET /field_groups/new
# GET /field_groups/new.xml AJAX
# GET /admin/field_groups/new
# GET /admin/field_groups/new.xml AJAX
#----------------------------------------------------------------------------
def new
@field_group = FieldGroup.new(:klass_name => params[:klass_name])
@@ -33,7 +33,7 @@ def new
respond_to_not_found(:html, :xml)
end

# GET /field_groups/1/edit AJAX
# GET /admin/field_groups/1/edit AJAX
#----------------------------------------------------------------------------
def edit
@field_group = FieldGroup.find(params[:id])
@@ -47,8 +47,8 @@ def edit
respond_to_not_found(:js)
end

# POST /field_groups
# POST /field_groups.xml AJAX
# POST /admin/field_groups
# POST /admin/field_groups.xml AJAX
#----------------------------------------------------------------------------
def create
@field_group = FieldGroup.new(params[:field_group])
@@ -64,8 +64,8 @@ def create
end
end

# PUT /field_groups/1
# PUT /field_groups/1.xml AJAX
# PUT /admin/field_groups/1
# PUT /admin/field_groups/1.xml AJAX
#----------------------------------------------------------------------------
def update
@field_group = FieldGroup.find(params[:id])
@@ -84,7 +84,26 @@ def update
respond_to_not_found(:js, :xml)
end

# POST /field_groups/sort
# DELETE /admin/field_groups/1
# DELETE /admin/field_groups/1.xml AJAX
#----------------------------------------------------------------------------
def destroy
@field_group = FieldGroup.find(params[:id])

respond_to do |format|
if @field_group.destroy
# Redirect to fields index
format.js { render(:destroy) { |page| page.redirect_to admin_fields_url } }
format.xml { head :ok }
else
flash[:warning] = t(:msg_cant_delete_field_group, @field_group.name)
format.js # destroy.js.rjs
format.xml { render :xml => @field_group.errors, :status => :unprocessable_entity }
end
end
end

# POST /admin/field_groups/sort
#----------------------------------------------------------------------------
def sort
asset = params[:asset]
@@ -96,4 +115,14 @@ def sort

render :nothing => true
end

# GET /admin/field_groups/1/confirm AJAX
#----------------------------------------------------------------------------
def confirm
@field_group = FieldGroup.find(params[:id])

rescue ActiveRecord::RecordNotFound
respond_to_not_found(:js, :xml)
end

end
@@ -115,8 +115,13 @@ def destroy
@field = CustomField.find(params[:id])

respond_to do |format|
format.js # destroy.js.rjs
format.xml { head :ok }
if @field.destroy
format.js # destroy.js.rjs
format.xml { head :ok }
else
format.js # destroy.js.rjs
format.xml { render :xml => @field.errors, :status => :unprocessable_entity }
end
end

rescue ActiveRecord::RecordNotFound
@@ -22,6 +22,8 @@ class Admin::PluginsController < Admin::ApplicationController
# GET /admin/plugins.xml
#----------------------------------------------------------------------------
def index
@plugins = FatFreeCRM::Plugin.list

respond_to do |format|
format.html # index.html.haml
format.xml { render :xml => nil }
@@ -0,0 +1,131 @@
# Fat Free CRM
# Copyright (C) 2008-2011 by Michael Dvorkin
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#------------------------------------------------------------------------------

class Admin::TagsController < Admin::ApplicationController
before_filter "set_current_tab('admin/tags')", :only => [ :index, :show ]

# GET /admin/tags
# GET /admin/tags.xml HTML
#----------------------------------------------------------------------------
def index
@tags = Tag.all

respond_to do |format|
format.html # index.html.haml
format.js # index.js.rjs
format.xml { render :xml => Tag.all }
format.xls { send_data @tags.to_xls, :type => :xls }
format.csv { send_data @tags.to_csv, :type => :csv }
format.rss { render "shared/index.rss.builder" }
format.atom { render "shared/index.atom.builder" }
end
end

# GET /admin/tags/new
# GET /admin/tags/new.xml AJAX
#----------------------------------------------------------------------------
def new
@tag = Tag.new

respond_to do |format|
format.js # new.js.rjs
format.xml { render :xml => @tag }
end
end

# GET /admin/tags/1/edit AJAX
#----------------------------------------------------------------------------
def edit
@tag = Tag.find(params[:id])

if params[:previous].to_s =~ /(\d+)\z/
@previous = Tag.find($1)
end

rescue ActiveRecord::RecordNotFound
@previous ||= $1.to_i
respond_to_not_found(:js) unless @tag
end

# POST /admin/tags
# POST /admin/tags.xml AJAX
#----------------------------------------------------------------------------
def create
@tag = Tag.new(params[:tag])

respond_to do |format|
if @tag.save
@tags = Tag.all
format.js # create.js.rjs
format.xml { render :xml => @tag, :status => :created, :location => @tag }
else
format.js # create.js.rjs
format.xml { render :xml => @tag.errors, :status => :unprocessable_entity }
end
end
end

# PUT /admin/tags/1
# PUT /admin/tags/1.xml AJAX
#----------------------------------------------------------------------------
def update
@tag = Tag.find(params[:id])

respond_to do |format|
if @tag.update_attributes(params[:tag])
format.js # update.js.rjs
format.xml { head :ok }
else
format.js # update.js.rjs
format.xml { render :xml => @tag.errors, :status => :unprocessable_entity }
end
end

rescue ActiveRecord::RecordNotFound
respond_to_not_found(:js, :xml)
end


# DELETE /admin/tags/1
# DELETE /admin/tags/1.xml AJAX
#----------------------------------------------------------------------------
def destroy
@tag = Tag.find(params[:id])

respond_to do |format|
if @tag.destroy
format.js # destroy.js.rjs
format.xml { head :ok }
else
flash[:warning] = t(:msg_cant_delete_tag, @tag.name)
format.js # destroy.js.rjs
format.xml { render :xml => @tag.errors, :status => :unprocessable_entity }
end
end
end

# GET /admin/tags/1/confirm AJAX
#----------------------------------------------------------------------------
def confirm
@tag = Tag.find(params[:id])

rescue ActiveRecord::RecordNotFound
respond_to_not_found(:js, :xml)
end

end

@@ -185,9 +185,13 @@ def get_users(options = {})
self.current_page = options[:page] if options[:page]
self.current_query = params[:query] if params[:query]

@search = klass.search(params[:q])
@search.build_grouping unless @search.groupings.any?

wants = request.format
scope = User.by_id
scope = scope.search(current_query) unless current_query.blank?
scope = scope.merge(@search.result)
scope = scope.text_search(current_query) if current_query.present?
scope = scope.unscoped if wants.csv?
scope = scope.paginate(:page => current_page) if wants.html? || wants.js? || wants.xml?
scope
@@ -25,18 +25,23 @@ class BaseController < ApplicationController
respond_to :json, :xml, :except => :edit
respond_to :atom, :csv, :rss, :xls, :only => :index

helper_method :search

# Common auto_complete handler for all core controllers.
#----------------------------------------------------------------------------
def auto_complete
@query = params[:auto_complete_query]
@auto_complete = hook(:auto_complete, self, :query => @query, :user => @current_user)
if @auto_complete.empty?
@auto_complete = klass.my.search(@query).limit(10)
@auto_complete = klass.my.text_search(@query).limit(10)
else
@auto_complete = @auto_complete.last
end
session[:auto_complete] = controller_name.to_sym
render "shared/auto_complete", :layout => nil
respond_to do |format|
format.any(:js, :html) { render "shared/auto_complete", :layout => nil }
format.json { render :json => @auto_complete.inject({}){|h,a| h[a.id] = a.name; h } }
end
end

# Common attach handler for all core controllers.
@@ -91,19 +96,25 @@ def tagged
end

def field_group
if @tag = Tag.find_by_name(params[:tag].strip) and
@field_group = FieldGroup.find_by_tag_id(@tag.id)

@asset = klass.find_by_id(params[:asset_id]) || klass.new

render 'fields/group'
else
render :text => ''
if @tag = Tag.find_by_name(params[:tag].strip)
if @field_group = FieldGroup.find_by_tag_id_and_klass_name(@tag.id, klass.to_s)
@asset = klass.find_by_id(params[:asset_id]) || klass.new
render 'fields/group' and return
end
end
render :text => ''
end

private

def search
@search ||= begin
search = klass.search(params[:q])
search.build_grouping unless search.groupings.any?
search
end
end

# Get list of records for a given model class.
#----------------------------------------------------------------------------
def get_list_of_records(klass, options = {})
@@ -133,8 +144,9 @@ def get_list_of_records(klass, options = {})
filter = session[options[:filter]].to_s.split(',') if options[:filter]

scope = klass.my(records)
scope = scope.merge(search.result)
scope = scope.state(filter) if filter.present?
scope = scope.search(query) if query.present?
scope = scope.text_search(query) if query.present?
scope = scope.tagged_with(tags, :on => :tags) if tags.present?
scope = scope.unscoped if wants.csv?
scope = scope.paginate(pages) if wants.html? || wants.js? || wants.xml?
@@ -0,0 +1,27 @@
class ListsController < ApplicationController
respond_to :js

# POST /lists
#----------------------------------------------------------------------------
def create
# Find any existing list with the same name (case insensitive)
if @list = List.find(:first, :conditions => ["lower(name) = ?", params[:list][:name].downcase])
@list.update_attributes(params[:list])
else
@list = List.create(params[:list])
end
respond_with(@list)
end

# DELETE /lists/1
#----------------------------------------------------------------------------
def destroy
@list = List.find(params[:id])
@list.destroy

respond_with(@list)

rescue ActiveRecord::RecordNotFound
respond_to_not_found(:html, :js, :json, :xml)
end
end
@@ -40,5 +40,15 @@ def account_summary(account)
t('pluralize.comment', account.comments.count)
].join(', ')
end

def account_select(options = {})
# Generates a select list with the first 25 accounts,
# and prepends the currently selected account, if available
options[:selected] = (@account && @account.id) || 0
accounts = ([@account] + Account.order("name").my.limit(25)).compact.uniq
collection_select :account, :id, accounts, :id, :name, options,
{:"data-placeholder" => t(:select_an_account),
:style => "width:330px; display:none;" }
end
end

@@ -16,5 +16,25 @@
#------------------------------------------------------------------------------

module Admin::ApplicationHelper
#----------------------------------------------------------------------------
def link_to_confirm_delete(model)
link_to(t(:yes_button),
url_for([:admin, model]),
:method => :delete,
:remote => true,
:onclick => visual_effect(:highlight, dom_id(model), :startcolor => "#ffe4e1")
)
end

#----------------------------------------------------------------------------
def link_to_delete(model, params = {})
name = (params[:klass_name] || model.class.name).underscore.downcase
link_to(t(:delete) + "!",
params[:url] || url_for([:admin, model]),
:method => :delete,
:remote => true,
:onclick => visual_effect(:highlight, dom_id(model), :startcolor => "#ffe4e1")
)
end
end

@@ -21,11 +21,16 @@ def field_group_subtitle(field_group)
html = t(field_group.name, :default => field_group.label)
html << content_tag(:small, :id => "#{asset}_field_group_#{field_group.id}_intro") do
if field_group.tag_id
t(:field_group_tag_restriction, :assets => asset.pluralize, :tag => field_group.tag.name)
t(:field_group_tag_restriction, :assets => asset.pluralize, :tag => field_group.tag.try(:name))
else
t(:field_group_unrestricted, :assets => asset.pluralize)
end
end
html.html_safe
end

def link_to_confirm(field_group)
link_to(t(:delete) + "?", confirm_admin_field_group_path(field_group), :method => :get, :remote => true)
end

end
@@ -0,0 +1,22 @@
# Fat Free CRM
# Copyright (C) 2008-2011 by Michael Dvorkin
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#------------------------------------------------------------------------------

module Admin::TagsHelper
def link_to_confirm(tag)
link_to(t(:delete) + "?", confirm_admin_tag_path(tag), :method => :get, :remote => true)
end
end
@@ -32,16 +32,6 @@ def link_to_confirm(user)
link_to(t(:delete) + "?", confirm_admin_user_path(user), :method => :get, :remote => true)
end

#----------------------------------------------------------------------------
def link_to_delete(user)
link_to(t(:yes_button),
admin_user_path(user),
:method => :delete,
:remote => true,
:onclick => visual_effect(:highlight, dom_id(user), :startcolor => "#ffe4e1")
)
end

# User summary info for RSS/ATOM feeds.
#----------------------------------------------------------------------------
def user_summary(user)
@@ -114,7 +114,7 @@ def link_to_edit(model, params = {})
link_to(t(:edit),
params[:url] || send(:"edit_#{name}_path", model),
:remote => true,
:onclick => "this.href += '?previous='+ crm.find_form('edit_#{name}');"
:onclick => "this.href = this.href.split('?')[0] + '?previous='+crm.find_form('edit_#{name}');"
)
end

@@ -399,5 +399,23 @@ def links_to_export

(exports + feeds).join(' | ')
end

def template_fields(f, type)
f.grouping_fields f.object.new_grouping, :object_name => "new_object_name", :child_index => "new_grouping" do |builder|
render('grouping_fields', :f => builder)
end
end

def link_to_add_fields(name, f, type)
new_object = f.object.send "build_#{type}"
fields = f.send("#{type}_fields", new_object, :child_index => "new_#{type}") do |builder|
render(type.to_s + "_fields", :f => builder)
end
link_to name, nil, :class => "add_fields", "data-field-type" => type, "data-content" => "#{fields}"
end

def link_to_remove_fields(name, f)
link_to image_tag('delete.png', :size => '16x16', :alt => name), nil, :class => "remove_fields"
end
end

@@ -0,0 +1,2 @@
module ListsHelper
end
@@ -29,5 +29,12 @@ def sort_by_language
%Q[{ name: "#{language}", on_select: function() { #{redraw(:locale, [ locale, language ], url_for(:action => :redraw))} } }]
end
end

def user_select(asset)
collection_select asset, :assigned_to, @users, :id, :full_name,
{ :include_blank => "" },
{ :"data-placeholder" => t(:myself),
:style => "width:160px" }
end
end

@@ -59,7 +59,7 @@ class Account < ActiveRecord::Base
scope :created_by, lambda { |user| where(:user_id => user.id) }
scope :assigned_to, lambda { |user| where(:assigned_to => user.id) }

scope :search, lambda { |query|
scope :text_search, lambda { |query|
query = query.gsub(/[^\w\s\-\.'\p{L}]/u, '').strip
where('upper(name) LIKE upper(:m) OR upper(email) LIKE upper(:m)', :m => "%#{query}%")
}
@@ -56,7 +56,7 @@ class Campaign < ActiveRecord::Base
scope :created_by, lambda { |user| where('user_id = ?' , user.id) }
scope :assigned_to, lambda { |user| where('assigned_to = ?', user.id) }

scope :search, lambda { |query|
scope :text_search, lambda { |query|
query = query.gsub(/[^\w\s\-\.'\p{L}]/u, '').strip
where('upper(name) LIKE upper(?)', "%#{query}%")
}
@@ -66,7 +66,7 @@ class Contact < ActiveRecord::Base
scope :created_by, lambda { |user| { :conditions => [ "user_id = ?", user.id ] } }
scope :assigned_to, lambda { |user| { :conditions => ["assigned_to = ?", user.id ] } }

scope :search, lambda { |query|
scope :text_search, lambda { |query|
query = query.gsub(/[^@\w\s\-\.'\p{L}]/u, '').strip
# We can't always be sure that names are entered in the right order, so we must
# split the query into all possible first/last name permutations.
@@ -68,7 +68,7 @@ class Lead < ActiveRecord::Base
scope :created_by, lambda { |user| where('user_id = ?' , user.id) }
scope :assigned_to, lambda { |user| where('assigned_to = ?' , user.id) }

scope :search, lambda { |query|
scope :text_search, lambda { |query|
query = query.gsub(/[^\w\s\-\.'\p{L}]/u, '').strip
where('upper(first_name) LIKE upper(:s) OR upper(last_name) LIKE upper(:s) OR upper(company) LIKE upper(:m) OR upper(email) LIKE upper(:m)', :s => "#{query}%", :m => "%#{query}%")
}
@@ -59,7 +59,7 @@ class Opportunity < ActiveRecord::Base
scope :pipeline, where("opportunities.stage IS NULL OR (opportunities.stage != 'won' AND opportunities.stage != 'lost')")

# Search by name OR id
scope :search, lambda { |query|
scope :text_search, lambda { |query|
query = query.gsub(/[^\w\s\-\.'\p{L}]/u, '').strip
# postgresql does not like to compare string to integer field
if query =~ /^\d+$/
@@ -92,7 +92,7 @@ class Task < ActiveRecord::Base
scope :completed_this_month, where('completed_at >= ? AND completed_at < ?', Time.zone.now.beginning_of_month.utc, Time.zone.now.beginning_of_week.utc - 7.days)
scope :completed_last_month, where('completed_at >= ? AND completed_at < ?', (Time.zone.now.beginning_of_month.utc - 1.day).beginning_of_month.utc, Time.zone.now.beginning_of_month.utc)

scope :search, lambda { |query|
scope :text_search, lambda { |query|
query = query.gsub(/[^\w\s\-\.'\p{L}]/u, '').strip
where('upper(name) LIKE upper(?)', "%#{query}%")
}
@@ -33,6 +33,7 @@
class FieldGroup < ActiveRecord::Base
has_many :fields, :order => :position
belongs_to :tag
before_destroy :not_default_field_group, :move_fields_to_default_field_group

validates_presence_of :label

@@ -63,5 +64,18 @@ def self.with_tags(tag_ids)
def label_i18n
I18n.t(name, :default => label)
end

private
# Can't delete default field group
def not_default_field_group
name != "custom_fields"
end

# When deleted, transfer fields to default field group
def move_fields_to_default_field_group
default_group = FieldGroup.find_by_name_and_klass_name("custom_fields", klass_name)
default_group.fields << fields
self.reload
end
end

@@ -0,0 +1,8 @@
class List < ActiveRecord::Base
validates_presence_of :name

# Parses the controller from the url
def controller
(url || "").sub(/^\//,'').split(/\/|\?/).first
end
end
@@ -1,2 +1,14 @@
class Tag < ActsAsTaggableOn::Tag
before_destroy :no_associated_field_groups

# Don't allow a tag to be deleted if it is associated with a Field Group
def no_associated_field_groups
FieldGroup.find_all_by_tag_id(self).none?
end

# Returns a count of taggings per model klass
# e.g. {"Contact" => 3, "Account" => 1}
def model_tagging_counts
Tagging.where(:tag_id => id).count(:group => :taggable_type)
end
end
@@ -73,10 +73,10 @@ class User < ActiveRecord::Base

# For some reason this does not play nice with is_paranoid when set as default scope
scope :by_id, order('id DESC')
scope :except, lambda { |user| where('id != ?', user.id) }
scope :except, lambda { |user| where('id != ?', user.id).by_name }
scope :by_name, order('first_name, last_name, email')

scope :search, lambda { |query|
scope :text_search, lambda { |query|
query = query.gsub(/[^\w\s\-\.'\p{L}]/u, '').strip
where('upper(username) LIKE upper(:s) OR upper(first_name) LIKE upper(:s) OR upper(last_name) LIKE upper(:s)', :s => "#{query}%")
}
@@ -8,7 +8,7 @@
%tr
%td
.label #{t :assigned_to}:
= collection_select :account, :assigned_to, @users, :id, :full_name, { :include_blank => t(:myself) }, { :style => "width:160px" }
= user_select(:account)
%td= spacer
%td
.label #{t :category}:
@@ -9,6 +9,7 @@ if @account.valid?
page.call "crm.flick", :empty, :remove
else
page[:create_account].replace_html :partial => "new"
page.call "crm.init_chosen_fields"
page[:create_account].visual_effect :shake, :duration => 0.25, :distance => 6
page[:account_name].focus
end
@@ -22,9 +22,11 @@ else # <---------------------------------------- Show [Edit Account] form.
page.call "crm.highlight_off", id # Disable onMouseOver for the list item.
page.call "crm.hide_form", :create_account # Hide [Create Account] form if any.
page[id].replace_html :partial => "edit" # Show [Edit Account] form.
page.call "crm.init_chosen_fields"

elsif params[:cancel].false? # Called from title of the account landing page...
page[:edit_account].replace_html :partial => "edit"
page.call "crm.init_chosen_fields"
page.call "crm.flip_form", :edit_account
page.call "crm.set_title", :edit_account, "#{t :edit} #{@account.name}"
end
@@ -2,12 +2,17 @@

.title_tools
= link_to_inline(:create_account, new_account_path, :text => t(:create_account)) << " | "
- if Setting.advanced_search_enabled
= link_to_inline(:advanced_search, advanced_search_accounts_path, :text => t(:advanced_search)) << " | "
= link_to_inline(:options, options_accounts_path, :text=> t(:options))

.title
%span#create_account_title #{t :accounts}
= image_tag("loading.gif", :size => "16x16", :id => "loading", :style => "display: none;")
.remote#options{ hidden }
.remote#advanced_search{ hidden_if(!params[:q]) }
- if @search
= render :partial => "advanced_search"
.remote#create_account{ hidden }

.list#accounts
@@ -4,6 +4,7 @@ page.call "crm.flip_form", :create_account

unless params[:cancel].true?
page[:create_account].replace_html :partial => "new"
page.call "crm.init_chosen_fields"
page.call "crm.set_title", :create_account, t(:create_account)
else
page.call "crm.set_title", :create_account, t(:accounts)
@@ -0,0 +1,5 @@
.confirm[@field_group, :confirm]
#{t :field_group_confirm_delete}
#{t :delete} <b>#{@field_group.name}</b>?
= link_to_confirm_delete(@field_group) << " : "
= link_to_function(t(:no_button), "crm.flick('#{dom_id(@field_group, :confirm)}', 'remove')")
@@ -5,9 +5,13 @@
- create_form_id = dom_id(field_group, :create_field)

.subtitle_tools
= link_to_inline(edit_form_id, edit_admin_field_group_path(field_group), :text => t(:edit_field_group))
|
- unless field_group.name == "custom_fields"
= link_to_inline(edit_form_id, edit_admin_field_group_path(field_group), :text => t(:edit_field_group))
|
= link_to_inline(create_form_id, new_admin_field_path(:field_group_id => field_group.id), :text => t(:create_field))
- unless field_group.name == "custom_fields"
|
= link_to_confirm(field_group)

.subtitle{ :id => "#{dom_id(field_group)}_title" }= field_group_subtitle(field_group)

@@ -0,0 +1,6 @@
id = dom_id(@field_group, :confirm)
page << "if ($('#{id}')) {"
page.call "crm.flick", id, :remove
page << "} else {"
page.insert_html :top, dom_id(@field_group), :partial => "confirm"
page << "}"
@@ -0,0 +1,11 @@
id = dom_id(@field_group)

if @field_group.destroyed?
page[id].visual_effect :blind_up, :duration => 0.25
else
page.call "crm.flick", dom_id(@field_group, :confirm), :remove
page[id].visual_effect :shake, :duration => 0.25, :distance => 6
page[:flash].replace_html flash[:warning]
page.call "crm.flash", :warning
flash[:warning] = nil
end
@@ -1 +1,11 @@
page[dom_id(@field)].visual_effect "blind_up", :duration => 0.25
id = dom_id(@field)

if @field.destroyed?
page[id].visual_effect :blind_up, :duration => 0.25
else
page.call "crm.flick", dom_id(@field, :confirm), :remove
page[id].visual_effect :shake, :duration => 0.25, :distance => 6
page[:flash].replace_html flash[:warning]
page.call "crm.flash", :warning
flash[:warning] = nil
end
@@ -4,7 +4,7 @@ page.call "crm.flip_form", :options

if params[:cancel] != "true"
page[:options].replace_html :partial => "options"
page.call "crm.set_title", :create_field, "Custom field Options"
page.call "crm.set_title", :create_field, t(:custom_field_options)
else
page.call "crm.set_title", :create_field, "Custom Fields"
page.call "crm.set_title", :create_field, t(:custom_fields)
end
@@ -0,0 +1,13 @@
%li
%span.black
%strong= plugin.name
%tt
%dt{ :style => "padding: 2px 0px 0px 0px" }
= t(:author) + ":"
%strong= plugin.author
%br
= t(:version) + ":"
%strong= plugin.version
%br
= t(:description) + ":"
= plugin.description
@@ -1,3 +1,9 @@
= styles_for :plugin

.title Plugins

%p #{t :not_implemented}
.list#plugins
- if @plugins.any?
= render :partial => "admin/plugins/plugin", :collection => @plugins
- else
= render "shared/empty"
@@ -0,0 +1,5 @@
.confirm[@tag, :confirm]
#{t :tag_with_taggings_confirm_delete, :value => Tagging.where(:tag_id => @tag).count}
#{t :delete} <b>#{@tag.name}</b>?
= link_to_confirm_delete(@tag) << " : "
= link_to_function(t(:no_button), "crm.flick('#{dom_id(@tag, :confirm)}', 'remove')")
@@ -0,0 +1,9 @@
.remote
= form_for([:admin, @tag], :as => :tag, :html => one_submit_only(:tag).merge(:class => "edit_tag", :id => "edit_tag_#{@tag.id}"), :remote => true) do |f|
= link_to_close edit_admin_tag_path(@tag)
= f.error_messages
= render :partial => "top_section", :locals => { :f => f, :edit => true }
.buttonbar
= f.submit t(:save_tag), :id => :tag_submit
or
= link_to_cancel edit_admin_tag_path(@tag)
@@ -0,0 +1,10 @@
- path = new_admin_tag_path

= form_for([:admin, @tag], :html => one_submit_only(:tag), :remote => true) do |f|
= link_to_close path
= f.error_messages
= render :partial => "top_section", :locals => { :f => f }
.buttonbar
= f.submit t(:create_tag), :id => :tag_submit
or
= link_to_cancel path
@@ -0,0 +1,29 @@
- tag_field_groups = FieldGroup.find_all_by_tag_id(tag)
- model_tagging_counts = tag.model_tagging_counts

%li[tag]{ highlightable(dom_id(tag, :tools)) }
.tools{ invisible, :id => dom_id(tag, :tools) }
= link_to_edit(tag, :url => edit_admin_tag_path(tag)) << " |"
- if tag_field_groups.any?
%span{:style => "color: #777"} Delete
- else
- if model_tagging_counts.any?
= link_to_confirm(tag)
- else
= link_to_delete(tag)

%span.black= tag.name
%tt
- if model_tagging_counts.any?
= t(:tagged) + ":"
= model_tagging_counts.map {|k, c| t(pluralize(c, k.downcase)) }.join(", ")

- if tag_field_groups.any?
%dt{ :style => "padding: 2px 0px 0px 0px" }
= t(:field_group_tags) + ":"
- tag_field_groups.each_with_index do |field_group, i|
%strong= field_group.label_i18n
on
%strong= field_group.klass_name.pluralize
- if i < tag_field_groups.size - 1
= "|"
@@ -0,0 +1,6 @@
.section
%table
%tr
%td
.label.top.req= t(:name) + ":"
= f.text_field :name
@@ -0,0 +1,6 @@
id = dom_id(@tag, :confirm)
page << "if ($('#{id}')) {"
page.call "crm.flick", id, :remove
page << "} else {"
page.insert_html :top, dom_id(@tag), :partial => "confirm"
page << "}"
@@ -0,0 +1,12 @@
if @tag.valid?
page[:create_tag_arrow].replace_html "&#9658;"
page[:create_tag_title].replace_html t(:tags)
page.visual_effect :toggle_blind, :create_tag, :duration => 0.3, :afterFinish => 'function(effect) {effect.element.update("")}'
page.insert_html :top, :tags, :partial => "tag", :collection => [ @tag ]
page[dom_id(@tag)].visual_effect :highlight, :duration => 1.5
page.call "crm.flick", :empty, :remove
else
page[:create_tag].replace_html :partial => "new"
page[:create_tag].visual_effect :shake, :duration => 0.25, :distance => 6
page[:tag_name].focus
end
@@ -0,0 +1,11 @@
id = dom_id(@tag)

if @tag.destroyed?
page[id].visual_effect :blind_up, :duration => 0.25
else
page.call "crm.flick", dom_id(@tag, :confirm), :remove
page[id].visual_effect :shake, :duration => 0.25, :distance => 6
page[:flash].replace_html flash[:warning]
page.call "crm.flash", :warning
flash[:warning] = nil
end
@@ -0,0 +1,21 @@
id = dom_id(@tag)

if params[:cancel].true? # <----------------- Hide [Edit Tag]
page[id].replace :partial => "tag", :collection => [ @tag ]

else # <---------------------------------------- Show [Edit Tag] form.

if @previous # Hide open [Edit Tag] form if any.
if @previous.is_a?(Tag) # Previous tag still exists?
page[dom_id(@previous)].replace :partial => "tag", :collection => [ @previous ]
else
page.call "crm.flick", "tag_#{@previous}", :remove
end
end

page.call "crm.highlight_off", id # Disable onMouseOver for the list item.
page.call "crm.hide_form", :create_tag # Hide [Create Tag] form if any.
page[id].replace_html :partial => "edit" # Show [Edit Tag] form.
page[:tag_name].focus

end
@@ -0,0 +1,17 @@
= styles_for :tag

.title_tools
= link_to_inline(:create_tag, new_admin_tag_path, :text => t(:create_tag))

.title
%span#create_tag_title #{t :tags}
= image_tag("loading.gif", :size => "16x16", :id => "loading", :style => "display: none;")
.remote#create_tag{ hidden }

.list#tags
- if @tags.any?
= render :partial => "admin/tags/tag", :collection => @tags
- else
= render "shared/empty"

#export= render "shared/export"
@@ -0,0 +1,8 @@
page.call "crm.flip_form", :create_tag

unless params[:cancel].true?
page[:create_tag].replace_html :partial => "new"
page.call "crm.set_title", :create_tag, t(:create_tag)
else
page.call "crm.set_title", :create_tag, t(:tags)
end
@@ -0,0 +1,10 @@
id = dom_id(@tag)

if @tag.errors.empty?
page[id].replace :partial => "tag", :collection => [ @tag ]
page[id].visual_effect :highlight, :duration => 1.0
else
page[id].replace_html :partial => "edit"
page[id].visual_effect :shake, :duration => 0.25, :distance => 6
page[:tag_tagname].focus
end
@@ -1,5 +1,5 @@
.confirm[@user, :confirm]
#{t :user_confirm_delete}
#{t :delete} <b>#{@user.full_name}</b>?
= link_to(t(:yes_button), admin_user_path(@user), :method => :delete) << " : "
= link_to_confirm_delete(@user) << " : "
= link_to_function(t(:no_button), "crm.flick('#{dom_id(@user, :confirm)}', 'remove')")
@@ -1,4 +1,3 @@

= styles_for :user

.title_tools
@@ -0,0 +1,12 @@
= search_form_for search, :url => url_for(:action => :index), :html => {:method => :get, :class => "advanced_search"}, :remote => true do |f|
= link_to_close url_for(:action => :advanced_search)

= f.grouping_fields do |g|
= render 'grouping_fields', :f => g

%p
= link_to_add_fields t(:advanced_search_add_group), f, :grouping

%p
= hidden_field_tag :distinct, '1'
= f.submit t(:advanced_search_submit)
@@ -0,0 +1,14 @@
.fields.condition{ "data-object-name" => f.object_name }
%p
= link_to_remove_fields t(:advanced_search_remove_condition), f

= f.attribute_fields do |a|
%span.fields{ "data-object-name" => f.object_name }
= a.attribute_select :associations => %w(account tags activities emails business_address billing_address)

= f.predicate_select({:only => [:cont, :not_cont, :blank, :present, :null, :not_null, :matches, :does_not_match, :eq, :not_eq, :lt, :gt], :compounds => false}, :class => "predicate")

= f.value_fields do |v|
%span.fields.value{ 'data-object-name' => f.object_name }
= v.text_field :value

@@ -0,0 +1,12 @@
.fields{ 'data-object-name' => f.object_name }
%p
- key = (f.object_name =~ /[0]/) ? :advanced_search_group_first : :advanced_search_group_rest
= t(key, :combinator => f.combinator_select).html_safe

.filters
- f.object.build_condition unless f.object.conditions.any?
= f.condition_fields do |c|
= render 'condition_fields', :f => c

%p
= link_to_add_fields t(:advanced_search_add_condition), f, :condition
@@ -0,0 +1,3 @@
%span.fields{ 'data-object-name' => f.object_name }
= f.sort_select
= button_to_remove_fields "remove", f
@@ -0,0 +1,9 @@
page.call "crm.flick", :empty, :toggle
page.call "crm.flip_form", :advanced_search

if params[:cancel].true?
page.call "jQuery('.show_lists_save_form').hide"
else
page.call "jQuery('.show_lists_save_form').show"
page[:advanced_search].replace_html :partial => "advanced_search"
end
@@ -9,6 +9,7 @@ if @campaign.valid?
page.call "crm.flick", :empty, :remove
else
page[:create_campaign].replace_html :partial => "new"
page.call "crm.init_chosen_fields"
page.call "crm.date_select_popup", :campaign_starts_on
page.call "crm.date_select_popup", :campaign_ends_on
page[:create_campaign].visual_effect :shake, :duration => 0.25, :distance => 6
@@ -22,9 +22,11 @@ else # <---------------------------------------- Show [Edit Campaign] form.
page.call "crm.highlight_off", id # Disable onMouseOver for the list item.
page.call "crm.hide_form", :create_campaign # Hide [Create Campaign] form if any.
page[id].replace_html :partial => "edit" # Show [Edit Campaign] form.
page.call "crm.init_chosen_fields"

elsif params[:cancel].false? # Called from title of the campaign landing page...
page[:edit_campaign].replace_html :partial => "edit"
page.call "crm.init_chosen_fields"
page.call "crm.flip_form", :edit_campaign
page.call "crm.set_title", :edit_campaign, "#{t :edit} #{@campaign.name}"
end
@@ -2,12 +2,17 @@

.title_tools
= link_to_inline(:create_campaign, new_campaign_path, :text => t(:create_campaign)) << " | "
- if Setting.advanced_search_enabled
= link_to_inline(:advanced_search, advanced_search_campaigns_path, :text => t(:advanced_search)) << " | "
= link_to_inline(:options, options_campaigns_path, :text => t(:options))

.title
%span#create_campaign_title #{t :campaigns}
= image_tag("loading.gif", :size => "16x16", :id => "loading", :style => "display: none;")
.remote#options{ hidden }
.remote#advanced_search{ hidden_if(!params[:q]) }
- if @search
= render :partial => "advanced_search"
.remote#create_campaign{ hidden }

.list#campaigns
@@ -4,6 +4,7 @@ page.call "crm.flip_form", :create_campaign

unless params[:cancel].true?
page[:create_campaign].replace_html :partial => "new"
page.call "crm.init_chosen_fields"
page.call "crm.set_title", :create_campaign, t(:create_campaign)
page.call "crm.date_select_popup", :campaign_starts_on
page.call "crm.date_select_popup", :campaign_ends_on
@@ -34,15 +34,14 @@
%span#account_disabled_title :

-# Add [-- None --] account choice when editing existing contact that has an account.
- options = { :selected => @account.id || 0 }
- options[:include_blank] = true #t(:select_none) unless @contact.new_record? || @contact.account.blank?
- options = {} and (options[:include_blank] = "" unless @contact.new_record? || @contact.account.blank?)
= account_select(options)

= collection_select :account, :id, @accounts, :id, :name, options, { :style => "width:330px; display:none;" }
= a.text_field :name, :style => "width:324px; display:none;"
%td= spacer
%td
.label #{t :assigned_to}:
= collection_select :contact, :assigned_to, @users, :id, :full_name, { :include_blank => t(:myself) }, { :style => "width:160px" }
= user_select(:contact)

- if Setting.background_info && Setting.background_info.include?(:contact)
%tr
@@ -13,6 +13,7 @@ if @contact.valid?
page.call "crm.flick", :empty, :remove
else
page[:create_contact].replace_html :partial => "new"
page.call "crm.init_chosen_fields"
page.call "crm.create_or_select_account", request.referer =~ /\/accounts\// || @account.id.blank?
page[:create_contact].visual_effect :shake, :duration => 0.25, :distance => 6
page[:contact_first_name].focus
@@ -22,9 +22,11 @@ else # <----------------------------------------- Show [Edit Contact] form.
page.call "crm.highlight_off", id # Disable onMouseOver for the list item.
page.call "crm.hide_form", :create_contact # Hide [Create Contact] form if any.
page[id].replace_html :partial => "edit" # Show [Edit Contact] form.
page.call "crm.init_chosen_fields"

elsif params[:cancel].false? # Called from title of the contact landing page...
page[:edit_contact].replace_html :partial => "edit"
page.call "crm.init_chosen_fields"
page.call "crm.flip_form", :edit_contact
page.call "crm.set_title", :edit_contact, "#{t :edit} #{h(@contact.full_name)}"
end
@@ -2,12 +2,18 @@

.title_tools
= link_to_inline(:create_contact, new_contact_path, :text=> t(:create_contact)) << " | "
- if Setting.advanced_search_enabled
= link_to_inline(:advanced_search, advanced_search_contacts_path, :text => t(:advanced_search)) << " | "
= link_to_inline(:options, options_contacts_path, :text=> t(:options))

.title
%span#create_contact_title #{t :contacts}
= image_tag("loading.gif", :size => "16x16", :id => "loading", :style => "display: none;")
.remote#options{ hidden }
.remote#advanced_search{ hidden_if(!params[:q]) }
- if @search
= render :partial => "advanced_search"

.remote#create_contact{ hidden }

.list#contacts
@@ -4,6 +4,7 @@ page.call "crm.flip_form", :create_contact

unless params[:cancel].true?
page[:create_contact].replace_html :partial => "new"
page.call "crm.init_chosen_fields"
page.call "crm.set_title", :create_contact, t(:create_contact)
page.call "crm.create_or_select_account", request.referer =~ /\/accounts\// || @account.id.blank?
hook(:new_contact_rjs, self, :page => page)
@@ -8,7 +8,10 @@
= link_to_delete(email)

%tt
= "To ".html_safe << link_to(email.user.full_name, user_path(email.user))
- if email.sent_to.to_s.include?(", ")
= "To: ".html_safe << email.sent_to
- else
= "To ".html_safe << link_to_email(email.sent_to)
= "from ".html_safe << link_to_email(email.sent_from) << ", sent on ".html_safe
= l(email.sent_at, :format => :mmddhhss)
= " | ".html_safe + link_to_function(email.collapsed? ? t(:more) : t(:less), "crm.flip_note_or_email(this, '#{t(:more)}', '#{t(:less)}')", :class => "toggle")
@@ -1,6 +1,6 @@
- collapsed = session[field_group.key].nil?

%div{ :id => "#{field_group.key}_container" }
%div{ :id => "#{field_group.key}_container", :"data-tag" => field_group.tag.try(:name) }
= subtitle field_group.key, collapsed, t(field_group.name, :default => field_group.label)
.section
%small{ hidden_if(!collapsed).merge(:id => "#{field_group.key}_intro") }
@@ -9,10 +9,3 @@
- field_groups[field_group.tag.name.downcase] = "#{field_group.key}_container" if field_group.tag

= render :partial => 'fields/group', :locals => {:f => f, :field_group => field_group}

:javascript
if (fbtaglist) {
crm.set_tag_list_event('#{controller_name}', '#{asset}', '#{params[:id]}');
loadedFieldGroups = $H(#{field_groups.to_json});
}

@@ -4,6 +4,4 @@ simple_fields_for(@asset) do |f|
:partial => 'fields/group',
:locals => {:f => f, :field_group => @field_group, :fields => @field_group.fields}
)

page << "loadedFieldGroups.set('#{@tag.name.downcase}', '#{@field_group.key}_container');"
end
@@ -7,4 +7,8 @@
- begin
= render sidebar
- rescue MissingTemplate

- if Setting.advanced_search_enabled
= render "shared/lists"

= render "shared/recently"
@@ -8,21 +8,22 @@
= stylesheet_link_tag :print, :media => 'print'
- unless tabless_layout?
= stylesheet_link_tag 'calendar_date_select/default'
%style= content_for :styles
%style= yield :styles

= javascript_include_tag :application

- unless tabless_layout?
= javascript_include_tag "calendar_date_select/format_#{t(:calendar_date_select_format, :default => 'american')}"

= csrf_meta_tag
= hook(:javascript_includes, self)

:javascript
#{yield :javascript}

<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<link rel="icon" href="/favicon.ico" type="image/x-icon">

%script{:type => "text/javascript"}= content_for :javascript
= csrf_meta_tag

%body
= render "layouts/header"
- if tabless_layout?
@@ -33,6 +34,7 @@

- unless tabless_layout? || I18n.locale == "en-US"
= localize_calendar_date_select

%script{:type => "text/javascript"}
= "crm.base_url = '#{Setting.base_url}';" unless Setting.base_url.blank?
= get_browser_timezone_offset
@@ -26,7 +26,7 @@
%td= spacer
%td
.label.req #{t :assigned_to}:
= collection_select :account, :assigned_to, @users, :id, :full_name, { :include_blank => t(:myself) }, { :style => "width:160px" }
= user_select(:account)
= render "leads/opportunity"
= render "leads/convert_permissions"
.buttonbar
@@ -10,7 +10,7 @@
%tr
%td
.label.top #{t :assigned_to}:
= collection_select :lead, :assigned_to, @users, :id, :full_name, { :include_blank => t(:myself) }, { :style => "width:160px" }
= user_select(:lead)
%td= spacer
%td
.label.top #{t :status}:
@@ -14,6 +14,7 @@ if @lead.valid?
page.call "crm.flick", :empty, :remove
else
page[:create_lead].replace_html :partial => "new"
page.call "crm.init_chosen_fields"
page[:create_lead].visual_effect :shake, :duration => 0.25, :distance => 6
page[:lead_first_name].focus
end
@@ -22,9 +22,11 @@ else # <---------------------------------------- Show [Edit Campaign] form.
page.call "crm.highlight_off", id # Disable onMouseOver for the list item.
page.call "crm.hide_form", :create_lead # Hide [Create Lead] form if any.
page[id].replace_html :partial => "edit" # Show [Edit Lead] form.
page.call "crm.init_chosen_fields"

elsif params[:cancel].false? # Called from title of the lead landing page...
page[:edit_lead].replace_html :partial => "edit"
page.call "crm.init_chosen_fields"
unless %w(converted rejected).include? @lead.status
page.call "crm.hide_form", :convert_lead
end
@@ -2,12 +2,17 @@

.title_tools
= link_to_inline(:create_lead, new_lead_path, :text => t(:create_lead)) << " | "
- if Setting.advanced_search_enabled
= link_to_inline(:advanced_search, advanced_search_leads_path, :text => t(:advanced_search)) << " | "
= link_to_inline(:options, options_leads_path, :text => t(:options))

.title
%span#create_lead_title #{t :leads}
= image_tag("loading.gif", :size => "16x16", :id => "loading", :style => "display: none;")
.remote#options{ hidden }
.remote#advanced_search{ hidden_if(!params[:q]) }
- if @search
= render :partial => "advanced_search"
.remote#create_lead{ hidden }

.list#leads
@@ -4,6 +4,7 @@ page.call "crm.flip_form", :create_lead

unless params[:cancel].true?
page[:create_lead].replace_html :partial => "new"
page.call "crm.init_chosen_fields"
page.call "crm.set_title", :create_lead, t(:create_lead)
else
page.call "crm.set_title", :create_lead, t(:leads)
@@ -0,0 +1,9 @@
if @list.valid?
page[:lists].replace :partial => "shared/lists"
page.call "jQuery('.show_lists_save_form').show"

else # Couldn't create the saved list -- validation failed.
page[:new_list].visual_effect :shake, :duration => 0.25, :distance => 6
page[:list_name].focus
page[:save_list].enable
end
@@ -0,0 +1 @@
page[dom_id(@list)].visual_effect "blind_up", :duration => 0.25
@@ -46,13 +46,13 @@
-# Add [-- None --] account choice when editing existing opportunity that has an account.
- options = { :selected => @account.id || 0 }
- options[:include_blank] = true #t(:select_none) unless @opportunity.new_record? || @opportunity.account.blank?

= collection_select :account, :id, @accounts, :id, :name, options, { :style => "width:330px; display:none;" }
= account_select(options)

= a.text_field :name, :style => "width:324px; display:none;"
%td= spacer
%td
.label.req #{t :assigned_to}:
= collection_select :opportunity, :assigned_to, @users, :id, :full_name, { :include_blank => t(:myself) }, { :style => "width:160px" }
= user_select(:opportunity)

- if Setting.background_info && Setting.background_info.include?(:opportunity)
%tr
@@ -22,6 +22,7 @@ if @opportunity.valid?
page.call "crm.flick", :empty, :remove
else
page[:create_opportunity].replace_html :partial => "new"
page.call "crm.init_chosen_fields"
page.call "crm.create_or_select_account", request.referer =~ /\/accounts\// || @account.id.blank?
page.call "crm.date_select_popup", "opportunity_closes_on"
hook(:new_opportunity_rjs, self, :page => page)
@@ -22,9 +22,11 @@ else # <----------------------------------------- Show requested [Edit Opportuni
page.call "crm.highlight_off", id # Disable onMouseOver for the list item.
page.call "crm.hide_form", :create_opportunity# Hide [Create Opportunity] form if any.
page[id].replace_html :partial => "edit" # Show [Edit Opportunity] form.
page.call "crm.init_chosen_fields"

elsif params[:cancel].false? # Called from title of the opportunity landing page...
page[:edit_opportunity].replace_html :partial => "edit"
page.call "crm.init_chosen_fields"
page.call "crm.flip_form", :edit_opportunity
page.call "crm.set_title", :edit_opportunity, "#{t :edit} #{@opportunity.name}"
end
@@ -2,12 +2,17 @@

.title_tools
= link_to_inline(:create_opportunity, new_opportunity_path, :text => t(:create_opportunity)) << " | "
- if Setting.advanced_search_enabled
= link_to_inline(:advanced_search, advanced_search_opportunities_path, :text => t(:advanced_search)) << " | "
= link_to_inline(:options, options_opportunities_path, :text => t(:options))

.title
%span#create_opportunity_title #{t :opportunities}
= image_tag("loading.gif", :size => "16x16", :id => "loading", :style => "display: none;")
.remote#options{ hidden }
.remote#advanced_search{ hidden_if(!params[:q]) }
- if @search
= render :partial => "advanced_search"
.remote#create_opportunity{ hidden }

.list#opportunities
@@ -4,6 +4,7 @@ page.call "crm.flip_form", :create_opportunity

unless params[:cancel].true?
page[:create_opportunity].replace_html :partial => "new"
page.call "crm.init_chosen_fields"
page.call "crm.set_title", :create_opportunity, t(:create_opportunity)
# Opportunity 'create' form defaults to select account instead of create new account
page.call "crm.select_account", true
@@ -1,7 +1,8 @@
- assets = controller_name
- asset = assets.singularize
- new_asset_path = controller.class.to_s.include?("Admin") ? url_for([:new, :admin, asset]) : url_for([:new, asset])
#empty
- if @current_query.blank?
== #{t(:could_not_find, t(assets.downcase))} #{link_to_inline(:"create_#{asset}", send("new_" << asset << "_path"), :plain => true, :text => t(:create_a_new) << ' ' << t(asset + '_small'))}.
== #{t(:could_not_find, t(assets.downcase))} #{link_to_inline(:"create_#{asset}", new_asset_path, :plain => true, :text => t(:create_a_new) << ' ' << t(asset + '_small'))}.
- else
== #{t(:could_not_find_matching, t(assets.downcase + '_small'))} <span class="cool"><b>#{h @current_query}</b></span>; #{t :please_retry}
@@ -0,0 +1,25 @@
- @lists = List.all
- @list = List.new
.panel#lists
.caption #{t :lists}
%ul
- if @lists.none?
%div #{t :no_saved_lists}
- else
- @lists.sort.each_with_index do |list, i|
%li[list]{ :class => i < @lists.size - 1 ? "" : "last" }
%dt= link_to(truncate(list.name, :length => 25), list.url, :title => list.name)
%tt= link_to(image_tag("/assets/tab_icons/#{list.controller}_active.png", :"data-controller" => list.controller), url_for(list), :method => :delete, :confirm => 'Are you sure?', :remote => true, :class => "list_icon delete_on_hover")

.show_lists_save_form{ hidden_if(!params[:q]) }
= link_to(t(:make_current_view_list), '#')

.save_list{ hidden }
= simple_form_for(@list, :html => one_submit_only(:list), :remote => true) do |f|
= f.text_field :name
= image_tag("/assets/info_tiny.png", :title => t(:list_name_info), :class => "input_info")
= f.hidden_field :url
%br
= f.submit(t(:save), :id => "save_list", :style => "vertical-align: bottom;")
#{t :or}
= link_to(t(:cancel), '#', :class => "hide_lists_save_form")
@@ -3,7 +3,7 @@

%h4 #{t(:search_assets, t(controller.controller_name + "_small"))}
%div{ :style => "margin: 0px 0px 6px 0px" }
= text_field_tag("query", @current_query, :size => 23)
= text_field_tag('query', @current_query, :size => 23)

:javascript
new Form.Element.Observer('query', 1.5, function(element, value) {
@@ -2,20 +2,8 @@
- f.object.tags = unsaved_param_tags(asset) if params[asset] && params[asset][:tag_list]
%tr
%td{ :valign => :top, :colspan => span }
.label Tags: <small>(comma separated, letters and digits only)</small>
#taggings
#facebook-list
= f.text_field :tag_list, :id => "tag_list", :style => "width:500px", :autocomplete => "off"
#facebook-auto
.default Type the name of a tag you'd like to use. Use commas to separate multiple tags.
%ul.feed
- # Get tags from the object.
- f.object.tags.map{|t| t.name }.each do |tag|
%li{ :value => tag }= tag
:javascript
fbtaglist = new FacebookList('tag_list', 'facebook-auto',
{ newValues: true,
regexSearch: false,
separator: Event.KEY_COMMA });
var tagjson = #{Tag.all.map{|t| {"caption" => t.name, "value" => t.name} }.to_json}
tagjson.each(function(t){fbtaglist.autoFeed(t)});
.label Tags:
= f.select :tag_list, Tag.all.map{|t| [t.name, t.name] }, {:value => f.object.tags.map{|t| t.name }}, {:multiple => true, :"data-placeholder" => t(:select_or_create_tags), :style => "width: 500px;"}

:javascript
crm.chosen_taglist('#{asset}', '#{controller_name}', '#{params[:id]}');
@@ -17,7 +17,7 @@
%td= spacer
%td
.label.req #{t :assign_to}:
= collection_select :task, :assigned_to, @users, :id, :full_name, { :include_blank => t(:myself) }, { :style => "width:160px", :disabled => @task.assigned_to == @current_user.id }
= user_select(:task)
%td= spacer
%td
.label.req #{t :category}:
@@ -18,6 +18,7 @@ else # <---------------------------------------- Show [Edit Task] form.
end
page.call "crm.highlight_off", id # Disable onMouseOver for the list item.
page[id].replace_html :partial => "edit" # Show [Edit Task] form.
page.call "crm.init_chosen_fields"
page.call "crm.date_select_popup", :task_calendar, :task_bucket, Setting.task_calendar_with_time
page[:task_name].focus

@@ -3,6 +3,7 @@ page.call "crm.flip_form", :create_task

unless params[:cancel].true?
page[:create_task].replace_html :partial => "new"
page.call "crm.init_chosen_fields"
page.call "crm.set_title", :create_task, t(:create_task)
page.call "crm.date_select_popup", :task_calendar, :task_bucket, Setting.task_calendar_with_time
else
@@ -26,13 +26,14 @@ en-US:

admin_tab_users: Users
admin_tab_fields: Custom Fields
admin_tab_tags: Tags
admin_tab_settings: Settings
admin_tab_plugins: Plugins

affiliate: Affiliate
competitor: Competitor
customer: Customer
partner: Partner
partner: Partneradm
reseller: Reseller
vendor: Vendor

@@ -272,6 +273,7 @@ en-US:
# Views -> Accounts.
#----------------------------------------------------------------------------
account: Account
select_an_account: Select an Account
account_small: account
account_summary: Account Summary
accounts: Accounts
@@ -572,6 +574,32 @@ en-US:
quick_find: Quick find
welcome: Welcome

# Views -> Advanced Search.
accounts_advanced_search: Accounts Advanced search
advanced_search: Advanced search
advanced_search_submit: Search
advanced_search_add_group: Add a group of filters
advanced_search_group_first: Show results where %{combinator} of the following match
advanced_search_group_rest: ...and where %{combinator} of the following match
advanced_search_add_condition: Add a filter
advanced_search_remove_condition: Remove filter

ransack:
predicates:
eq: is
not_eq: is not
lt: is less than
gt: is greater than
matches: matches
does_not_match: doesn't match
cont: contains
not_cont: doesn't contain
blank: is blank
present: is present
"null": is null
not_null: is not null


# Views -> Comments.
#----------------------------------------------------------------------------
edit_comment: Editing comment
@@ -628,6 +656,14 @@ en-US:
subject: Subject
body: Body

# Lists
#----------------------------------------------------------------------------
lists: Lists
list: List
no_saved_lists: No saved lists
make_current_view_list: Make current view a list
list_name_info: If you use the name of an existing list, you will overwrite that list with your current settings

# Pluralizations.
#----------------------------------------------------------------------------
pluralize:
@@ -688,10 +724,12 @@ en-US:

multi_page: "Displaying %{plural} %{from} - %{to} of %{total} in total"


# Views -> Admin -> Custom Fields
#----------------------------------------------------------------------------
label: Label
custom_fields: Custom Fields
custom_field_options: Custom Field Options
create_field: Create field
save_field: Save field
create_field_group: Create field group
@@ -700,12 +738,34 @@ en-US:

field_group_empty: There are no fields in this group.

select_or_create_tags: Select some tags, or create a new tag by pressing 'enter'.

restrict_by_tag: Restrict by Tag
restrict_by_tag_info: Only show fields for %{assets} that are tagged with
field_group_tag_restriction: This field group applies to %{assets} tagged with "%{tag}"
field_group_unrestricted: This field group applies to all %{assets}
field_group_confirm_delete: If a field group is deleted, any fields will be moved to the default field group.
msg_cant_delete_field_group: Field Group could not be deleted.

admin_fields_info: |
Custom fields are displayed in groups.
Create a new field group, or add fields to one of the groups below.<br />
You can drag and drop fields to change their display order or move them between field groups.

# Views -> Admin -> Tags
#----------------------------------------------------------------------------
tags: Tags
tag_small: tag
tagged: Tagged
create_tag: Create Tag
save_tag: Save Tag
field_group_tags: Field Groups shown for this Tag
tag_with_taggings_confirm_delete: "If this tag is deleted, it will be removed from %{value} records."
msg_cant_delete_tag: "Couldn't delete '%{value}' since it has associated Field Groups."


# Views -> Admin -> Plugins
#----------------------------------------------------------------------------
author: Author
version: Version
description: Description
@@ -1,5 +1,7 @@
Rails.application.routes.draw do
scope Setting.base_url.to_s do
resources :lists

root :to => 'home#index'

match 'activities' => 'home#index'
@@ -21,10 +23,11 @@

resources :accounts, :id => /\d+/ do
collection do
get :advanced_search
post :filter
get :options
get :field_group
post :auto_complete
match :auto_complete
post :redraw
end
member do
@@ -37,6 +40,7 @@

resources :campaigns, :id => /\d+/ do
collection do
get :advanced_search
post :filter
get :options
get :field_group
@@ -53,6 +57,7 @@

resources :contacts, :id => /\d+/ do
collection do
get :advanced_search
post :filter
get :options
get :field_group
@@ -68,6 +73,7 @@

resources :leads, :id => /\d+/ do
collection do
get :advanced_search
post :filter
get :options
get :field_group
@@ -85,6 +91,7 @@

resources :opportunities, :id => /\d+/ do
collection do
get :advanced_search
post :filter
get :options
get :field_group
@@ -133,6 +140,9 @@
collection do
post :sort
end
member do
get :confirm
end
end

resources :fields do
@@ -143,6 +153,13 @@
post :sort
end
end

resources :tags do
member do
get :confirm
end
end

resources :fields, :as => :custom_fields
resources :fields, :as => :core_fields

@@ -144,6 +144,7 @@ task_calendar_with_time: false
:admin_tabs: [
{ :active : true, :text : :admin_tab_users, :url : { :controller : "admin/users" } },
{ :active : true, :text : :admin_tab_fields, :url : { :controller : "admin/fields" } },
{ :active : true, :text : :admin_tab_tags, :url : { :controller : "admin/tags" } },
{ :active : false, :text : :admin_tab_settings, :url : { :controller : "admin/settings" } },
{ :active : false, :text : :admin_tab_plugins, :url : { :controller : "admin/plugins" } }
]
@@ -286,3 +287,9 @@ task_completed:
- :completed_this_month
- :completed_last_month


# Alpha features
#------------------------------------------------------------------------------
# Enable the advanced search feature provided by Ransack
# (Feature works, but UI design is still in progress)
advanced_search_enabled: false
@@ -0,0 +1,10 @@
class CreateLists < ActiveRecord::Migration
def change
create_table :lists do |t|
t.string :name
t.text :url

t.timestamps
end
end
end
@@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.

ActiveRecord::Schema.define(:version => 20111209175716) do
ActiveRecord::Schema.define(:version => 20120121054235) do

create_table "account_contacts", :force => true do |t|
t.integer "account_id"
@@ -256,6 +256,13 @@
add_index "leads", ["assigned_to"], :name => "index_leads_on_assigned_to"
add_index "leads", ["user_id", "last_name", "deleted_at"], :name => "index_leads_on_user_id_and_last_name_and_deleted_at", :unique => true

create_table "lists", :force => true do |t|
t.string "name"
t.text "url"
t.datetime "created_at"
t.datetime "updated_at"
end

create_table "opportunities", :force => true do |t|
t.integer "user_id"
t.integer "campaign_id"
@@ -95,7 +95,7 @@ def auto_link_urls(text, html_options = {}, options = {})
sanitize = options[:sanitize] != false
content_tag(:a, link_text, link_attributes.merge('href' => href), sanitize) + punctuation.reverse.join('')
end
end
end.html_safe
end

# Turns all email addresses into clickable links. If a block is given,
@@ -111,7 +111,7 @@ def auto_link_email_addresses(text, html_options = {}, options = {})
display_text = sanitize(display_text) unless options[:sanitize] == false
mail_to text, display_text, html_options
end
end
end.html_safe
end

# Detects already linked context or position in the middle of a tag
@@ -0,0 +1,5 @@
require 'spec_helper'

describe ListsController do

end
@@ -0,0 +1,5 @@
Factory.define :list do |s|
s.name "Foo List"
s.url "/controller/action"
end

@@ -0,0 +1,15 @@
require 'spec_helper'

# Specs in this file have access to a helper object that includes
# the ListsHelper. For example:
#
# describe ListsHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# helper.concat_strings("this","that").should == "this that"
# end
# end
# end
describe ListsHelper do
pending "add some examples to (or delete) #{__FILE__}"
end
@@ -0,0 +1,12 @@
require 'spec_helper'

describe List do
it "should parse the controller from the url" do
["/controller/action", "controller/action?utf8=%E2%9C%93"].each do |url|
list = Factory.build(:list, :url => url)
list.controller.should == "controller"
end
list = Factory.build(:list, :url => nil)
list.controller.should == nil
end
end
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,64 @@
/**
* Event.simulate(@element, eventName[, options]) -> Element
*
* - @element: element to fire event on
* - eventName: name of event to fire (only MouseEvents and HTMLEvents interfaces are supported)
* - options: optional object to fine-tune event properties - pointerX, pointerY, ctrlKey, etc.
*
* $('foo').simulate('click'); // => fires "click" event on an element with id=foo
*
**/
(function(){

var eventMatchers = {
'HTMLEvents': /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,
'MouseEvents': /^(?:click|mouse(?:down|up|over|move|out))$/
}
var defaultOptions = {
pointerX: 0,
pointerY: 0,
button: 0,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
bubbles: true,
cancelable: true
}

Event.simulate = function(element, eventName) {
var options = Object.extend(defaultOptions, arguments[2] || { });
var oEvent, eventType = null;

element = $(element);

for (var name in eventMatchers) {
if (eventMatchers[name].test(eventName)) { eventType = name; break; }
}

if (!eventType)
throw new SyntaxError('Only HTMLEvents and MouseEvents interfaces are supported');

if (document.createEvent) {
oEvent = document.createEvent(eventType);
if (eventType == 'HTMLEvents') {
oEvent.initEvent(eventName, options.bubbles, options.cancelable);
}
else {
oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
options.button, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element);
}
element.dispatchEvent(oEvent);
}
else {
options.clientX = options.pointerX;
options.clientY = options.pointerY;
oEvent = Object.extend(document.createEventObject(), options);
element.fireEvent('on' + eventName, oEvent);
}
return element;
}

Element.addMethods({ simulate: Event.simulate });
})()