1 package SL::DBUpgrade2::Base;
5 use parent qw(Rose::Object);
9 use English qw(-no_match_vars);
10 use File::Basename ();
13 use List::MoreUtils qw(uniq);
14 use SL::DBUtils qw(selectfirst_hashref_query);
17 use Rose::Object::MakeMethods::Generic (
18 scalar => [ qw(dbh myconfig) ],
26 my $file_name = delete $params{file_name};
28 if (!eval { require $file_name }) {
29 delete $INC{$file_name};
33 my $package = delete $params{tag};
34 $package =~ s/[^a-zA-Z0-9_]+/_/g;
35 $package = "SL::DBUpgrade2::${package}";
37 $package->new(%params)->run;
41 my ($self, $msg) = @_;
43 die $::locale->text("Database update error:") . "<br>$msg<br>" . $self->db_errstr('DBI');
47 my ($self, $query, %params) = @_;
49 my $dbh = $params{dbh} || $self->dbh;
51 return if $dbh->do($query, undef, @{ $params{bind} || [] });
53 $self->db_error($query) unless $params{may_fail};
60 my ($self, $handle) = @_;
62 # DBD::Pg before 2.16.1 doesn't set the UTF-8 flag for error
63 # messages even if the connection has UTF-8 enabled. Therefore we
64 # have to convert it to Perl's internal encoding ourselves. See
65 # https://rt.cpan.org/Public/Bug/Display.html?id=53854
67 my $error = $handle ? $handle->errstr : $self->dbh->errstr;
69 return $error if version->new("$DBD::Pg::VERSION")->numify >= version->new("2.16.1")->numify;
70 return Encode::decode('utf-8', $error);
74 my ($self, $wanted_coa) = @_;
76 my ($have_coa) = selectrow_query($::form, $self->dbh, q{ SELECT count(*) FROM defaults WHERE coa = ? }, $wanted_coa);
84 my $query = q{ SELECT count(*)
85 FROM ar, ap, gl, invoice, acc_trans, customer, vendor, parts
87 my ($empty) = selectrow_query($::form, $self->dbh, $query);
92 sub add_print_templates {
93 my ($self, $src_dir, @files) = @_;
95 $::lxdebug->message(LXDebug::DEBUG1(), "add_print_templates: src_dir $src_dir files " . join(' ', @files));
98 croak "File '${src_dir}/$_' does not exist" unless -f "${src_dir}/$_";
101 # can't use Rose or InstanceConf here because defaults might not be fully upgraded yet.
102 my $defaults = selectfirst_hashref_query($::form, $::form->get_standard_dbh, "SELECT * FROM defaults");
103 return 1 unless my $template_dir = $defaults->{template};
104 $::lxdebug->message(LXDebug::DEBUG1(), "add_print_templates: template_dir $template_dir");
106 foreach my $src_file (@files) {
107 my $dest_file = $template_dir . '/' . $src_file;
110 $::lxdebug->message(LXDebug::DEBUG1(), "add_print_templates: dest_file exists, skipping: ${dest_file}");
114 my $dest_dir = File::Basename::dirname($dest_file);
116 if ($dest_dir && !-d $dest_dir) {
117 File::Path::make_path($dest_dir) or die "Cannot create directory '${dest_dir}': $!";
120 File::Copy::copy($src_dir . '/' . $src_file, $dest_file) or die "Cannot copy '${src_dir}/${src_file}' to '${dest_file}': $!";
122 $::lxdebug->message(LXDebug::DEBUG1(), "add_print_templates: copied '${src_dir}/${src_file}' to '${dest_file}'");
128 sub drop_constraints {
129 my ($self, %params) = @_;
131 croak "Missing parameter 'table'" unless $params{table};
132 $params{type} ||= 'FOREIGN KEY';
133 $params{schema} ||= 'public';
135 my $constraints = $self->dbh->selectall_arrayref(<<SQL, undef, $params{type}, $params{schema}, $params{table});
136 SELECT constraint_name
137 FROM information_schema.table_constraints
138 WHERE (constraint_type = ?)
139 AND (table_schema = ?)
143 $self->db_query(qq|ALTER TABLE $params{schema}."$params{table}" DROP CONSTRAINT "${_}"|) for map { $_->[0] } @{ $constraints };
146 sub convert_column_to_html {
147 my ($self, $table, $column) = @_;
149 my $sth = $self->dbh->prepare(qq|UPDATE $table SET $column = ? WHERE id = ?|) || $self->dberror;
151 foreach my $row (selectall_hashref_query($::form, $self->dbh, qq|SELECT id, $column FROM $table WHERE $column IS NOT NULL|)) {
152 next if !$row->{$column} || (($row->{$column} =~ m{^<[a-z]+>}) && ($row->{$column} =~ m{</[a-z]+>$}));
154 my $new_content = "" . $::request->presenter->escape($row->{$column});
155 $new_content =~ s{\r}{}g;
156 $new_content =~ s{\n\n+}{</p><p>}g;
157 $new_content =~ s{\n}{<br />}g;
158 $new_content = "<p>${new_content}</p>" if $new_content;
160 $sth->execute($new_content, $row->{id}) if $new_content ne $row->{$column};
175 SL::DBUpgrade2::Base - Base class for Perl-based database upgrade files
179 Database scripts written in Perl must be derived from this class and
180 provide a method called C<run>.
182 The functions in this base class offer functionality for the upgrade
187 The following properties (which can be accessed with
188 C<$self-E<gt>property_name>) are available to the database upgrade
195 The database handle; an Instance of L<DBI>. It is connected, and a
196 transaction has been started right before the script (the method
197 L</run>)) was executed.
201 The stripped-down version of the C<%::myconfig> hash: this hash
202 reference only contains the database connection parameters applying to
203 the current database.
211 =item C<add_print_templates $source_dir, @files>
213 Adds (copies) new print templates to existing users. All existing
214 users in the authentication database are read. The listed C<@files>
215 are copied to each user's configured templates directory preserving
216 sub-directory structure (non-existing sub-directories will be
217 created). If a template with the same name exists it will be skipped.
219 The source file names must all be relative to the source directory
220 C<$source_dir>. This way only the desired sub-directories are created
221 in the users' template directories. Example:
223 $self->add_print_templates(
224 'templates/print/Standard',
225 qw(receipt.tex common.sty images/background.png)
228 Let's assume a user's template directory is
229 C<templates/big-money-inc>. The call above would trigger five actions:
233 =item 1. Create the directory C<templates/big-money-inc> if it doesn't
236 =item 2. Copy C<templates/print/Standard/receipt.tex> to
237 C<templates/big-money-inc/receipt.tex> if there's no such file in that
240 =item 3. Copy C<templates/print/Standard/common.sty> to
241 C<templates/big-money-inc/common.sty> if there's no such file in that
244 =item 4. Create the directory C<templates/big-money-inc/images> if it
247 =item 5. Copy C<templates/print/Standard/images/background.png> to
248 C<templates/big-money-inc/images/background.png> if there's no such
249 file in that directory.
253 =item C<check_coa $coa_name>
255 Returns trueish if the database uses the chart of accounts named
258 =item C<db_error $message>
260 Outputs an error message C<$message> to the user and aborts execution.
262 =item C<db_query $query, %params>
264 Executes an SQL query. The following parameters are supported:
270 What the method does if the query fails depends on this parameter. If
271 it is falsish (the default) then the method will simply die outputting
272 the error message via L</db_error>. If C<may_fail> is trueish then the
273 current transaction will be rolled back, a new one will be started.
277 An optional array reference containing bind parameter for the query.
281 The database handle to use. If undefined then C<$self-E<gt>dbh> will
286 =item C<db_errstr [$handle]>
288 Returns the last database from C<$handle> error message encoded in
289 Perl's internal encoding. The PostgreSQL DBD before 2.16.1 leaves the
290 UTF-8 flag off for error messages even if the C<pg_enable_utf8>
291 attribute is set. For older versions the error string is already
292 encoded correctly and is left unchanged.
294 C<$handle> is optional and can be one of three things:
298 =item 1. A database or statement handle. In that case
299 C<$handle-E<gt>errstr> is used.
301 =item 2. The string 'DBI'. In that case C<$DBI::errstr> is used.
303 =item 3. If it is undefined then C<$self-E<gt>dbh-E<gt>errstr> is
308 =item C<drop_constraints %params>
310 Drops all constraints of a type (e.g. foreign keys) on a table. One
311 parameter is mandatory: C<table>. Optional parameters include:
315 =item * C<schema> -- if missing defaults to C<public>
317 =item * C<type> -- if missing defaults to C<FOREIGN KEY>. Must be one of
318 the values contained in the C<information_schema.table_constraints>
319 view in the C<constraint_type> column.
323 =item C<execute_script>
325 Executes a named database upgrade script. This function is not
326 supposed to be called from an upgrade script. Instead, the upgrade
327 manager L<SL::DBUpgrade2> uses it in order to execute the actual
328 database upgrade scripts.
330 =item C<is_coa_empty>
332 Returns trueish if no transactions have been recorded in the table
337 This method is the entry point for the actual upgrade. Each upgrade
338 script must provide this method.
340 =item C<convert_column_to_html $table, $column>
342 Converts the content of a single column from text to HTML suitable for
343 use with the ckeditor.
353 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>