Revision 6c630204
Von Sven Schöling vor fast 9 Jahren hinzugefügt
SL/Controller/TopQuickSearch.pm | ||
---|---|---|
1 |
package SL::Controller::TopQuickSearch; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent qw(SL::Controller::Base); |
|
5 |
|
|
6 |
use SL::ClientJS; |
|
7 |
use SL::JSON; |
|
8 |
use SL::Locale::String qw(t8); |
|
9 |
|
|
10 |
use Rose::Object::MakeMethods::Generic ( |
|
11 |
'scalar --get_set_init' => [ qw(module js) ], |
|
12 |
); |
|
13 |
|
|
14 |
my @available_modules = qw( |
|
15 |
SL::Controller::TopQuickSearch::Assembly |
|
16 |
SL::Controller::TopQuickSearch::Contact |
|
17 |
SL::Controller::TopQuickSearch::GLTransaction |
|
18 |
); |
|
19 |
my %modules_by_name; |
|
20 |
|
|
21 |
sub action_query_autocomplete { |
|
22 |
my ($self) = @_; |
|
23 |
|
|
24 |
my $hashes = $self->module->query_autocomplete; |
|
25 |
|
|
26 |
$self->render(\ SL::JSON::to_json($hashes), { layout => 0, type => 'json', process => 0 }); |
|
27 |
} |
|
28 |
|
|
29 |
sub action_select_autocomplete { |
|
30 |
my ($self) = @_; |
|
31 |
|
|
32 |
my $redirect_url = $self->module->select_autocomplete; |
|
33 |
|
|
34 |
$self->js->redirect_to($redirect_url)->render; |
|
35 |
} |
|
36 |
|
|
37 |
sub action_do_search { |
|
38 |
my ($self) = @_; |
|
39 |
|
|
40 |
my $redirect_url = $self->module->do_search; |
|
41 |
|
|
42 |
if ($redirect_url) { |
|
43 |
$self->js->redirect_to($redirect_url) |
|
44 |
} |
|
45 |
|
|
46 |
$self->js->render; |
|
47 |
} |
|
48 |
|
|
49 |
sub available_modules { |
|
50 |
my ($self) = @_; |
|
51 |
|
|
52 |
$self->require_modules; |
|
53 |
|
|
54 |
map { $_->new } @available_modules; |
|
55 |
} |
|
56 |
|
|
57 |
sub active_modules { |
|
58 |
grep { |
|
59 |
$::auth->assert($_->auth, 1) |
|
60 |
} $_[0]->available_modules |
|
61 |
} |
|
62 |
|
|
63 |
sub init_module { |
|
64 |
my ($self) = @_; |
|
65 |
|
|
66 |
$self->require_modules; |
|
67 |
|
|
68 |
die t8('Need module') unless $::form->{module}; |
|
69 |
|
|
70 |
$::lxdebug->dump(0, "modules", \%modules_by_name); |
|
71 |
|
|
72 |
die t8('Unknown module #1', $::form->{module}) unless my $class = $modules_by_name{$::form->{module}}; |
|
73 |
|
|
74 |
$::lxdebug->dump(0, "auth:", $class->auth); |
|
75 |
|
|
76 |
$::auth->assert($class->auth); |
|
77 |
|
|
78 |
return $class->new; |
|
79 |
} |
|
80 |
|
|
81 |
sub init_js { |
|
82 |
SL::ClientJS->new(controller => $_[0]) |
|
83 |
} |
|
84 |
|
|
85 |
sub require_modules { |
|
86 |
my ($self) = @_; |
|
87 |
|
|
88 |
if (!$self->{__modules_required}) { |
|
89 |
for my $class (@available_modules) { |
|
90 |
eval "require $class" or die $@; |
|
91 |
$modules_by_name{ $class->name } = $class; |
|
92 |
} |
|
93 |
$self->{__modules_required} = 1; |
|
94 |
} |
|
95 |
} |
|
96 |
|
|
97 |
1; |
|
98 |
|
|
99 |
__END__ |
|
100 |
|
|
101 |
=encoding utf-8 |
|
102 |
|
|
103 |
=head1 NAME |
|
104 |
|
|
105 |
SL::Controller::TopQuickSearch - Framework for pluggable quicksearch fields in the layout top header. |
|
106 |
|
|
107 |
=head1 SYNOPSIS |
|
108 |
|
|
109 |
use SL::Controller::TopQuickSearch; |
|
110 |
my $search = SL::Controller::TopQuickSearch->new; |
|
111 |
|
|
112 |
# in layout |
|
113 |
[%- FOREACH module = search.available_modules %] |
|
114 |
<input type='text' id='top-search-[% module.name %]'> |
|
115 |
[%- END %] |
|
116 |
|
|
117 |
=head1 DESCRIPTION |
|
118 |
|
|
119 |
This controller provides abstraction for different search plugins, and ensures |
|
120 |
that all follow a common useability scheme. |
|
121 |
|
|
122 |
Modules should be configurable, but currently are not. Diabling modules can be |
|
123 |
done by removing them from available_modules. |
|
124 |
|
|
125 |
=head1 BEHAVIOUR REQUIREMENTS |
|
126 |
|
|
127 |
=over 4 |
|
128 |
|
|
129 |
=item * |
|
130 |
|
|
131 |
A single text input field with the html5 placeholder containing a small |
|
132 |
description of the target will be rendered from the plugin information. |
|
133 |
|
|
134 |
=item * |
|
135 |
|
|
136 |
On typing, the autocompletion must be enabled. |
|
137 |
|
|
138 |
=item * |
|
139 |
|
|
140 |
On C<Enter>, the search should redirect to an appropriate listing of matching |
|
141 |
results. |
|
142 |
|
|
143 |
If only one item matches the result, the plugin should instead redirect |
|
144 |
directly to the matched item. |
|
145 |
|
|
146 |
=item * |
|
147 |
|
|
148 |
Search terms should accept the broadest possible matching, and if possible with |
|
149 |
C<multi> parsing. |
|
150 |
|
|
151 |
=item * |
|
152 |
|
|
153 |
In case nothing is found, a visual indicator should be given, but no actual |
|
154 |
redirect should occur. |
|
155 |
|
|
156 |
=item * |
|
157 |
|
|
158 |
Each search must check rights and must not present a backdoor into data that |
|
159 |
the user should not see. |
|
160 |
|
|
161 |
=back |
|
162 |
|
|
163 |
=head1 INTERFACE |
|
164 |
|
|
165 |
Plugins need to provide: |
|
166 |
|
|
167 |
- name |
|
168 |
- localized description for config |
|
169 |
- localized description for textfield |
|
170 |
- autocomplete callback |
|
171 |
- redirect callback |
|
172 |
|
|
173 |
the frontend will only generate urls of the forms: |
|
174 |
action=TopQuickSearch/autocomplete&module=<module>&term=<term> |
|
175 |
action=TopQuickSearch/search&module=<module>&term=<term> |
|
176 |
|
|
177 |
=head1 TODO |
|
178 |
|
|
179 |
- filter available searches with auth |
|
180 |
- toggling with cofiguration doesn't work yet |
|
181 |
|
|
182 |
=head1 BUGS |
|
183 |
|
|
184 |
None yet :) |
|
185 |
|
|
186 |
=head1 AUTHOR |
|
187 |
|
|
188 |
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt> |
|
189 |
|
|
190 |
=cut |
SL/Controller/TopQuickSearch/Assembly.pm | ||
---|---|---|
1 |
package SL::Controller::TopQuickSearch::Assembly; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent qw(Rose::Object); |
|
5 |
|
|
6 |
use SL::Locale::String qw(t8); |
|
7 |
use SL::DB::Part; |
|
8 |
use SL::Controller::Helper::GetModels; |
|
9 |
use SL::Controller::Base; |
|
10 |
|
|
11 |
use Rose::Object::MakeMethods::Generic ( |
|
12 |
'scalar --get_set_init' => [ qw(parts models part) ], |
|
13 |
); |
|
14 |
|
|
15 |
sub auth { 'part_service_assembly_edit' } |
|
16 |
|
|
17 |
sub name { 'assembly' } |
|
18 |
|
|
19 |
sub description_config { t8('Assemblies') } |
|
20 |
|
|
21 |
sub description_field { t8('Assemblies') } |
|
22 |
|
|
23 |
sub query_autocomplete { |
|
24 |
my ($self) = @_; |
|
25 |
|
|
26 |
my $objects = $self->models->get; |
|
27 |
|
|
28 |
[ |
|
29 |
map { |
|
30 |
value => $_->displayable_name, |
|
31 |
label => $_->displayable_name, |
|
32 |
id => $_->id, |
|
33 |
}, @$objects |
|
34 |
]; |
|
35 |
} |
|
36 |
|
|
37 |
sub select_autocomplete { |
|
38 |
redirect_to_part($::form->{id}); |
|
39 |
} |
|
40 |
|
|
41 |
sub do_search { |
|
42 |
my ($self) = @_; |
|
43 |
|
|
44 |
my $objects = $self->models->get; |
|
45 |
|
|
46 |
return !@$objects ? () |
|
47 |
: @$objects == 1 ? redirect_to_part($objects->[0]->id) |
|
48 |
: redirect_to_search($::form->{term}); |
|
49 |
} |
|
50 |
|
|
51 |
sub redirect_to_search { |
|
52 |
SL::Controller::Base->new->url_for( |
|
53 |
controller => 'ic.pl', |
|
54 |
action => 'generate_report', |
|
55 |
searchitems => 'assembly', |
|
56 |
all => $_[0], |
|
57 |
); |
|
58 |
} |
|
59 |
|
|
60 |
sub redirect_to_part { |
|
61 |
SL::Controller::Base->new->url_for( |
|
62 |
controller => 'ic.pl', |
|
63 |
action => 'edit', |
|
64 |
id => $_[0], |
|
65 |
); |
|
66 |
} |
|
67 |
|
|
68 |
sub init_models { |
|
69 |
my ($self) = @_; |
|
70 |
|
|
71 |
SL::Controller::Helper::GetModels->new( |
|
72 |
controller => $self, |
|
73 |
model => 'Part', |
|
74 |
source => { |
|
75 |
filter => { |
|
76 |
type => 'assembly', |
|
77 |
'all:substr:multi::ilike' => $::form->{term}, |
|
78 |
}, |
|
79 |
}, |
|
80 |
sorted => { |
|
81 |
_default => { |
|
82 |
by => 'partnumber', |
|
83 |
dir => 1, |
|
84 |
}, |
|
85 |
partnumber => t8('Partnumber'), |
|
86 |
}, |
|
87 |
paginated => { |
|
88 |
per_page => 10, |
|
89 |
}, |
|
90 |
) |
|
91 |
} |
|
92 |
|
|
93 |
1; |
SL/Controller/TopQuickSearch/Base.pm | ||
---|---|---|
1 |
package SL::Controller::TopQuickSearch::Base; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent qw(Rose::Object); |
|
5 |
|
|
6 |
sub auth { ... } |
|
7 |
|
|
8 |
sub name { ... } |
|
9 |
|
|
10 |
sub description_config { ... } |
|
11 |
|
|
12 |
sub description_field { ... } |
|
13 |
|
|
14 |
sub query_autocomplete { ... } |
|
15 |
|
|
16 |
sub select_autocomplete { ... } |
|
17 |
|
|
18 |
sub do_search { ... } |
|
19 |
|
|
20 |
1; |
|
21 |
|
|
22 |
__END__ |
|
23 |
|
|
24 |
=encoding utf-8 |
|
25 |
|
|
26 |
=head1 NAME |
|
27 |
|
|
28 |
SL::Controller::TopQuickSearch::Base - base interface class for quick search plugins |
|
29 |
|
|
30 |
=head1 DESCRIPTION |
|
31 |
|
|
32 |
see L<SL::Controller::TopQuickSearch> |
|
33 |
|
|
34 |
=head1 INTERFACE |
|
35 |
|
|
36 |
An implementation must provide these functions. |
|
37 |
|
|
38 |
=over 4 |
|
39 |
|
|
40 |
=item C<auth> |
|
41 |
|
|
42 |
Must return a string used for access checks. Empty string or undef will mean |
|
43 |
unrestricted access. |
|
44 |
|
|
45 |
=item C<name> |
|
46 |
|
|
47 |
Internal name, must be plain ASCII. |
|
48 |
|
|
49 |
=item C<description_config> |
|
50 |
|
|
51 |
Localized name used in the configuration (NYI) |
|
52 |
|
|
53 |
=item C<description_field> |
|
54 |
|
|
55 |
Localized name used in the search field as hint. Should fit into an input of |
|
56 |
length 20. |
|
57 |
|
|
58 |
=item C<query_autocomplete> |
|
59 |
|
|
60 |
Needs to take C<term> from C<$::form> and must return an arrayref of JSON |
|
61 |
serializable matches fit for jquery autocomplete. |
|
62 |
|
|
63 |
=item C<select_autocomplete> |
|
64 |
|
|
65 |
Needs to take C<id> from C<$::form> and must return a redirect string to be |
|
66 |
used with C<SL::Controller::Base::redirect_to> pointing to a representation of |
|
67 |
the selected object. |
|
68 |
|
|
69 |
=item C<do_search> |
|
70 |
|
|
71 |
Needs to take C<term> from C<$::form> and must return a redirect string to be |
|
72 |
used with C<SL::Controller::Base::redirect_to> pointing to a representation of |
|
73 |
the search results. If the search will display only only one match, it should |
|
74 |
instead return the same result as if that object was selected directly using |
|
75 |
C<select_autocomplete>. |
|
76 |
|
|
77 |
=back |
|
78 |
|
|
79 |
=head1 BUGS |
|
80 |
|
|
81 |
None yet :) |
|
82 |
|
|
83 |
=head1 AUTHOR |
|
84 |
|
|
85 |
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt> |
|
86 |
|
|
87 |
=cut |
SL/Controller/TopQuickSearch/Contact.pm | ||
---|---|---|
1 |
package SL::Controller::TopQuickSearch::Contact; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent qw(SL::Controller::TopQuickSearch::Base); |
|
5 |
|
|
6 |
use SL::Controller::CustomerVendor; |
|
7 |
use SL::DB::Vendor; |
|
8 |
use SL::DBUtils qw(selectfirst_array_query); |
|
9 |
use SL::Locale::String qw(t8); |
|
10 |
|
|
11 |
sub auth { 'customer_vendor_edit' } |
|
12 |
|
|
13 |
sub name { 'contact' } |
|
14 |
|
|
15 |
sub description_config { t8('Contact') } |
|
16 |
|
|
17 |
sub description_field { t8('Contacts') } |
|
18 |
|
|
19 |
sub query_autocomplete { |
|
20 |
my ($self) = @_; |
|
21 |
|
|
22 |
my $result = SL::DB::Manager::Contact->get_all( |
|
23 |
query => [ |
|
24 |
or => [ |
|
25 |
cp_name => { ilike => "%$::form->{term}%" }, |
|
26 |
cp_givenname => { ilike => "%$::form->{term}%" }, |
|
27 |
cp_email => { ilike => "%$::form->{term}%" }, |
|
28 |
], |
|
29 |
cp_cv_id => [ \'SELECT id FROM customer UNION SELECT id FROM vendor' ], |
|
30 |
], |
|
31 |
limit => 10, |
|
32 |
sort_by => 'cp_name', |
|
33 |
); |
|
34 |
|
|
35 |
return [ |
|
36 |
map { |
|
37 |
value => $_->full_name, |
|
38 |
label => $_->full_name, |
|
39 |
id => $_->cp_id, |
|
40 |
}, @$result |
|
41 |
]; |
|
42 |
} |
|
43 |
|
|
44 |
sub select_autocomplete { |
|
45 |
my ($self) = @_; |
|
46 |
|
|
47 |
my $contact = SL::DB::Manager::Contact->find_by(cp_id => $::form->{id}); |
|
48 |
|
|
49 |
SL::Controller::CustomerVendor->new->url_for(action => 'edit', id => $contact->cp_cv_id, db => db_for_contact($contact)); |
|
50 |
} |
|
51 |
|
|
52 |
sub do_search { |
|
53 |
my ($self) = @_; |
|
54 |
|
|
55 |
my $results = $self->query_autocomplete; |
|
56 |
|
|
57 |
if (@$results != 1) { |
|
58 |
return SL::Controller::CustomerVendor->new->url_for( |
|
59 |
controller => 'ct.pl', |
|
60 |
action => 'list_contacts', |
|
61 |
'filter.status' => 'active', |
|
62 |
search_term => $::form->{term}, |
|
63 |
); |
|
64 |
} else { |
|
65 |
$::form->{id} = $results->[0]{id}; |
|
66 |
return $self->select_autocomplete; |
|
67 |
} |
|
68 |
} |
|
69 |
|
|
70 |
|
|
71 |
sub db_for_contact { |
|
72 |
my ($contact) = @_; |
|
73 |
|
|
74 |
my ($customer, $vendor) = selectfirst_array_query($::form, $::form->get_standard_dbh, <<SQL, ($contact->cp_cv_id)x2); |
|
75 |
SELECT (SELECT COUNT(id) FROM customer WHERE id = ?), (SELECT COUNT(id) FROM vendor WHERE id = ?); |
|
76 |
SQL |
|
77 |
|
|
78 |
die 'Contact is orphaned, cannot link to it' if !$customer && !$vendor; |
|
79 |
|
|
80 |
$customer ? 'customer' : 'vendor'; |
|
81 |
} |
|
82 |
|
|
83 |
# TODO: multi search |
|
84 |
|
|
85 |
1; |
SL/Controller/TopQuickSearch/GLTransaction.pm | ||
---|---|---|
1 |
package SL::Controller::TopQuickSearch::GLTransaction; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent qw(Rose::Object); |
|
5 |
|
|
6 |
use SL::DB::GLTransaction; |
|
7 |
use SL::DB::Invoice; |
|
8 |
use SL::DB::PurchaseInvoice; |
|
9 |
use SL::DB::AccTransaction; |
|
10 |
use SL::Locale::String qw(t8); |
|
11 |
use List::Util qw(sum); |
|
12 |
|
|
13 |
sub auth { 'general_ledger' } |
|
14 |
|
|
15 |
sub name { 'gl_transction' } |
|
16 |
|
|
17 |
sub description_config { t8('GL search') } |
|
18 |
|
|
19 |
sub description_field { t8('GL search') } |
|
20 |
|
|
21 |
sub query_autocomplete { |
|
22 |
my ($self, %params) = @_; |
|
23 |
|
|
24 |
my $limit = $::form->{limit} || 40; # max number of results per type (AR/AP/GL) |
|
25 |
my $term = $::form->{term} || ''; |
|
26 |
|
|
27 |
my $descriptionquery = { ilike => '%' . $term . '%' }; |
|
28 |
my $referencequery = { ilike => '%' . $term . '%' }; |
|
29 |
my $apinvnumberquery = { ilike => '%' . $term . '%' }; |
|
30 |
my $namequery = { ilike => '%' . $term . '%' }; |
|
31 |
my $arinvnumberquery = { ilike => '%' . $term }; |
|
32 |
# ar match is more restrictive. Left fuzzy beginning so it also matches "Storno zu $INVNUMBER" |
|
33 |
# and numbers like 000123 if you only enter 123. |
|
34 |
# When used in quicksearch short numbers like 1 or 11 won't match because of the |
|
35 |
# ajax autocomplete minlimit of 3 characters |
|
36 |
|
|
37 |
my (@glfilter, @arfilter, @apfilter); |
|
38 |
|
|
39 |
push( @glfilter, (or => [ description => $descriptionquery, reference => $referencequery ] ) ); |
|
40 |
push( @arfilter, (or => [ invnumber => $arinvnumberquery, name => $namequery ] ) ); |
|
41 |
push( @apfilter, (or => [ invnumber => $apinvnumberquery, name => $namequery ] ) ); |
|
42 |
|
|
43 |
my $gls = SL::DB::Manager::GLTransaction->get_all( query => [ @glfilter ], limit => $limit, sort_by => 'transdate DESC'); |
|
44 |
my $ars = SL::DB::Manager::Invoice->get_all( query => [ @arfilter ], limit => $limit, sort_by => 'transdate DESC', with_objects => [ 'customer' ]); |
|
45 |
my $aps = SL::DB::Manager::PurchaseInvoice->get_all(query => [ @apfilter ], limit => $limit, sort_by => 'transdate DESC', with_objects => [ 'vendor' ]); |
|
46 |
|
|
47 |
# use the sum of all credit amounts as the "amount" of the gl transaction |
|
48 |
foreach my $gl ( @$gls ) { |
|
49 |
$gl->{'amount'} = sum map { $_->amount if $_->amount > 0 } @{$gl->transactions}; |
|
50 |
}; |
|
51 |
|
|
52 |
my $gldata = [ |
|
53 |
map( |
|
54 |
{ |
|
55 |
{ |
|
56 |
transdate => DateTime->from_object(object => $_->transdate)->ymd(), |
|
57 |
label => $_->abbreviation. ": " . $_->description . " " . $_->reference . " " . $::form->format_amount(\%::myconfig, $_->{'amount'},2). " (" . $_->transdate->to_lxoffice . ")" , |
|
58 |
id => 'gl.pl?action=edit&id=' . $_->id, |
|
59 |
} |
|
60 |
} |
|
61 |
@{$gls} |
|
62 |
), |
|
63 |
]; |
|
64 |
|
|
65 |
my $ardata = [ |
|
66 |
map( |
|
67 |
{ |
|
68 |
{ |
|
69 |
transdate => DateTime->from_object(object => $_->transdate)->ymd(), |
|
70 |
label => $_->abbreviation . ": " . $_->invnumber . " " . $_->customer->name . " " . $::form->format_amount(\%::myconfig, $_->amount,2) . " (" . $_->transdate->to_lxoffice . ")" , |
|
71 |
id => ($_->invoice ? "is" : "ar" ) . '.pl?action=edit&id=' . $_->id, |
|
72 |
} |
|
73 |
} |
|
74 |
@{$ars} |
|
75 |
), |
|
76 |
]; |
|
77 |
|
|
78 |
my $apdata = [ |
|
79 |
map( |
|
80 |
{ |
|
81 |
{ |
|
82 |
transdate => DateTime->from_object(object => $_->transdate)->ymd(), |
|
83 |
label => $_->abbreviation . ": " . $_->invnumber . " " . $_->vendor->name . " " . $::form->format_amount(\%::myconfig, $_->amount,2) . " (" . $_->transdate->to_lxoffice . ")" , |
|
84 |
value => "", |
|
85 |
id => ($_->invoice ? "ir" : "ap" ) . '.pl?action=edit&id=' . $_->id, |
|
86 |
} |
|
87 |
} |
|
88 |
@{$aps} |
|
89 |
), |
|
90 |
]; |
|
91 |
|
|
92 |
my $data; |
|
93 |
push(@{$data},@{$gldata}); |
|
94 |
push(@{$data},@{$ardata}); |
|
95 |
push(@{$data},@{$apdata}); |
|
96 |
|
|
97 |
@$data = reverse sort { $a->{'transdate'} cmp $b->{'transdate'} } @$data; |
|
98 |
|
|
99 |
$data; |
|
100 |
} |
|
101 |
|
|
102 |
sub select_autocomplete { |
|
103 |
$::form->{id} |
|
104 |
} |
|
105 |
|
|
106 |
sub do_search { |
|
107 |
my ($self) = @_; |
|
108 |
|
|
109 |
my $results = $self->query_autocomplete; |
|
110 |
|
|
111 |
return @$results == 1 |
|
112 |
? $results->[0]{id} |
|
113 |
: undef; |
|
114 |
} |
|
115 |
|
|
116 |
# TODO: result overview page |
|
117 |
|
|
118 |
1; |
SL/Layout/Top.pm | ||
---|---|---|
3 | 3 |
use strict; |
4 | 4 |
use parent qw(SL::Layout::Base); |
5 | 5 |
|
6 |
use SL::Controller::TopQuickSearch; |
|
7 |
|
|
6 | 8 |
sub pre_content { |
7 | 9 |
my ($self) = @_; |
8 | 10 |
|
... | ... | |
10 | 12 |
now => DateTime->now_local, |
11 | 13 |
is_fastcgi => $::dispatcher ? scalar($::dispatcher->interface_type =~ /fastcgi/i) : 0, |
12 | 14 |
is_links => scalar($ENV{HTTP_USER_AGENT} =~ /links/i), |
15 |
quick_search => SL::Controller::TopQuickSearch->new, |
|
13 | 16 |
); |
14 | 17 |
} |
15 | 18 |
|
... | ... | |
18 | 21 |
} |
19 | 22 |
|
20 | 23 |
sub javascripts { |
21 |
('jquery-ui.js', 'quicksearch_input.js') x!! $::auth->assert('customer_vendor_edit|part_service_assembly_edit', 1),
|
|
22 |
('jquery-ui.js', 'glquicksearch.js') x!! $::auth->assert('general_ledger', 1)
|
|
24 |
'jquery-ui.js',
|
|
25 |
'kivi.QuickSearch.js',
|
|
23 | 26 |
} |
24 | 27 |
|
25 | 28 |
1; |
js/kivi.QuickSearch.js | ||
---|---|---|
1 |
namespace('kivi', function(k){ |
|
2 |
k.QuickSearch = function($real, options) { |
|
3 |
if ($real.data("quick_search")) |
|
4 |
return $real.data("quick_search"); |
|
5 |
|
|
6 |
var KEY = { |
|
7 |
ENTER: 13, |
|
8 |
}; |
|
9 |
var o = $.extend({ |
|
10 |
limit: 20, |
|
11 |
delay: 50, |
|
12 |
}, options); |
|
13 |
|
|
14 |
function send_query(action, term, id, success) { |
|
15 |
var data = { module: o.module }; |
|
16 |
if (term != undefined) data.term = term; |
|
17 |
if (id != undefined) data.id = id; |
|
18 |
$.ajax($.extend(o, { |
|
19 |
url: 'controller.pl?action=TopQuickSearch/' + action, |
|
20 |
dataType: "json", |
|
21 |
data: data, |
|
22 |
success: success |
|
23 |
})); |
|
24 |
} |
|
25 |
|
|
26 |
function submit_search(term) { |
|
27 |
send_query('do_search', term, undefined, kivi.eval_json_result); |
|
28 |
} |
|
29 |
|
|
30 |
$real.autocomplete({ |
|
31 |
source: function(req, rsp) { |
|
32 |
send_query('query_autocomplete', req.term, undefined, function (data){ rsp(data) }); |
|
33 |
}, |
|
34 |
select: function(event, ui) { |
|
35 |
send_query('select_autocomplete', undefined, ui.item.id, kivi.eval_json_result); |
|
36 |
}, |
|
37 |
}); |
|
38 |
$real.keydown(function(event){ |
|
39 |
if (event.which == KEY.ENTER) { |
|
40 |
if ($real.val() != '') { |
|
41 |
submit_search($real.val()); |
|
42 |
} |
|
43 |
} |
|
44 |
}); |
|
45 |
|
|
46 |
$real.data('quick_search', {}); |
|
47 |
} |
|
48 |
}); |
|
49 |
|
|
50 |
$(function(){ |
|
51 |
$('input[id^=top-quick-search]').each(function(_,e){ |
|
52 |
kivi.QuickSearch($(e), { module: $(e).attr('module') }) |
|
53 |
}) |
|
54 |
}) |
templates/webpages/menu/header.html | ||
---|---|---|
5 | 5 |
<span class="frame-header-element frame-header-left"> |
6 | 6 |
[<a href="controller.pl?action=LoginScreen/user_login" target="_blank" title="[% 'Open a further kivitendo window or tab' | $T8 %]">[% 'New window/tab' | $T8 %]</a>] |
7 | 7 |
[<a href="JavaScript:top.print();" title="[% 'Hardcopy' | $T8 %]">[% 'Print' | $T8 %]</a>] |
8 |
[%- IF AUTH.assert('part_service_assembly_edit', 1) %] |
|
9 |
[<input name="frame_header_parts_search" id="frame_header_parts_search" placeholder="[% 'Search parts' | $T8 %]" size="14">] |
|
10 |
[%- END %] |
|
11 |
[%- IF AUTH.assert('customer_vendor_edit|customer_vendor_edit_all', 1) %] |
|
12 |
[<input name="frame_header_contact_search" id="frame_header_contact_search" placeholder="[% 'Search contacts' | $T8 %]" size="14">] |
|
13 |
[%- END %] |
|
14 |
[%- IF AUTH.assert('general_ledger', 1) %] |
|
15 |
[<input id="glquicksearch" name="glquicksearch" type="text" class="ui-widget" placeholder="[% 'GL search' | $T8 %]" maxlength="20">] |
|
8 |
|
|
9 |
[%- FOREACH search = quick_search.active_modules %] |
|
10 |
[<input id="top-quick-search-[% search.name %]" module="[% search.name %]" placeholder="[% search.description_field %]" maxlength="20">] |
|
16 | 11 |
[%- END %] |
12 |
|
|
17 | 13 |
</span> |
18 | 14 |
[%- END %] |
19 | 15 |
<span class="frame-header-element frame-header-right"> |
Auch abrufbar als: Unified diff
TopQuickSearch: erste version