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