Skip to content

Commit

Permalink
Add "autocomplete" custom field type that can look up values in user-…
Browse files Browse the repository at this point in the history
…defined lists
  • Loading branch information
reubenjs committed Dec 29, 2014
1 parent fc94778 commit 2caf940
Show file tree
Hide file tree
Showing 25 changed files with 363 additions and 2 deletions.
1 change: 1 addition & 0 deletions app/assets/javascripts/application.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
//= require timeago
//= require pagination
//= require ffcrm_merge
//= require crm_text_with_autocomplete
//= require_self

<%
Expand Down
17 changes: 17 additions & 0 deletions app/assets/javascripts/crm_text_with_autocomplete.js.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
(($) ->

window.crm ||= {}

crm.text_with_autocomplete = (el_id) ->
unless $("#text_with_autocomplete_" + el_id)[0]
$('#' + el_id).autocomplete
minLength: 2
source: (request, response) ->
$.ajax
url: $('#' + el_id).data('autocompleteurl')
dataType: "json"
data:
name: request.term
success: (data) ->
response(data)
) jQuery
65 changes: 65 additions & 0 deletions app/controllers/admin/autocompletes_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright (c) 2008-2013 Michael Dvorkin and contributors.
#
# Fat Free CRM is freely distributable under the terms of MIT license.
# See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
#------------------------------------------------------------------------------
class Admin::AutocompletesController < Admin::ApplicationController
before_filter "set_current_tab('admin/autocompletes')", :only => [ :index, :show ]

load_resource

# GET /admin/autocompletes
# GET /admin/autocompletes.xml HTML
#----------------------------------------------------------------------------
def index
@autocompletes = Autocomplete.all
respond_with(@autocompletes)
end

# GET /admin/autocompletes/new
# GET /admin/autocompletes/new.xml AJAX
#----------------------------------------------------------------------------
def new
respond_with(@autocomplete)
end

# GET /admin/autocompletes/1/edit AJAX
#----------------------------------------------------------------------------
def edit
if params[:previous].to_s =~ /(\d+)\z/
@previous = Autocomplete.find_by_id($1) || $1.to_i
end
end

# POST /admin/autocompletestags
# POST /admin/autocompletes.xml AJAX
#----------------------------------------------------------------------------
def create
@autocomplete.update_attributes(params[:autocomplete])

respond_with(@autocomplete)
end

# PUT /admin/autocompletes/1
# PUT /admin/autocompletes/1.xml AJAX
#----------------------------------------------------------------------------
def update
@autocomplete.update_attributes(params[:autocomplete])

respond_with(@autocomplete)
end

# DELETE /admin/autocompletes/1
# DELETE /admin/autocompletes/1.xml AJAX
#----------------------------------------------------------------------------
def destroy
@autocomplete.destroy

respond_with(@autocomplete)
end

# GET /admin/autocompletes/1/confirm AJAX
#----------------------------------------------------------------------------
def confirm
end
end
28 changes: 28 additions & 0 deletions app/controllers/autocompletes_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class AutocompletesController < ApplicationController
def get_results
if params[:id] && params[:name]
if list = Autocomplete.find_by_name(params[:id])

#construct regex /.*(?=.*TERM1.*)(?=.*TERM2.*) ... .*/
r = ".*"
search_terms = params[:name].split(" ").each do |t|
r += "(?=.*#{t}.*)"
end
r += ".*"

