c3ad7f37abcc898b32630d244ba64230b82c0045
[kivitendo-erp.git] / 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 = (
15   'SL::Controller::TopQuickSearch::Article',
16   'SL::Controller::TopQuickSearch::Part',
17   'SL::Controller::TopQuickSearch::Service',
18   'SL::Controller::TopQuickSearch::Assembly',
19   'SL::Controller::TopQuickSearch::Assortment',
20   'SL::Controller::TopQuickSearch::Contact',
21   'SL::Controller::TopQuickSearch::SalesQuotation',
22   'SL::Controller::TopQuickSearch::SalesOrder',
23   'SL::Controller::TopQuickSearch::RequestForQuotation',
24   'SL::Controller::TopQuickSearch::PurchaseOrder',
25   'SL::Controller::TopQuickSearch::GLTransaction',
26   'SL::Controller::TopQuickSearch::Customer',
27   'SL::Controller::TopQuickSearch::Vendor',
28 );
29 my %modules_by_name;
30
31 sub action_query_autocomplete {
32   my ($self) = @_;
33
34   my $hashes = $self->module->query_autocomplete;
35
36   $self->render(\ SL::JSON::to_json($hashes), { layout => 0, type => 'json', process => 0 });
37 }
38
39 sub action_select_autocomplete {
40   my ($self) = @_;
41
42   my $redirect_url = $self->module->select_autocomplete;
43
44   $self->js->redirect_to($redirect_url)->render;
45 }
46
47 sub action_do_search {
48   my ($self) = @_;
49
50   my $redirect_url = $self->module->do_search;
51
52   if ($redirect_url) {
53     $self->js->redirect_to($redirect_url)
54   }
55
56   $self->js->render;
57 }
58
59 sub available_modules {
60   my ($self) = @_;
61
62   $self->require_modules;
63
64   map { $_->new } @available_modules;
65 }
66
67 sub enabled_modules {
68   my %enabled_names = map {
69     $_ => 1
70   } @{ $::instance_conf->get_quick_search_modules };
71
72   grep {
73     $enabled_names{$_->name}
74   } $_[0]->available_modules
75 }
76
77 sub active_modules {
78   grep {
79     $::auth->assert($_->auth, 1)
80   } $_[0]->enabled_modules
81 }
82
83 sub init_module {
84   my ($self) = @_;
85
86   $self->require_modules;
87
88   die 'Need module' unless $::form->{module};
89
90   die 'Unknown module ' . $::form->{module} unless my $class = $modules_by_name{$::form->{module}};
91
92   $::auth->assert($class->auth);
93
94   return $class->new;
95 }
96
97 sub init_js {
98   SL::ClientJS->new(controller => $_[0])
99 }
100
101 sub require_modules {
102   my ($self) = @_;
103
104   if (!$self->{__modules_required}) {
105     for my $class (@available_modules) {
106       eval "require $class" or die $@;
107       $modules_by_name{ $class->name } = $class;
108     }
109     $self->{__modules_required} = 1;
110   }
111 }
112
113 1;
114
115 __END__
116
117 =encoding utf-8
118
119 =head1 NAME
120
121 SL::Controller::TopQuickSearch - Framework for pluggable quicksearch fields in the layout top header.
122
123 =head1 SYNOPSIS
124
125   use SL::Controller::TopQuickSearch;
126   my $search = SL::Controller::TopQuickSearch->new;
127   $::request->layout->add_javascripts('kivi.QuickSearch.js');
128
129   # in template
130   [%- FOREACH module = search.enabled_modules %]
131   <input type='text' id='top-search-[% module.name %]'>
132   [%- END %]
133
134 =head1 DESCRIPTION
135
136 This controller provides abstraction for different search plugins, and ensures
137 that all follow a common useability scheme.
138
139 Modules should be configurable per user, but currently are not. Disabling
140 modules can be done by removing them from available_modules or in client_config.
141
142 =head1 BEHAVIOUR REQUIREMENTS
143
144 =over 4
145
146 =item *
147
148 A single text input field with the html5 placeholder containing a small
149 description of the target will be rendered from the plugin information.
150
151 =item *
152
153 On typing, the autocompletion must be enabled.
154
155 =item *
156
157 On C<Enter>, the search should redirect to an appropriate listing of matching
158 results.
159
160 If only one item matches the result, the plugin should instead redirect
161 directly to the matched item.
162
163 =item *
164
165 Search terms should accept the broadest possible matching, and if possible with
166 C<multi> parsing.
167
168 =item *
169
170 In case nothing is found, a visual indicator should be given, but no actual
171 redirect should occur.
172
173 =item *
174
175 Each search must check rights and must not present a backdoor into data that
176 the user should not see.
177
178 =item *
179
180 By design the search must not try to guess C<exact matches>.
181
182 =back
183
184 =head1 INTERFACE
185
186 The full interface is described in L<SL::Controller::TopQuickSeach::Base>
187
188 =head1 TODO
189
190   * user configuration
191
192 =head1 BUGS
193
194 None yet :)
195
196 =head1 AUTHOR
197
198 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
199
200 =cut