LP#1786987: Locale-less org tree cache object becomes stale
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / Utils / Configure.pm
1 package OpenILS::Utils::Configure;
2
3 use strict;
4 use warnings;
5 use File::Spec;
6 use OpenILS::Utils::Cronscript;
7
8 sub fieldmapper {
9     my $web = shift;
10
11     my $core = OpenILS::Utils::Cronscript->new({nolockfile => 1});
12     $core->bootstrap;
13
14     my $output;
15     my $map = $Fieldmapper::fieldmap;
16
17     # if a true value is provided, we generate the web (light) version of the fieldmapper
18     if(!$web) { $web = ""; }
19
20     my @web_core = qw/ 
21         aou au perm_ex ex aout 
22         mvr ccs ahr aua ac actscecm cbreb acpl 
23         cbrebi acpn acp acnn acn bren asc asce 
24         clfm cifm citm cam ahtc
25         asv asva asvr asvq 
26         circ ccs ahn bre mrd
27         crcd crmf crrf mbts aoc aus 
28         mous mobts mb ancc cnct cnal
29         /;
30
31     my @reports = qw/ perm_ex ex ao aou aout /;
32
33
34     $output = "var _c = {};\n";
35
36     for my $object (keys %$map) {
37
38         my $hint = $map->{$object}->{hint};
39
40         if($web eq "web_core") {
41             next unless (grep { $_ eq $hint } @web_core );
42         }
43
44         if($web eq "reports") {
45             next unless (grep { $_ eq $hint } @web_core );
46         }
47
48
49         my $short_name = $map->{$object}->{hint};
50
51         my @fields;
52         for my $field (keys %{$map->{$object}->{fields}}) {
53             my $position = $map->{$object}->{fields}->{$field}->{position};
54             $fields[$position] = $field;
55         }
56
57         $output .= "_c[\"$short_name\"] = [";
58         for my $f (@fields) { 
59             next unless $f;
60             if( $f ne "isnew" and $f ne "ischanged" and $f ne "isdeleted" ) {
61                 $output .= "\"$f\","; 
62             }
63         }
64         $output .= "];\n";
65
66
67     }
68
69     $output .= "var fmclasses = _c;\n";
70     return $output;
71 }
72
73 sub org_tree_js {
74     # ------------------------------------------------------------
75     # turns the orgTree and orgTypes into js files
76     # ------------------------------------------------------------
77     use OpenSRF::Utils::Cache;
78
79     my $path = shift;
80     my $filename = shift;
81
82     my $core = OpenILS::Utils::Cronscript->new({nolockfile => 1});
83     $core->bootstrap;
84
85     # must be loaded after the IDL is parsed
86     require OpenILS::Utils::CStoreEditor;
87
88     # Get our list of locales
89     my $locales = get_locales();
90
91     # Remove the no-locale copy
92     my $cache = OpenSRF::Utils::Cache->new;
93     $cache->delete_cache("orgtree.");
94
95     foreach my $locale (@$locales) {
96         warn "removing OrgTree from the cache for locale " . $locale->code . "...\n";
97         $cache->delete_cache("orgtree.".$locale->code);
98
99         # fetch the org_unit's and org_unit_type's
100         my $e = OpenILS::Utils::CStoreEditor->new;
101         $e->init();
102         $e->session->session_locale($locale->code) if ($locale->code);
103
104         my $types = $e->retrieve_all_actor_org_unit_type;
105         my $tree = $e->request(
106             'open-ils.cstore.direct.actor.org_unit.search.atomic',
107             {id => {"!=" => undef}},
108             {order_by => {aou => 'name'}, no_i18n => $locale->code ? 0 : 1 }
109         );
110         my $dir = File::Spec->catdir($path, $locale->code);
111         if (!-d $dir) {
112             mkdir($dir);
113         }
114         build_tree_js($types, $tree, File::Spec->catfile($dir, $filename));
115     }
116 }
117
118 sub val {
119     my $v = shift;
120     return 'null' unless defined $v;
121
122     # required for JS code this is checking truthness 
123     # without using isTrue() (1/0 vs. t/f)
124     return 1 if $v eq 't';
125     return 0 if $v eq 'f';
126
127     $v =~ s/([\x{0080}-\x{fffd}])/sprintf('\u%04x',ord($1))/sgoe;
128
129     return "\"$v\"";
130 }
131
132 sub build_tree_js {
133     my $types = shift;
134     my $tree = shift;
135     my $outfile = shift;
136
137     my $pile = "var _l = [";
138
139     my @array;
140     for my $o (@$tree) {
141         my ($i,$t,$p,$n,$v,$s) = ($o->id,$o->ou_type,$o->parent_ou,val($o->name),val($o->opac_visible),val($o->shortname));
142         $p ||= 'null';
143         push @array, "[$i,$t,$p,$n,$v,$s]";
144     }
145
146     $pile .= join ',', @array;
147     $pile .= "]; /* Org Units */ \n";
148
149     $pile .= 'var globalOrgTypes = [';
150     for my $t (@$types) {
151         my ($u,$v,$d,$i,$n,$o,$p) = (val($t->can_have_users),val($t->can_have_vols),$t->depth,$t->id,val($t->name),val($t->opac_label),$t->parent);
152         $p ||= 'null';
153         $pile .= "new aout([null,$u,$v,$d,$i,$n,$o,$p]), ";
154     }
155     $pile =~ s/, $//; # remove trailing comma
156     $pile .= ']; /* OU Types */';
157
158     open(OUTFH, '>', $outfile) or die "Could not open $outfile : $!";
159     print OUTFH "$pile\n";
160     close(OUTFH);
161 }
162
163 sub org_tree_html_options {
164     # for each supported locale, turn the orgTree and orgTypes into a static HTML option list
165
166     use Unicode::Normalize;
167     use Data::Dumper;
168
169     my $path = shift;
170     my $filename = shift; 
171
172     my @types;
173
174     my $core = OpenILS::Utils::Cronscript->new({nolockfile => 1});
175     $core->bootstrap;
176
177     my $locales = get_locales();
178
179     foreach my $locale (@$locales) {
180         my $ses = OpenSRF::AppSession->create("open-ils.actor");
181         $ses->session_locale($locale->code);
182         my $tree = $ses->request("open-ils.actor.org_tree.retrieve")->gather(1);
183
184         my $aout = $ses->request("open-ils.actor.org_types.retrieve")->gather(1);
185         foreach my $type (@$aout) {
186             $types[int($type->id)] = $type;
187         }
188         my $dir = File::Spec->catdir($path, $locale->code);
189         if (!-d $dir) {
190             mkdir($dir) or die "Could not create output directory: $dir $!\n";
191         }
192
193         my @org_tree_html;
194         print_org_tree_html($tree, \@org_tree_html, \@types);
195         $ses->disconnect();
196
197         open(OUTFH, '>', File::Spec->catfile($dir, $filename)) or die $!;
198         print OUTFH @org_tree_html;
199         close OUTFH;
200     }
201
202 }
203
204 sub print_org_tree_html {
205     my $node = shift;
206     my $org_tree_html = shift;
207     my $types = shift;
208
209     return unless ($node->opac_visible =~ /^[y1t]+/i);
210
211     my $depth = $types->[$node->ou_type]->depth;
212     my $sname = OpenILS::Application::AppUtils->entityize($node->shortname);
213     my $name = OpenILS::Application::AppUtils->entityize($node->name);
214     my $kids = $node->children;
215
216     push @$org_tree_html, "<option value='$sname'>" . '&#160;&#160;&#160;'x$depth . "$name</option>\n";
217     print_org_tree_html($_, $org_tree_html, $types) for (@$kids);
218 }
219
220 sub org_lasso {
221     # Renders a JavaScript version of the org unit search groups
222     
223     my $core = OpenILS::Utils::Cronscript->new({nolockfile => 1});
224     $core->bootstrap;
225
226     # must be loaded after the IDL is parsed
227     require OpenILS::Utils::CStoreEditor;
228
229     my $output;
230
231     # fetch the org_unit's and org_unit_type's
232     my $e = OpenILS::Utils::CStoreEditor->new;
233     $e->init();
234     my $lassos = $e->request(
235         'open-ils.cstore.direct.actor.org_lasso.search.atomic',
236         {id => {"!=" => undef}},
237         {order_by => {lasso => 'name'}}
238     );
239
240     # We need at least one defined search group; otherwise, just generate an empty array
241     if (scalar(@$lassos) > 0) {
242        $output =  
243             "var _lasso = [\n  new lasso(" .
244             join( "),\n  new lasso(", map { OpenSRF::Utils::JSON->perl2JSON( bless($_, 'ARRAY') ) } @$lassos ) .
245             ")\n]; /* Org Search Groups (Lassos) */ \n";
246     } else {
247         $output = <<HERE;
248 var _lasso = [
249 ]; /* Org Search Groups (Lassos) */
250 HERE
251     }
252
253     return $output;
254 }
255
256 sub locale_html_options {
257     # Turns supported locales into a static HTML option list
258     my $locales = get_locales();
259
260     my $output = "<select name='locale'>\n";
261     foreach my $locale (@$locales) {
262         my $code = OpenILS::Application::AppUtils->entityize($locale->code);
263         my $name = OpenILS::Application::AppUtils->entityize($locale->name);
264         $output .= "  <option value='$code'>$name</option>\n";
265     }
266     $output .= "</select>\n";
267
268     return $output;
269 }
270
271 sub facet_types {
272     # ------------------------------------------------------------
273     # turns the facet fields defined on config.metabib_field into JS
274     # ------------------------------------------------------------
275
276     my $path = shift;
277     my $filename = shift;
278     # Get our list of locales
279     my $locales = get_locales();
280
281     foreach my $locale (@$locales) {
282         warn "removing facet list from the cache for locale " . $locale->code . "...\n";
283         my $cache = OpenSRF::Utils::Cache->new;
284         $cache->delete_cache("facet_definition.".$locale->code);
285
286         # fetch the org_unit's and org_unit_type's
287         my $e = OpenILS::Utils::CStoreEditor->new;
288         $e->init();
289         $e->session->session_locale($locale->code) if ($locale->code);
290
291         my $types = $e->retrieve_all_actor_org_unit_type;
292         my $tree = $e->request(
293             'open-ils.cstore.direct.config.metabib_field.search.atomic',
294             {   facet_field     => 't' },
295             {   no_i18n         => $locale->code ? 0 : 1,
296                 flesh           => 1,
297                 flesh_fields    => { cmf => [ 'field_class' ] }
298             }
299         );
300         my $dir = File::Spec->catdir($path, $locale->code);
301         if (!-d $dir) {
302             mkdir($dir);
303         }
304         build_facet_type_js($tree, File::Spec->catfile($dir, $filename));
305     }
306 }
307
308 sub build_facet_type_js {
309     my $tree = shift;
310     my $outfile = shift;
311
312     my $pile = "var globalFacets = {";
313     my @array;
314     for my $o (@$tree) {
315         my %hash = (
316             id          => $o->id,
317             name        => val($o->name),
318             label       => val($o->label),
319             classname   => val($o->field_class->name),
320             classlabel  => val($o->field_class->label)
321         );
322
323         $pile .= $hash{id}.':{'.join(',', map { "$_:$hash{$_}" } keys %hash).'},';
324     }
325
326     $pile =~ s/,$//; # remove trailing comma
327     $pile .= "}; /* Facets */";
328
329     open(OUTFH, '>', $outfile) or die "Could not open $outfile : $!";
330     print OUTFH "$pile\n";
331     close(OUTFH);
332 }
333
334 sub org_tree_proximity {
335     # calculate the proximity of organizations in the organization tree
336
337     my $session = OpenILS::Utils::Cronscript->new({nolockfile => 1})->session('open-ils.storage');
338     my $result = $session->request("open-ils.storage.actor.org_unit.refresh_proximity");
339
340     if ($result) {
341         print "Successfully updated the organization proximity\n";
342     } else {
343         print "Failed to update the organization proximity\n";
344     }
345     $session->disconnect();
346 }
347
348 sub get_locales {
349     # Get our list of locales
350     my $session = OpenILS::Utils::Cronscript->new({nolockfile => 1})->session("open-ils.cstore");
351     my $locales = $session->request(
352         "open-ils.cstore.direct.config.i18n_locale.search.atomic",
353         {"code" => {"!=" => undef}},
354         {"order_by" => {"i18n_l" => "name"}}
355     )->gather();
356     $session->disconnect();
357
358     return $locales;
359 }
360
361 1;