result = list.terms.select{ |s| s =~ /#{r}/i }

result.collect do |t|
{ value: t }
end

render json: result

else
render json: "list not found", status: 404
end
else
render json: "list and search term(s) required", status: 500
end
end
end
10 changes: 10 additions & 0 deletions app/helpers/admin/autocompletes_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2008-2013 Michael Dvorkin and contributors.
#
# Fat Free CRM is freely distributable under the terms of MIT license.
# See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
#------------------------------------------------------------------------------
module Admin::AutocompletesHelper
def link_to_confirm(autocomplete)
link_to(t(:delete) + "?", confirm_admin_autocomplete_path(autocomplete), :method => :get, :remote => true)
end
end
39 changes: 39 additions & 0 deletions app/inputs/autocomplete_input.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright (c) 2008-2013 Michael Dvorkin and contributors.
#
# Fat Free CRM is freely distributable under the terms of MIT license.
# See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
#------------------------------------------------------------------------------
class AutocompleteInput < SimpleForm::Inputs::Base
def input
field = get_field

script = template.content_tag(:script, type: 'text/javascript') do
template.cdata_section("crm.text_with_autocomplete('#{@builder.object.class.to_s.downcase}_#{attribute_name}');")
end

input_html_options.merge!('data-autocompleteUrl' => "/autocompletes/#{field.collection_string}/get_results")

@builder.text_field(attribute_name, input_html_options) + script
end





private

# Autocomplete latches onto the 'text_with_autocomplete' class.
#------------------------------------------------------------------------------
def input_html_classes
super.push('text_with_autocomplete')
end

# Returns the field as field1
#------------------------------------------------------------------------------
def get_field
@field1 ||= Field.where(:name => attribute_name).first
end


ActiveSupport.run_load_hooks(:fat_free_crm_autocomplete_input, self)
end
14 changes: 14 additions & 0 deletions app/models/autocomplete.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Autocomplete < ActiveRecord::Base
validates_presence_of :name
validates_presence_of :terms
validates_uniqueness_of :name

serialize :terms, Array

def terms_string=(value)
self.terms = value.lines.map(&:squish).reject(&:blank?)
end
def terms_string
terms.try(:join, "\n")
end
end
3 changes: 2 additions & 1 deletion app/models/fields/field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ class Field < ActiveRecord::Base
'datetime' => {:klass => 'CustomField', :type => 'timestamp'},
'decimal' => {:klass => 'CustomField', :type => 'decimal', :column_options => {:precision => 15, :scale => 2} },
'integer' => {:klass => 'CustomField', :type => 'integer'},
'float' => {:klass => 'CustomField', :type => 'float'}
'float' => {:klass => 'CustomField', :type => 'float'},
'autocomplete'=> {:klass => 'CustomField', :type => 'string'}
}.with_indifferent_access

validates_presence_of :label, :message => "^Please enter a field label."
Expand Down
8 changes: 8 additions & 0 deletions app/views/admin/autocompletes/_autocomplete.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
%li.highlight[autocomplete]
.tools
= link_to_edit(autocomplete, :url => edit_admin_autocomplete_path(autocomplete)) << " |"
= link_to_confirm(autocomplete)

%span.black= autocomplete.name
%tt
%dt
5 changes: 5 additions & 0 deletions app/views/admin/autocompletes/_confirm.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.confirm[@autocomplete, :confirm]
#{t :autocomplete_confirm_delete}
#{t :delete} <b>#{@autocomplete.name}</b>?
= link_to_confirm_delete(@autocomplete) << " : "
= link_to_function(t(:no_button), "crm.flick('#{dom_id(@autocomplete, :confirm)}', 'remove')")
9 changes: 9 additions & 0 deletions app/views/admin/autocompletes/_edit.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.remote
= form_for([:admin, @autocomplete], :as => :autocomplete, :html => one_submit_only.merge(:class => "edit_autocomplete", :id => "edit_autocomplete_#{@autocomplete.id}"), :remote => true) do |f|
= link_to_close edit_admin_autocomplete_path(@autocomplete)
= f.error_messages
= render :partial => "top_section", :locals => { :f => f, :edit => true }
.buttonbar
= f.submit t(:save_autocomplete), :id => :autocomplete_submit
or
= link_to_cancel edit_admin_autocomplete_path(@autocomplete)
10 changes: 10 additions & 0 deletions app/views/admin/autocompletes/_new.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- path = new_admin_autocomplete_path

= form_for([:admin, @autocomplete], :html => one_submit_only, :remote => true) do |f|
= link_to_close path
= f.error_messages
= render :partial => "top_section", :locals => { :f => f }
.buttonbar
= f.submit t(:create_autocomplete), :id => :autocomplete_submit
or
= link_to_cancel path
10 changes: 10 additions & 0 deletions app/views/admin/autocompletes/_top_section.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.section
%table
%tr
%td
.label.top.req= t(:name) + ":"
= f.text_field :name
%tr
%td
.label.top.req= t(:terms) + "(one per line):"
= f.text_area :terms_string
7 changes: 7 additions & 0 deletions app/views/admin/autocompletes/confirm.js.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- id = dom_id(@autocomplete, :confirm)

if ($('##{id}').size() > 0) {
crm.flick('#{id}', 'remove');
} else {
$('##{dom_id(@autocomplete)}').prepend('#{ j (render :partial => "confirm") }');
}
11 changes: 11 additions & 0 deletions app/views/admin/autocompletes/create.js.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
- if @autocomplete.valid?
$('#create_autocomplete_arrow').html(crm.COLLAPSED);
$('#create_autocomplete_title').html('#{ j t(:autocompletes) }');
$('#create_autocomplete').slideUp(250);
$('#autocompletes').prepend('#{ j (render :partial => "autocomplete", :collection => [ @autocomplete ]) }');
$('##{dom_id(@autocomplete)}').effect("highlight", { duration:1500 });
crm.flick('empty', 'remove');
- else
$('#create_autocomplete').html('#{ j render(:partial => 'new') }');
$('#create_autocomplete').effect("shake", { duration:250, distance: 6 });
$('#autocomplete_name').focus();
10 changes: 10 additions & 0 deletions app/views/admin/autocompletes/destroy.js.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- id = dom_id(@autocomplete)

