xref: /AOO41X/main/solenv/bin/modules/installer/windows/feature.pm (revision d62abd1a66f4ba03ba5ec1e8fa54ad53c228495c)
1#**************************************************************
2#
3#  Licensed to the Apache Software Foundation (ASF) under one
4#  or more contributor license agreements.  See the NOTICE file
5#  distributed with this work for additional information
6#  regarding copyright ownership.  The ASF licenses this file
7#  to you under the Apache License, Version 2.0 (the
8#  "License"); you may not use this file except in compliance
9#  with the License.  You may obtain a copy of the License at
10#
11#    http://www.apache.org/licenses/LICENSE-2.0
12#
13#  Unless required by applicable law or agreed to in writing,
14#  software distributed under the License is distributed on an
15#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16#  KIND, either express or implied.  See the License for the
17#  specific language governing permissions and limitations
18#  under the License.
19#
20#**************************************************************
21
22
23
24package installer::windows::feature;
25
26use installer::existence;
27use installer::exiter;
28use installer::files;
29use installer::globals;
30use installer::sorter;
31use installer::worker;
32use installer::windows::idtglobal;
33use installer::windows::language;
34
35##############################################################
36# Returning the gid for a feature.
37# Attention: Maximum length
38##############################################################
39
40sub get_feature_gid
41{
42    my ($onefeature) = @_;
43
44    my $gid = "";
45
46    if ( $onefeature->{'gid'} ) { $gid = $onefeature->{'gid'}; }
47
48    # Attention: Maximum feature length is 38!
49    installer::windows::idtglobal::shorten_feature_gid(\$gid);
50
51    return $gid
52}
53
54##############################################################
55# Returning the gid of the parent.
56# Attention: Maximum length
57##############################################################
58
59sub get_feature_parent
60{
61    my ($onefeature) = @_;
62
63    my $parentgid = "";
64
65    if ( $onefeature->{'ParentID'} ) { $parentgid = $onefeature->{'ParentID'}; }
66
67    # The modules, hanging directly below the root, have to be root modules.
68    # Only then it is possible to make the "real" root module invisible by
69    # setting the display to "0".
70
71    if ( $parentgid eq $installer::globals::rootmodulegid ) { $parentgid = ""; }
72
73    # Attention: Maximum feature length is 38!
74    installer::windows::idtglobal::shorten_feature_gid(\$parentgid);
75
76    return $parentgid
77}
78
79##############################################################
80# Returning the display for a feature.
81# 0: Feature is not shown
82# odd: subfeatures are shown
83# even:  subfeatures are not shown
84##############################################################
85
86sub get_feature_display
87{
88    my ($onefeature) = @_;
89
90    my $display;
91    my $parentid = "";
92
93    if ( $onefeature->{'ParentID'} ) { $parentid = $onefeature->{'ParentID'}; }
94
95    if ( $parentid eq "" )
96    {
97        $display = "0";                                 # root module is not visible
98    }
99    elsif ( $onefeature->{'gid'} eq "gid_Module_Prg")   # program module shows subfeatures
100    {
101        $display = "1";                                 # root module shows subfeatures
102    }
103    else
104    {
105        $display = "2";                                 # all other modules do not show subfeatures
106    }
107
108    # special case: Feature has flag "HIDDEN_ROOT" -> $display is 0
109    my $styles = "";
110    if ( $onefeature->{'Styles'} ) { $styles = $onefeature->{'Styles'}; }
111    if ( $styles =~ /\bHIDDEN_ROOT\b/ ) { $display = "0"; }
112
113    # Special handling for language modules. Only visible in multilingual installation set
114    if (( $styles =~ /\bSHOW_MULTILINGUAL_ONLY\b/ ) && ( ! $installer::globals::ismultilingual )) { $display = "0"; }
115
116    # Special handling for c05office. No program module visible.
117    if (( $onefeature->{'gid'} eq "gid_Module_Prg" ) && ( $installer::globals::product =~ /c05office/i )) { $display = "0"; }
118
119    # making all feature invisible in Language packs!
120    if ( $installer::globals::languagepack ) { $display = "0"; }
121
122    return $display
123}
124
125##############################################################
126# Returning the level for a feature.
127##############################################################
128
129sub get_feature_level
130{
131    my ($onefeature) = @_;
132
133    my $level = "20";   # the default
134
135    my $localdefault = "";
136
137    if ( $onefeature->{'Default'} ) { $localdefault = $onefeature->{'Default'}; }
138
139    if ( $localdefault eq "NO" )    # explicitely set Default = "NO"
140    {
141        $level = "200";             # deselected in default installation, base is 100
142        if ( $installer::globals::patch ) { $level = "20"; }
143    }
144
145    # special handling for Java and Ada
146    if ( $onefeature->{'Name'} )
147    {
148        if ( $onefeature->{'Name'} =~ /java/i ) { $level = $level + 40; }
149    }
150
151    # if FeatureLevel is defined in scp, this will be used
152
153    if ( $onefeature->{'FeatureLevel'} ) { $level = $onefeature->{'FeatureLevel'}; }
154
155    return $level
156}
157
158##############################################################
159# Returning the directory for a feature.
160##############################################################
161
162sub get_feature_directory
163{
164    my ($onefeature) = @_;
165
166    my $directory;
167
168    $directory = "INSTALLLOCATION";
169
170    return $directory
171}
172
173##############################################################
174# Returning the directory for a feature.
175##############################################################
176
177sub get_feature_attributes
178{
179    my ($onefeature) = @_;
180
181    my $attributes;
182
183    # No advertising of features and no leaving on network.
184    # Feature without parent must not have the "2"
185
186    my $parentgid = "";
187    if ( $onefeature->{'ParentID'} ) { $parentgid = $onefeature->{'ParentID'}; }
188
189    if (( $parentgid eq "" ) || ( $parentgid eq $installer::globals::rootmodulegid )) { $attributes = "8"; }
190    else { $attributes = "10"; }
191
192    return $attributes
193}
194
195#################################################################################
196# Replacing one variable in one files
197#################################################################################
198
199sub replace_one_variable
200{
201    my ($translationfile, $variable, $searchstring) = @_;
202
203    for ( my $i = 0; $i <= $#{$translationfile}; $i++ )
204    {
205        ${$translationfile}[$i] =~ s/\%$searchstring/$variable/g;
206    }
207}
208
209#################################################################################
210# Replacing the variables in the feature names and descriptions
211#################################################################################
212
213sub replace_variables
214{
215    my ($translationfile, $variableshashref) = @_;
216
217    foreach $key (keys %{$variableshashref})
218    {
219        my $value = $variableshashref->{$key};
220        replace_one_variable($translationfile, $value, $key);
221    }
222}
223
224#################################################################################
225# Collecting the feature recursively.
226#################################################################################
227
228sub collect_modules_recursive
229{
230    my ($modulesref, $parentid, $feature, $directaccess, $directgid, $directparent, $directsortkey, $sorted) = @_;
231
232    my @allchildren = ();
233    my $childrenexist = 0;
234
235    # Collecting children from Module $parentid
236
237    my $modulegid;
238    foreach $modulegid ( keys %{$directparent})
239    {
240        if ( $directparent->{$modulegid} eq $parentid )
241        {
242            my %childhash = ( "gid" => "$modulegid", "Sortkey" => "$directsortkey->{$modulegid}");
243            push(@allchildren, \%childhash);
244            $childrenexist = 1;
245        }
246    }
247
248    # Sorting children
249
250    if ( $childrenexist )
251    {
252        # Sort children
253        installer::sorter::sort_array_of_hashes_numerically(\@allchildren, "Sortkey");
254
255        # Adding children to new array
256        my $childhashref;
257        foreach $childhashref ( @allchildren )
258        {
259            my $gid = $childhashref->{'gid'};
260
261            # Saving all lines, that have this 'gid'
262
263            my $unique;
264            foreach $unique ( keys %{$directgid} )
265            {
266                if ( $directgid->{$unique} eq $gid )
267                {
268                    push(@{$feature}, ${$modulesref}[$directaccess->{$unique}]);
269                    if ( $sorted->{$unique} == 1 ) { installer::exiter::exit_program("ERROR: Sorting feature failed! \"$unique\" already sorted.", "sort_feature"); }
270                    $sorted->{$unique} = 1;
271                }
272            }
273
274            collect_modules_recursive($modulesref, $gid, $feature, $directaccess, $directgid, $directparent, $directsortkey, $sorted);
275        }
276    }
277}
278
279#################################################################################
280# Sorting the feature in specified order. Evaluated is the key "Sortkey", that
281# is set in scp2 projects.
282# The display order of modules in Windows Installer is dependent from the order
283# in the idt file. Therefore the order of the modules array has to be adapted
284# to the Sortkey order, before the idt file is created.
285#################################################################################
286
287sub sort_feature
288{
289    my ($modulesref) = @_;
290
291    my @feature = ();
292
293    my %directaccess = ();
294    my %directparent = ();
295    my %directgid = ();
296    my %directsortkey = ();
297    my %sorted = ();
298
299    for ( my $i = 0; $i <= $#{$modulesref}; $i++ )
300    {
301        my $onefeature = ${$modulesref}[$i];
302
303        my $uniquekey = $onefeature->{'uniquekey'};
304        my $modulegid = $onefeature->{'gid'};
305
306        $directaccess{$uniquekey} = $i;
307
308        $directgid{$uniquekey} = $onefeature->{'gid'};
309
310        # ParentID and Sortkey are not saved for the 'uniquekey', but only for the 'gid'
311
312        if ( $onefeature->{'ParentID'} ) { $directparent{$modulegid} = $onefeature->{'ParentID'}; }
313        else { $directparent{$modulegid} = ""; }
314
315        if ( $onefeature->{'Sortkey'} ) { $directsortkey{$modulegid} = $onefeature->{'Sortkey'}; }
316        else { $directsortkey{$modulegid} = "9999"; }
317
318        # Bookkeeping:
319        $sorted{$uniquekey} = 0;
320    }
321
322    # Searching all feature recursively, beginning with ParentID = ""
323    my $parentid = "";
324    collect_modules_recursive($modulesref, $parentid, \@feature, \%directaccess, \%directgid, \%directparent, \%directsortkey, \%sorted);
325
326    # Bookkeeping
327    my $modulekey;
328    foreach $modulekey ( keys %sorted )
329    {
330        if ( $sorted{$modulekey} == 0 )
331        {
332            $installer::logger::Lang->printf(
333                "Warning: Module \"%s\" could not be sorted. Added to the end of the module array.\n",
334                $modulekey);
335            push(@feature, ${$modulesref}[$directaccess{$modulekey}]);
336        }
337    }
338
339    return \@feature;
340}
341
342#################################################################################
343# Adding a unique key to the modules array. The gid is not unique for
344# multilingual modules. Only the combination from gid and specific language
345# is unique. Uniqueness is required for sorting mechanism.
346#################################################################################
347
348sub add_uniquekey
349{
350    my ( $modulesref ) = @_;
351
352    for ( my $i = 0; $i <= $#{$modulesref}; $i++ )
353    {
354        my $uniquekey = ${$modulesref}[$i]->{'gid'};
355        if ( ${$modulesref}[$i]->{'specificlanguage'} ) { $uniquekey = $uniquekey . "_" . ${$modulesref}[$i]->{'specificlanguage'}; }
356        ${$modulesref}[$i]->{'uniquekey'} = $uniquekey;
357    }
358}
359
360#################################################################################
361# Creating the file Feature.idt dynamically
362# Content:
363# Feature Feature_Parent Title Description Display Level Directory_ Attributes
364#################################################################################
365
366sub prepare_feature_table ($$$)
367{
368    my ($modules, $language, $variables) = @_;
369
370    my $features = [];
371
372    foreach my $onefeature (@$modules)
373    {
374        # Java and Ada only, if the correct settings are set
375        my $styles = $onefeature->{'Styles'};
376        $styles = "" unless defined $styles;
377        if (( $styles =~ /\bJAVAMODULE\b/ ) && ( ! ($variables->{'JAVAPRODUCT'} ))) { next; }
378
379        # Controlling the language!
380        # Only language independent feature or feature with the correct language will be included into the table
381
382        next if $onefeature->{'ismultilingual'} && ($onefeature->{'specificlanguage'} ne $language);
383
384        my $feature_gid =get_feature_gid($onefeature);
385
386        my $feature = {
387            'Feature' => $feature_gid,
388            'Feature_Parent' => get_feature_parent($onefeature),
389            'Title' => $onefeature->{'Name'},
390            'Description' => $onefeature->{'Description'},
391            'Display' => get_feature_display($onefeature),
392            'Level' => get_feature_level($onefeature),
393            'Directory_' => get_feature_directory($onefeature),
394            'Attributes' => get_feature_attributes($onefeature)
395        };
396        push @$features, $feature;
397
398        # collecting all feature in global feature collector (so that properties can be set in property table)
399        $installer::globals::featurecollector{$feature_gid} = 1;
400
401        # collecting all language feature in feature collector for check of language selection
402        if (( $styles =~ /\bSHOW_MULTILINGUAL_ONLY\b/ ) && $onefeature->{'ParentID'} ne $installer::globals::rootmodulegid)
403        {
404            $installer::globals::multilingual_only_modules{$feature_gid} = 1;
405        }
406
407        # collecting all application feature in global feature collector for check of application selection
408        if ( $styles =~ /\bAPPLICATIONMODULE\b/ )
409        {
410            $installer::globals::application_modules{$feature_gid} = 1;
411        }
412    }
413
414    return $features;
415}
416
417
418
419
420=head add_missing_features($features)
421
422    When we are building a release, then there may be features missing
423    that where present in the source release.  As missing features
424    would prevent patches from being created, we add the missing
425    features.
426
427    The returned feature hash is either identical to the given
428    $features or is a copy with the missing features added.
429
430=cut
431
432sub add_missing_features ($)
433{
434    my ($features) = @_;
435
436    return $features if ! $installer::globals::is_release;
437
438    # Aquire the feature list of the source release.
439    my $source_feature_table = $installer::globals::source_msi->GetTable("Feature");
440    my $feature_column_index = $source_feature_table->GetColumnIndex("Feature");
441
442    # Prepare fast lookup of the target features.
443    my %target_feature_map = map {$_->{'Feature'} => $_} @$features;
444
445    # Find missing features.
446    my @missing_features = ();
447    foreach my $source_feature_row (@{$source_feature_table->GetAllRows()})
448    {
449        my $feature_gid = $source_feature_row->GetValue($feature_column_index);
450        if ( ! defined $target_feature_map{$feature_gid})
451        {
452            push @missing_features, $source_feature_row;
453        }
454    }
455
456    # Return when there are no missing features.
457    return $features if scalar @missing_features==0;
458
459    # Process the missing features.
460    my $extended_features = [@$features];
461    foreach my $missing_feature_row (@missing_features)
462    {
463        my %feature = map
464            {$_ => $missing_feature_row->GetValue($_)}
465            ('Feature', 'Feature_Parent', 'Title', 'Description', 'Display', 'Level', 'Directory_', 'Attributes');
466        push @$extended_features, \%feature;
467
468        $installer::logger::Lang->printf("added missing feature %s\n", $feature->{'Feature'});
469    }
470    return $extended_features;
471}
472
473
474
475
476sub create_feature_table ($$$)
477{
478    my ($basedir, $language, $features) = @_;
479
480    my @feature_table = ();
481    installer::windows::idtglobal::write_idt_header(\@feature_table, "feature");
482
483    foreach my $feature (@$features)
484    {
485        my $line = join("\t",
486            $feature->{'Feature'},
487            $feature->{'Feature_Parent'},
488            $feature->{'Title'},
489            $feature->{'Description'},
490            $feature->{'Display'},
491            $feature->{'Level'},
492            $feature->{'Directory_'},
493            $feature->{'Attributes'}) . "\n";
494
495        push(@feature_table, $line);
496    }
497
498    my $filename = $basedir . $installer::globals::separator . "Feature.idt" . "." . $language;
499    installer::files::save_file($filename ,\@feature_table);
500    $installer::logger::Lang->printf("Created idt file: %s\n", $filename);
501}
502
5031;
504