#################### main pod documentation begin ################### =head1 NAME Zymonic::Manifest::Core - Core manifest =head1 SYNOPSIS Describes what should be installed as part of the core. =head1 DESCRIPTION Describes what should be installed as part of the core. =head1 USAGE TODO =head1 BUGS NONE =head1 SUPPORT As in the license, Zymonic is provided without warranty or support unless purchased separately, however... If you email zymonic-support@zednax.com your issue will be noted and may receive a response. For security issues, please contact zymonic-security@zednax.com and someone will respond within 8 working hours. =head1 AUTHOR Alex Masidlover et al. CPAN ID: MODAUTHOR Zednax Limited alex.masidlover@zednax.com http://www.zednax.com =head1 COPYRIGHT This program is free software licensed under the... Zymonic Public License 1.0 The full text of the license can be found in the LICENSE file included with this module. Other licenses may be acceptable if including parts of Zymonic in larger projects, please contact Zednax for details. =head1 SEE ALSO Zymonic perl(1). =cut #################### main pod documentation end ################### package Zymonic::Manifest::core; use strict; BEGIN { use Exporter (); use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $Working_copy); $VERSION = '0.01'; $Working_copy = ''; @ISA = qw(Exporter); #Give a hoot don't pollute, do not export more than needed by default @EXPORT = qw(); @EXPORT_OK = qw(); %EXPORT_TAGS = (); } use Zymonic::Utils qw(get_array debug debug_exception); use base "Zymonic::Manifest"; use Cwd qw(realpath); use Zymonic; use Exception::Class ( 'Zymonic::Exception::Manifest' => { isa => 'Zymonic::Exception', fields => ['module'], description => 'Manifest related exceptions' }, ); #################### subroutine header begin #################### =head2 working_copy Usage : $manifest_obj->working_copy(); Purpose : Returns the working copy directory for the manifest Returns : see purpose Argument : none Throws : Zymonic::Exception::Manifest::NoWorkingCopy Comment : STUB See Also : =cut #################### subroutine header end #################### sub working_copy { return $Working_copy; } #################### subroutine header begin #################### =head2 get_contents Usage : @opts = $ManS->get_opts; Purpose : get contents for manifest Returns : options as an array of hash refs Argument : nothing Throws : nothing Comment : See Also : =cut #################### subroutine header end #################### #need to make a function in makefile.pl to record the files downloaded to be passed into a global variable in this file (se version setting) sub get_contents { my $self = shift; #loads all the folders as specified into a hash # the hash here needs to load all the files in which need to be installed my %Manifest = ( # bin - move contents to modules/[correct module]/bin cgi_bin => { destination => 'cgi-bin', files => ["$Working_copy/cgi-bin/"], user => $self->{app_server}->{options}->{apache_user}, group => $self->{app_server}->{options}->{apache_group}, post_install => [ sub { my $app_server = shift; $app_server->restart_webserver(); } ] }, ui_stylesheets => { destination => 'stylesheets', # this is a hacky workaround to install the Reports xsl into the root dir # xml has a mix of referenceing the files in the subdir and not # tickt 3651 raised to update the xml to be consistent files => [ "$Working_copy/stylesheets/", "$Working_copy/stylesheets/Reports/" ], user => $self->{app_server}->{options}->{apache_user}, group => $self->{app_server}->{options}->{apache_group}, post_install => [ sub { my $app_server = shift; $app_server->combine_stylesheet(); } ] }, images => { destination => 'images', files => ["$Working_copy/images/"], user => $self->{app_server}->{options}->{apache_user}, group => $self->{app_server}->{options}->{apache_group}, post_install => [] }, javascript => { destination => 'javascript', files => ["$Working_copy/javascript/"], user => $self->{app_server}->{options}->{apache_user}, group => $self->{app_server}->{options}->{apache_group}, post_install => [] }, perl_modules => { destination => '', files => ["$Working_copy/modules/Zymonic/"], post_install => [ sub { my $app_server = shift; my $zymonic_version = "Z" . $self->current_revision('with_type'); $app_server->install_perl_module( "Zymonic Core", "$Working_copy/modules/Zymonic/" ); $app_server->set_versions($zymonic_version); } ] }, xml => { destination => '', files => ["$Working_copy/xml/"], post_install => [ sub { my $app_server = shift; my @changed_files = $self->get_changed_files('xml'); $app_server->offer_config_build( "$Working_copy/xml/", @changed_files ); # this relies on changes to AppServer, however manifests are updated first # so wrap this in an eval so we don't get errors when this change appears # before the AppServer ones eval { # run build cache command on any systems which contain one of the changed # files, and one of those files has the flag to build caches map { $app_server->run_toolkit_command( 'System', 'build_object_caches', { system => $_ } ); } $app_server->get_systems_by_xml( 'build_cache="true"', \@changed_files ); 1; } or do { my $exception = $@; $app_server->write_log( "Error checking whether to rebuild caches: " . $self->ll_exception($exception) ); }; } ] }, ); return %Manifest; } #################### subroutine header begin #################### =head2 instructions Usage : my $revision = $self->instructions(); Purpose : Will be called before andafter each install to see if any special instructions are needed. Returns : a hashref containing: rollback => a hashref of rollback CODE refs to be run if/when the update is rolled back. update => a hashref of update CODE refs to be run if/when the update is done. Argument : app server object Throws : nothing Comment : The keys of the rollback/update hashref can be 'anything' but must be unique to each instruction within the return. See Also : =cut #################### subroutine header end #################### sub instructions { my $self = shift; my $app_server = shift; return { update => { 'SR72282.G' => sub { $app_server->write_log("Forcing autocreate to be re-run where SR: 72282 needs it."); foreach my $system ( $app_server->get_configured_systems ) { my $matched_xml = ` grep -l 'show_multiple_records="1"' /etc/zymonic/$system/*.xml`; if ($matched_xml) { my @files = split( /\n/, $matched_xml ); foreach my $file (@files) { eval { utime( undef, undef, realpath($file) ); } or do { $app_server->write_log( "Failed to 'touch' $file realpath : " . realpath($file) . " - " . $@ . ' ' . $! ); } } $app_server->do_offer_config_build($system); } } }, 'SR74175.D' => sub { $app_server->write_log("Forcing rebuild to set new AutoRunFilterAction option on relevant Filters."); foreach my $system ( $app_server->get_configured_systems ) { my $matched_xml = `grep -l 'do_offer_config_build($system); } } }, 'SR74883.D' => sub { $app_server->write_log("Forcing autocreate to be re-run where SR: 74883 needs it."); foreach my $system ( $app_server->get_configured_systems ) { my $matched_xml = ` grep -l 'AutoCreateFilter' /etc/zymonic/$system/*.xml`; if ($matched_xml) { my @files = split( /\n/, $matched_xml ); foreach my $file (@files) { utime( undef, undef, $file ); } } # offer config build of all systems to update the field ref details $app_server->do_offer_config_build($system); } }, 'SR6705.B' => sub { $app_server->write_log("Forcing rebuild to set cannot_close on all system blocks."); foreach my $system ( $app_server->get_configured_systems ) { # update all files which have a block definitions my $matched_xml = `grep -l 'do_offer_config_build($system); } } }, 'SR82115.A' => sub { $app_server->write_log("Forcing rebuild to repopulate field ref details with new truncate_value flag."); map { $app_server->do_offer_config_build($_); } $app_server->get_configured_systems(); }, 'SR67514.A' => sub { $app_server->write_log( "Adding message about manual running of build_object_caches when this SR if first updated. " . "Will be automatically done by Installer in future updates." ); # can't use get_systems_by_xml here, see comments in get_contents, 'xml' entry # so use the old style so this message shows always # future instructions can just use get_systems_by_xml foreach my $system ( $app_server->get_configured_systems ) { my $matched_xml = `grep -l 'build_cache="true"' /etc/zymonic/$system/*.xml`; if ($matched_xml) { $app_server->post_install_instruction( "Build caches for $system: zymonic_toolkit.pl System build_object_caches --system $system", 200 ); } } }, 'SR67154.C' => sub { $app_server->write_log("Forcing full config build as this update remmoved ZymonicCoreDefinitions.xml"); map { $app_server->post_install_instruction( "Full Config build $_", 200 ); } $app_server->get_configured_systems(); }, 'SR10876.F' => sub { $app_server->write_log( "Old default for system_email_from_domain SystemOption " . "was localhost, should now default to '', correcting instances of " . "localhost to '' as leaving it as localhost causes issues with the " . "current smtp server" ); foreach my $system ( $app_server->get_configured_systems() ) { eval { $app_server->write_log("Updating system_email_from_domain for $system"); my $config = Zymonic::Config->new( system_name => $system, config_dir => '/etc/zymonic', ip_address => '127.0.0.1', protocol => 'http' ); my $db = $config->{DB}; $db->run_statement( { string => "UPDATE zz_system_options SET option_value = ? " . "WHERE option_name = ? AND option_value = ? AND category = ? " . " AND sub_category = ? AND deleted IS NULL AND " . "autocreated IS NULL", params => [ '', 'system_email_from_domain', 'localhost', 'Communications', 'Email' ] } ); 1; } or do { my $exception = $@; $app_server->write_log( "Error encountered when removing old default for system_email_from_domain" . "\n" . $exception ); }; } }, '94313.D' => sub { $app_server->write_log("Resecuring tables that added new flag to autocreate Relationship Permissions"); # all systems since changes were in core, hardcoded list of tables, avoids expensive lookups my @tables_to_resecure = ( 'zz_document_types', 'zz_system_options', 'zz_label_io_table', 'zz_label_usage', 'zz_labels', 'zz_lang_codes', 'zz_user_record', 'zz_groups', 'zz_user_groups', 'zz_roles', 'zz_rpts', 'zz_role_rpts', 'zz_rpt_ft' ); my $tables_list = join( ",", @tables_to_resecure ); # running the actual resecure ran into issues with the toolkit and config objects # also it causes errors running for systems which might no longer be in use # so instead we just prompt the user to run the resecure # this also ensures zymobuild is quick foreach my $system ( $app_server->get_configured_systems() ) { $app_server->post_install_instruction( "Run the following command: " . "zymonic_toolkit.pl Security resecure --system $system --zname \"$tables_list\"", 200 ); } }, 'SR94313.G' => sub { $app_server->write_log("Cleaning up potential duplicate role permissions created by this SR"); my $db; foreach my $system ( $app_server->get_configured_systems() ) { eval { $app_server->write_log("Checking for duplicate role permissions in $system"); my $config = Zymonic::Config->new( system_name => $system, config_dir => '/etc/zymonic', ip_address => '127.0.0.1', protocol => 'http' ); $db = $config->{DB}; my @tables = qw(zz_role_permissions zz_rpt_ft); foreach my $table (@tables) { my $duplicate_role_permissions = $db->run_query( { string => "SELECT role, role_permission FROM $table " . "GROUP BY role, role_permission HAVING count(*) > ?", params => [1] } ); if ( $duplicate_role_permissions->[0] ) { $app_server->write_log( "Found " . ( scalar @{$duplicate_role_permissions} ) . " duplicate role permissions in $system - $table" ); # identify duplicates $db->run_statement( { string => "CREATE TEMPORARY TABLE SR94313a AS " . "SELECT role, role_permission FROM $table " . "GROUP BY role, role_permission HAVING count(*) > ?", params => [1] } ); # identify the ones to keep, smallest id $db->run_statement( { string => "CREATE TEMPORARY TABLE SR94313b AS " . "SELECT MIN(id) AS id FROM $table " . "JOIN SR94313a USING (role, role_permission) " . "GROUP BY role, role_permission", params => [] } ); # setup table of ids to delete $db->run_statement( { string => "CREATE TEMPORARY TABLE SR94313c AS " . "SELECT id FROM $table JOIN SR94313a USING (role, role_permission)", params => [] } ); # clear down the duplicates that we aren't keeping $db->run_statement( { string => "DELETE FROM $table " . "WHERE id IN (SELECT id FROM SR94313c) " . "AND id NOT IN (SELECT id FROM SR94313b)", params => [] } ); $db->run_statement( { string => "DROP TEMPORARY TABLE IF EXISTS SR94313a", params => [] } ); $db->run_statement( { string => "DROP TEMPORARY TABLE IF EXISTS SR94313b", params => [] } ); $db->run_statement( { string => "DROP TEMPORARY TABLE IF EXISTS SR94313c", params => [] } ); $app_server->write_log("Cleared duplicate role permissions in $system - $table"); } else { $app_server->write_log("No duplicate role permissions in $system - $table"); } } 1; } or do { my $exception = $@; $app_server->write_log( "Error encountered when clearing duplicate role permissions\n" . $exception ); }; # make sure to try and clean up any temporary tables after error handling, so they don't hang around if ($db) { eval { $db->run_statement( { string => "DROP TEMPORARY TABLE IF EXISTS SR94313a", params => [] } ); $db->run_statement( { string => "DROP TEMPORARY TABLE IF EXISTS SR94313b", params => [] } ); $db->run_statement( { string => "DROP TEMPORARY TABLE IF EXISTS SR94313c", params => [] } ); 1; } or do { my $exception = $@; $app_server->write_log( "Error encountered when cleaning up temporary tables\n" . $exception ); }; } } }, 'SR94313A.A' => sub { $app_server->write_log("Cleaning up superuser role permissions within incorrect parent process ids"); my $db; foreach my $system ( $app_server->get_configured_systems() ) { eval { $app_server->write_log( "Checking for superuser role permissions with incorrect parent process ids in $system"); my $config = Zymonic::Config->new( system_name => $system, config_dir => '/etc/zymonic', ip_address => '127.0.0.1', protocol => 'http' ); $db = $config->{DB}; my $su_role_pid = $db->run_query( { string => "SELECT parent_process_id FROM zz_roles WHERE role = ? ", params => ['superuser'] } )->[0]->{parent_process_id}; my @tables = qw(zz_role_permissions zz_rpt_ft); foreach my $table (@tables) { my $incorrect_pid_role_permissions = $db->run_query( { string => "SELECT role, role_permission FROM $table " . "WHERE role = ? AND parent_process_id != ?", params => [ 'superuser', $su_role_pid ] } ); if ( $incorrect_pid_role_permissions->[0] ) { $app_server->write_log( "Found " . ( scalar @{$incorrect_pid_role_permissions} ) . " superuser permissions with incorrect process ids in $system - $table" ); # update them to the correct value $db->run_statement( { string => "UPDATE $table SET parent_process_id = ? " . "WHERE role = ? AND parent_process_id != ?", params => [ 'superuser', $su_role_pid ] } ); $app_server->write_log( "Updated superuser permissions with correct process ids in $system - $table"); } else { $app_server->write_log( "No superuser permissions with incorrect process ids in $system - $table"); } } 1; } or do { my $exception = $@; $app_server->write_log( "Error encountered when checking for incorrect parent process ids\n" . $exception ); }; } }, 'SR95152.A' => sub { $app_server->write_log( "Forcing rebuild to repopulate field ref details with changes to can_save flag."); map { $app_server->do_offer_config_build($_); } $app_server->get_configured_systems(); }, 'SR10765.A' => sub { $app_server->write_log("Forcing revision history tables to be rebuilt for newly added field."); foreach my $system ( $app_server->get_systems_by_xml('enable_revision_history') ) { my $matched_xml = `grep -l 'enable_revision_history' /etc/zymonic/$system/*.xml`; if ($matched_xml) { my @files = split( /\n/, $matched_xml ); foreach my $file (@files) { utime( undef, undef, $file ); } } # offer config build of all systems to update the rh table $app_server->do_offer_config_build($system); } }, 'SR72593.A' => sub { $app_server->write_log( "Forcing rebuild as an earlier commit in this SR led to" . " auto CSV/HTML reports on filters displaying duplicate entries in the FilterActions menu." . " Offending code has been removed but the old definitions remain unless we rebuild." ); foreach my $system ( $app_server->get_configured_systems ) { my $matched_xml = `grep -l 'do_offer_config_build($system); } } }, 'SR97729.A' => sub { $app_server->post_install_instruction( "Do a full config build of all systems, with do_db_column_changes set, " . "to ensure all DB updates are applied.", 200 ); }, 'SR100938.C' => sub { $app_server->write_log("Checking for revision history tables in xml."); foreach my $system ( $app_server->get_configured_systems ) { my $matched_xml = ` grep -l 'enable_revision_history' /etc/zymonic/$system/*.xml`; if ($matched_xml) { my @files = split( /\n/, $matched_xml ); foreach my $file (@files) { $app_server->write_log("Attempting to touch file $file for system $system"); eval { utime( undef, undef, realpath($file) ); } or do { $app_server->write_log( "Failed to 'touch' $file realpath : " . realpath($file) . " - " . $@ . ' ' . $! ); } } $app_server->do_offer_config_build($system); } } }, 'SR103597' => sub { $app_server->post_install_instruction( "Create new logging directory if not already present: /var/log/zymonic_transitions", 1 ); }, 'SR100332.A' => sub { $app_server->post_install_instruction( "Prior to any config builds, running the following commands to reset some options:\n" . join( "\n", map { "\tcp /etc/zymonic/$_/ZZPersist.conf /tmp/ZZPersist_$_.conf\n" . "\tsudo grep -v CreateMaterializedView /tmp/ZZPersist_$_.conf > /etc/zymonic/$_/ZZPersist.conf" } $app_server->get_configured_systems() ), 1 ); $app_server->post_install_instruction( "Each config build should be done with the full flag to apply the above changes", 2 ); }, 'SR103482.A' => sub { my $db; foreach my $system ( $app_server->get_configured_systems() ) { eval { $app_server->write_log("Dropping primary key on lang codes"); my $config = Zymonic::Config->new( system_name => $system, config_dir => '/etc/zymonic', ip_address => '127.0.0.1', protocol => 'http' ); $db = $config->{DB}; $db->run_statement( { string => "ALTER TABLE zz_lang_codes DROP PRIMARY KEY;", params => [], } ); }; } }, 'SR103641' => sub { $app_server->post_install_instruction( "Do a full config build of all systems to ensure all system definition changes are applied.", 1 ); }, 'SR103699' => sub { $app_server->post_install_instruction( "Do a full config build of all systems to ensure new file access options are setup.", 1 ); $app_server->post_install_instruction( "After full config build, run the following command on each system to setup file access linkes:\n" . "\tzymonic_toolkit.pl Data refresh_file_access_tokens --system SYSTEM --user USER --password PASSWORD", 999 ); }, # e.g. abc123 => sub { $app_server->post_install_instruction("Do something special after updating!", 200); }, }, rollback => { # e.g. abc123 => sub { $app_server->post_install_instruction("Do something special after rollback!", 200); }, } }; } #make 1;