- if @autocomplete.destroyed?
$('##{id}').css('background-color', '#ffe4e1').slideUp(250);
- else
crm.flick("#{dom_id(@autocomplete, :confirm)}, 'remove')");
$('##{id}').effect("shake", { duration:250, distance: 6 });
$('#flash').html('#{ j flash[:warning] }');
crm.flash('warning');
- flash[:warning] = nil
20 changes: 20 additions & 0 deletions app/views/admin/autocompletes/edit.js.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
- id = dom_id(@autocomplete)

- if params[:cancel].true? # <----------------- Hide [Edit autocomplete]
$('##{id}').replaceWith('#{ j render(:partial => "autocomplete", :collection => [ @autocomplete ]) }');

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

- if @previous # Hide open [Edit autocomplete] form if any.
- if @previous.is_a?(Autocomplete) # Previous autocomplete still exists?
$('##{dom_id(@previous)}').replaceWith('#{ j render(:partial => "autocomplete", :collection => [ @previous ]) }');
- else
crm.flick('autocomplete_#{@previous}', 'remove');

-# Disable onMouseOver for the list item.
-# Hide [Create autocomplete] form if any.
-# Show [Edit autocomplete] form.
crm.highlight_off('#{id}');
crm.hide_form('create_autocomplete');
$('##{id}').html('#{ j render(:partial => "edit") }');
$('#autocomplete_name').focus();
17 changes: 17 additions & 0 deletions app/views/admin/autocompletes/index.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
= styles_for :autocomplete

.title_tools
= link_to_inline(:create_autocomplete, new_admin_autocomplete_path, :text => t(:create_autocomplete))

.title
%span#create_autocomplete_title #{t :autocompletes}
= image_tag("loading.gif", :size => :thumb, :id => "loading", :style => "display: none;")
.remote#create_autocomplete{ hidden }

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

#export= render "shared/export"
7 changes: 7 additions & 0 deletions app/views/admin/autocompletes/new.js.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
crm.flip_form('create_autocomplete');

- unless params[:cancel].true?
$('#create_autocomplete').html('#{ j render(:partial => "new") }');
crm.set_title('create_autocomplete', '#{t(:create_autocomplete)}');
- else
crm.set_title('create_autocomplete', '#{t(:autocompletes)}');
9 changes: 9 additions & 0 deletions app/views/admin/autocompletes/update.js.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
- id = dom_id(@autocomplete)

- if @autocomplete.errors.empty?
$('##{id}').replaceWith('#{ j render(:partial => "autocomplete", :collection => [ @autocomplete ]) }');
$('##{id}').effect('highlight', { duration: 1000 });
- else
$('##{id}').html('#{ j render(:partial => "edit") }');
$('##{id}').effect("shake", { duration:250, distance: 6 });
$('#tag_autocompletename').focus();
25 changes: 25 additions & 0 deletions app/views/admin/custom_fields/_autocomplete_field.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
%div
.label.top.req
= "Autocomplete list name:"
= f.text_field :collection_string, :class => 'field_collection_string', :size => 78

%table
%tr
%td

%td= spacer
%td
.label.top
%span Required:
= f.check_box :required
%br
%span Disabled:
= f.check_box :disabled
%tr
%td
.label.top Hint:
= f.text_field :hint
%td= spacer
%td
.label.top Placeholder:
= f.text_field :placeholder
10 changes: 10 additions & 0 deletions config/locales/en-US_fat_free_crm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ en-US:
admin_tab_settings: Settings
admin_tab_plugins: Plugins
admin_tab_imports: Import
admin_tab_autocompletes: Autocomplete

affiliate: Affiliate
competitor: Competitor
Expand Down Expand Up @@ -973,6 +974,13 @@ en-US:
version: Version
description: Description

# Views -> Admin -> Autocompletes
#----------------------------------------------------------------------------
autocompletes: Autocomplete lists
autocomplete_small: autocomplete
create_autocomplete: Create Autocomplete
save_autocomplete: Save Autocomplete

# Simple Form translations
#----------------------------------------------------------------------------
simple_form:
Expand Down Expand Up @@ -1024,6 +1032,8 @@ en-US:
title: Number (Integer)
float:
title: Number (Floating Point)
autocomplete:
title: Autocomplete list
pair:
start: Start
end: End
Expand Down
Loading

0 comments on commit 2caf940

Please sign in to comment.