From 3a644a272388a07da8019d64d2f3c1e519a254c0 Mon Sep 17 00:00:00 2001 From: Peter Staab Date: Sat, 24 Feb 2024 07:22:01 -0500 Subject: [PATCH 1/3] Move database and external program settings This moves database and external program settings to webwork2.mojolicious.yml, moves code from database.conf.dist to WeBWorK::DB::Utils and load these settings into the CourseEnvironment. --- conf/README.md | 4 +- conf/database.conf.dist | 340 ----------------------------- conf/defaults.config | 45 +--- conf/site.conf.dist | 160 +------------- conf/webwork2.mojolicious.dist.yml | 87 ++++++++ lib/WeBWorK/CourseEnvironment.pm | 56 +++++ lib/WeBWorK/DB/Utils.pm | 272 +++++++++++++++++++++++ 7 files changed, 420 insertions(+), 544 deletions(-) delete mode 100644 conf/database.conf.dist diff --git a/conf/README.md b/conf/README.md index 4ef53a321d..aeaeebfb4e 100644 --- a/conf/README.md +++ b/conf/README.md @@ -16,8 +16,6 @@ Basic webwork2 configuration files. - `localOverrides.conf.dist` should be copied to `localOverrides.conf`. `localOverrides.conf` will be read after the `defaults.config` file is processed and will overwrite configurations in `defaults.config`. Use this file to make changes to the settings in `defaults.config`. -- `database.conf.dist` contains database configuration parameters. It is included by `defaults.config`. This file - should not be copied or modified unless you really know what you are doing. Configuration extension files. @@ -30,7 +28,7 @@ Configuration extension files. Server configuration files. - `webwork2.mojolicious.dist.yml` contains the webwork2 Mojolicious app configuration settings. Copy this file to - `webwork2.mojolicious.yml` if you need to change those settings. You usually will need to do this. + `webwork2.mojolicious.yml` if you need to change those settings. You usually will need to do this. This file contains server settings, database settings and paths to external programs. - `webwork2.dist.service` is a systemd configuration file for linux systems that serves the webwork2 app via the Mojolicious hypnotoad server. If you need to change it, then copy it to `webwork2.service`. - `webwork2-job-queue.dist.service` is a systemd configuration file for linux systems that runs the webwork2 job queue diff --git a/conf/database.conf.dist b/conf/database.conf.dist deleted file mode 100644 index 3b13b4e393..0000000000 --- a/conf/database.conf.dist +++ /dev/null @@ -1,340 +0,0 @@ -#!perl -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2023 The WeBWorK Project, https://github.com/openwebwork -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -=head1 NAME - -database.conf - define standard database layouts - -=head1 SYNOPSIS - -In defaults.config: - - include "conf/database.conf"; - *dbLayout = $dbLayouts{layoutName}; - -=head1 DESCRIPTION - -This file contains definitions for the commonly-used database layouts. Database -layouts consist of all the information necessary to describe how to access data -used by WeBWorK. For more information on the format of a database layout, -consult the documentation for the WeBWorK::DB module. - -A database layout is selected from the list of possible layouts by adding a -line like the one below to the F or F file. - - $dbLayoutName = "layoutName"; - *dbLayout = $dbLayouts{$dbLayoutName}; - -=head2 THE SQL_SINGLE DATABASE LAYOUT - -The C layout is similar to the C layout, except that it uses a -single database for all courses. This is accomplished by prefixing each table -name with the name of the course. The names and passwords of these accounts are -given as parameters to each table in the layout. - - username the username to use when connecting to the database - password the password to use when connecting to the database - -Be default, username is "webworkRead" and password is "". It is not recommended -that you use only a non-empty password to secure database access. Most RDBMSs -allow IP-based authorization as well. As the system administrator, IT IS YOUR -RESPONSIBILITY TO SECURE DATABASE ACCESS. - -Don't confuse the account information above with the accounts of the users of a -course. This is a system-wide account which allow WeBWorK to talk to the -database server. - -Other parameters that can be given are as follows: - - tableOverride an alternate name to use when referring to the table (used - when a table name is a reserved word) - debug if true, SQL statements are printed before being executed - -=cut - -# params common to all tables - -my %sqlParams = ( - username => $database_username, - password => $database_password, - debug => $database_debug, - # kinda hacky, but needed for table dumping - mysql_path => $externalPrograms{mysql}, - mysqldump_path => $externalPrograms{mysqldump}, -); - -if ($ce->{database_driver} =~ /^mysql$/i) { - # The extra UTF8 connection setting is ONLY needed for older DBD:mysql driver - # and forbidden by the newer DBD::MariaDB driver - if ($ENABLE_UTF8MB4) { - $sqlParams{mysql_enable_utf8mb4} = 1; # Full 4-bit UTF-8 - } else { - $sqlParams{mysql_enable_utf8} = 1; # Only the partial 3-bit mySQL UTF-8 - } -} - -%dbLayouts = (); # layouts are added to this hash below - -$dbLayouts{sql_single} = { - locations => { - record => "WeBWorK::DB::Record::Locations", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, non_native => 1 }, - }, - location_addresses => { - record => "WeBWorK::DB::Record::LocationAddresses", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, non_native => 1 }, - }, - depths => { - record => "WeBWorK::DB::Record::Depths", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - params => { %sqlParams, non_native => 1 }, - }, - password => { - record => "WeBWorK::DB::Record::Password", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_password" }, - }, - permission => { - record => "WeBWorK::DB::Record::PermissionLevel", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_permission" }, - }, - key => { - record => "WeBWorK::DB::Record::Key", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_key" }, - }, - user => { - record => "WeBWorK::DB::Record::User", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_user" }, - }, - set => { - record => "WeBWorK::DB::Record::Set", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_set" }, - }, - set_user => { - record => "WeBWorK::DB::Record::UserSet", - schema => "WeBWorK::DB::Schema::NewSQL::NonVersioned", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_set_user" }, - }, - set_merged => { - record => "WeBWorK::DB::Record::UserSet", - schema => "WeBWorK::DB::Schema::NewSQL::Merge", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - depend => [qw/set_user set/], - params => { - %sqlParams, - non_native => 1, - merge => [qw/set_user set/], - }, - }, - set_version => { - record => "WeBWorK::DB::Record::SetVersion", - schema => "WeBWorK::DB::Schema::NewSQL::Versioned", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - params => { - %sqlParams, - non_native => 1, - tableOverride => "${courseName}_set_user", - - }, - }, - set_version_merged => { - record => "WeBWorK::DB::Record::SetVersion", - schema => "WeBWorK::DB::Schema::NewSQL::Merge", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - depend => [qw/set_version set_user set/], - params => { - %sqlParams, - non_native => 1, - merge => [qw/set_version set_user set/], - }, - }, - set_locations => { - record => "WeBWorK::DB::Record::SetLocations", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_set_locations" }, - }, - set_locations_user => { - record => "WeBWorK::DB::Record::UserSetLocations", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_set_locations_user" }, - }, - problem => { - record => "WeBWorK::DB::Record::Problem", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_problem" }, - }, - problem_user => { - record => "WeBWorK::DB::Record::UserProblem", - schema => "WeBWorK::DB::Schema::NewSQL::NonVersioned", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_problem_user" }, - }, - problem_merged => { - record => "WeBWorK::DB::Record::UserProblem", - schema => "WeBWorK::DB::Schema::NewSQL::Merge", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - depend => [qw/problem_user problem/], - params => { - %sqlParams, - non_native => 1, - merge => [qw/problem_user problem/], - }, - }, - problem_version => { - record => "WeBWorK::DB::Record::ProblemVersion", - schema => "WeBWorK::DB::Schema::NewSQL::Versioned", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { - %sqlParams, - non_native => 1, - tableOverride => "${courseName}_problem_user", - }, - }, - problem_version_merged => { - record => "WeBWorK::DB::Record::ProblemVersion", - schema => "WeBWorK::DB::Schema::NewSQL::Merge", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - depend => [qw/problem_version problem_user problem/], - params => { - %sqlParams, - non_native => 1, - merge => [qw/problem_version problem_user problem/], - }, - }, - setting => { - record => "WeBWorK::DB::Record::Setting", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_setting" }, - }, - achievement => { - record => "WeBWorK::DB::Record::Achievement", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_achievement" }, - }, - past_answer => { - record => "WeBWorK::DB::Record::PastAnswer", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_past_answer" }, - }, - - achievement_user => { - record => "WeBWorK::DB::Record::UserAchievement", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_achievement_user" }, - }, - global_user_achievement => { - record => "WeBWorK::DB::Record::GlobalUserAchievement", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $database_dsn, - engine => $database_storage_engine, - character_set => $database_character_set, - params => { %sqlParams, tableOverride => "${courseName}_global_user_achievement" }, - }, -}; - -# include ("conf/database.conf"); # uncomment to provide local overrides - -1; diff --git a/conf/defaults.config b/conf/defaults.config index 6936fc6a04..4952dfa893 100644 --- a/conf/defaults.config +++ b/conf/defaults.config @@ -585,37 +585,6 @@ $default_status = "Enrolled"; }, ); -################################################################################ -# Database options -################################################################################ - -# Database schemas are defined in the file conf/database.conf and stored in the -# hash %dbLayouts. The standard schema is called "sql_single"; - -include( "./conf/database.conf.dist"); # always include database.conf.dist - - # in the rare case where you want local overrides - # you can place include("conf/database.conf") in - # the database.conf.dist file -# this change is meant to help alleviate the common mistake of forgetting to update the -# database.conf file when changing WW versions. - -# Select the default database layout. This can be overridden in the course.conf -# file of a particular course. The only database layout supported in WW 2.1.4 -# and up is "sql_single". -$dbLayoutName = "sql_single"; - -# This sets the symbol "dbLayout" as an alias for the selected database layout. -*dbLayout = $dbLayouts{$dbLayoutName}; - -# This sets the max course id length. It might need to be changed depending -# on what database tables are present. Mysql allows a max table length of 64 -# characters. With the ${course_id}_global_user_achievement table that means -# the max ${course_id} is exactly 40 characters. - -$maxCourseIdLength = 40; - -# Reference: https://dev.mysql.com/doc/refman/8.0/en/identifier-length.html ################################################################################ # Problem library options @@ -636,13 +605,7 @@ $contribLibrary{root} = "/opt/webwork/libraries/webwork-open-problem-libra $problemLibrary{version} = "2.5"; ########################################################### -# Problem Library SQL database connection information -$problemLibrary_db = { - dbsource => $database_dsn, - user => $database_username, - passwd => $database_password, - storage_engine => 'MYISAM', -}; +# The database settings for the OPL is now set in CourseEnvironment. $problemLibrary{tree} = 'library-directory-tree.json'; @@ -1122,10 +1085,8 @@ $pg{displayModeOptions}{images} = { # If dbsource is set to a nonempty value, then this database connection information will be used to store dvipng # depths. It is assumed that the 'depths' table exists in the database. - dvipng_depth_db => { - dbsource => $database_dsn, - user => $database_username, - passwd => $database_password, + # These are set in the CourseEnvironment. + dvipng_depth_db => {} }, }; diff --git a/conf/site.conf.dist b/conf/site.conf.dist index a5cd8f23a9..b1e2725c02 100644 --- a/conf/site.conf.dist +++ b/conf/site.conf.dist @@ -93,165 +93,7 @@ $admin_course_id = 'admin'; # status. #$new_courses_hidden_status = 'hidden'; -# password strings (or any other string allowing special characters) should be specified inside single quotes -# otherwise a string such as "someone@nowhere" will interpolate the contents of the array @nowhere -- which is probably -# empty, but still not what you want. Similar things happen with % and $ -################################################################################ -# Paths to external programs -################################################################################ - -# These applications are often found in /bin, but sometimes in /usr/bin -# or even in /opt/local/bin. -# You can use "which tar" for example to find out where the "tar" program is located - -#################################################### -# system utilities -#################################################### -$externalPrograms{tar} = "/bin/tar"; -$externalPrograms{git} = "/usr/bin/git"; - - -#################################################### -# equation rendering/hardcopy utiltiies -#################################################### -$externalPrograms{latex} = "/usr/bin/latex --no-shell-escape"; - -$externalPrograms{pdflatex} = "/usr/bin/pdflatex --no-shell-escape"; -# Note that --no-shell-escape is important for security reasons. -# Consider using xelatex instead of pdflatex for multilingual use, and -# use polyglossia and fontspec packages (which require xelatex or lualatex). -#$externalPrograms{pdflatex} = "/usr/bin/xelatex --no-shell-escape"; - -$externalPrograms{dvipng} = "/usr/bin/dvipng"; - -# In order to use imagemagick convert you need to change the rights for PDF files from -# "none" to "read" in the policy file /etc/ImageMagick-6/policy.xml. This has possible -# security implications for the server. -$externalPrograms{convert} = "/usr/bin/convert"; - -$externalPrograms{dvisvgm} = "/usr/bin/dvisvgm"; -$externalPrograms{pdf2svg} = "/usr/bin/pdf2svg"; - -#################################################### -# NetPBM - basic image manipulation utilities -# Most sites only need to configure $netpbm_prefix. -#################################################### -my $netpbm_prefix = "/usr/bin"; -$externalPrograms{giftopnm} = "$netpbm_prefix/giftopnm"; -$externalPrograms{ppmtopgm} = "$netpbm_prefix/ppmtopgm"; -$externalPrograms{pnmtops} = "$netpbm_prefix/pnmtops"; -$externalPrograms{pnmtopng} = "$netpbm_prefix/pnmtopng"; -$externalPrograms{pngtopnm} = "$netpbm_prefix/pngtopnm"; - -#################################################### -# curl -#################################################### -$externalPrograms{curl} = "/usr/bin/curl"; - -#################################################### -# image conversions utiltiies -# the source file is given on stdin, and the output expected on stdout. -#################################################### - -$externalPrograms{gif2eps} = "$externalPrograms{giftopnm} | $externalPrograms{ppmtopgm} | $externalPrograms{pnmtops} -noturn 2>/dev/null"; -$externalPrograms{png2eps} = "$externalPrograms{pngtopnm} | $externalPrograms{ppmtopgm} | $externalPrograms{pnmtops} -noturn 2>/dev/null"; -$externalPrograms{gif2png} = "$externalPrograms{giftopnm} | $externalPrograms{pnmtopng}"; - -#################################################### -# mysql clients -#################################################### - -$externalPrograms{mysql} ="/usr/bin/mysql"; -$externalPrograms{mysqldump} ="/usr/bin/mysqldump"; - - -#################################################### -# End paths to external utilities. -#################################################### - -################################################################################ -# Database options -################################################################################ - -# $database_debug = 0; - -# Standard permissions command used to initialize the webwork database -# GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, INDEX, LOCK TABLES ON webwork.* TO webworkWrite@localhost IDENTIFIED BY 'passwordRW'; -# where webworkWrite and passwordRW must match the corresponding variables in the next section. - -################################################################################ -# these variables are used by database.conf. we define them here so that editing -# database.conf isn't necessary. - -# You must initialize the database and set the password for webworkWrite. -# Edit the $database_password line and replace 'passwordRW' by the actual password used in the GRANT command above -################################################################################ - -# The database DSN is the path to the WeBWorK database which you have created. - -# Modern database DSN format: -# DBI:driver:database=$database;host=$hostname;port=$port (when DB not on localhost) -# or DBI:driver:database=$database;host=127.0.0.1;port=$port (when DB on localhost, using TCP) -# See: https://metacpan.org/pod/DBD::MariaDB#port -# "To connect to a MariaDB or MySQL server on localhost using TCP/IP, -# you must specify the host as 127.0.0.1 with the optional port, e.g. 3306." -# or DBI:driver:database=$database (when DB on localhost, using socket) - -# One thing on which it depends is the driver name, which you may want to modify. -# It also depends on the database name, which may be non-standard in some settings, -# as may be the hostname and port of the database server. - -# driver should be one of: -# "mysql" for the DBD:mysql driver -# "MariaDB" for the DBD:mysql driver - -# Select the desired DB driver: -#$database_driver="mysql"; -$database_driver="MariaDB"; - -$database_host="localhost"; -$database_port="3306"; -$database_name="webwork"; - -# For a DB on localhost - default to using Unix socket. -# Change to 0 to use a TCP connection to 127.0.0.1. -$database_use_socket_if_localhost=1; - -if ( $database_host eq "localhost" ) { - if ( $database_use_socket_if_localhost ) { - $database_dsn="DBI:$database_driver:database=$database_name"; - } else { - $database_dsn="DBI:$database_driver:database=$database_name;host=127.0.0.1;port=$database_port"; - } -} else { - $database_dsn="DBI:$database_driver:database=$database_name;host=$database_host;port=$database_port"; -} - -# The default storange engine to use is set here: -$database_storage_engine = 'myisam'; - -######################### -# MYSQL compatibility settings for handling international Unicode characters (utf8 and utf8mb) -######################### -# These set the way characters are encoded in mysql and will depend on the version of mysqld being used. -# the default is to use latin1. With version 2.15 we will move to -# encoding utf8mb4 which allows the encoding of characters from many languages -# including chinese, arabic and hebrew. - -$ENABLE_UTF8MB4 =1; # setting this to 1 enables utf8mb4 encoding, setting this to - # 0 sets this for older mysql (pre 5.3) which cannot - # handle utf8mb4 characters. - -$database_character_set=($ENABLE_UTF8MB4) ? 'utf8mb4' : 'utf8'; - - -# DATABASE login information -# The following two variables must match the GRANT statement run on the mysql server as described above. -$database_username ="webworkWrite"; -$database_password ='passwordRW'; -# password strings (or any other string allowing special characters) should be specified inside single quotes -# otherwise a string such as "someone@nowhere" will interpolate the contents of the array @nowhere -- which is probably -# empty, but still not what you want. Similar things happen with % and $ +# External Programs and database settings are now defined in webwork2.mojolicious.yml ################################################################################# # These variables describe the locations of various components of WeBWorK on your diff --git a/conf/webwork2.mojolicious.dist.yml b/conf/webwork2.mojolicious.dist.yml index 106f36b57b..aa7914c646 100644 --- a/conf/webwork2.mojolicious.dist.yml +++ b/conf/webwork2.mojolicious.dist.yml @@ -240,6 +240,93 @@ hardcopy: # If 1, don't delete temporary files created when a hardcopy is generated. preserve_temp_files: 0 +# External Programs + +# These applications are often found in /bin, but sometimes in /usr/bin or even in /opt/local/bin. +# You can use "which tar" for example to find out where the "tar" program is located + +externalPrograms: + mv: /bin/mv + cp: /bin/cp + rm: /bin/rm + mkdir: /bin/mkdir + tar: /bin/tar + gzip: /bin/gzip + git: /bin/git + curl: /usr/bin/curl + mysql: /usr/bin/mysql + mysqldump: /usr/bin/mysqldump + + # latex and related utilities + latex: /usr/bin/latex --no-shell-escape + pdflatex: /usr/bin/pdflatex --no-shell-escape + + # Note that --no-shell-escape is important for security reasons. + # Consider using xelatex instead of pdflatex for multilingual use, and + # use polyglossia and fontspec packages (which require xelatex or lualatex). + # pdflatex: /usr/bin/xelatex --no-shell-escape + + dvipng: /usr/bin/dvipng + convert: /usr/bin/convert + dvisvgm: /usr/bin/dvisvgm + pdf2svg: /usr/bin/pdf2svg + + giftopnm: /usr/bin/giftopnm + ppmtopgm: /usr/bin/ppmtopgm + pnmtops: /usr/bin/pnmtops + pnmtopng: /usr/bin/pnmtopng + pnmtopnm: /usr/bin/pnmtopnm + +# Database options + +database: + # uncomment to allow database debug information + # debug: 1 + + # Select the desired DB driver: either 'mysql' or 'MariaDB' + # database_driver: mysql + driver: MariaDB + + host: localhost + port: 3306 + + # name of the database + name: webwork + + # Standard permissions command used to initialize the webwork database + # GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, INDEX, LOCK TABLES ON webwork.* TO webworkWrite@localhost IDENTIFIED BY 'passwordRW'; + # where webworkWrite and passwordRW must match the corresponding variables in the next section. + username: '' + password: '' + + # For a DB on localhost - default to using Unix socket. Change to 0 to use a TCP connection to 127.0.0.1. + use_socket_if_localhost: 1 + + # Need to add the code in lines 209-217 to a perl file + # Add line 234 to same file + + # The default storange engine to use is set here: + storage_engine: myisam + + # MYSQL compatibility settings for handling international Unicode characters (utf8 and utf8mb) + + # These set the way characters are encoded in mysql and will depend on the version of mysqld being used. + # the default is to use latin1. With version 2.15 we will move to encoding utf8mb4 which allows + # the encoding of characters from many languages including chinese, arabic and hebrew. + + # setting this to 1 enables utf8mb4 encoding, setting this to 0 sets this for older + # mysql (pre 5.3) which cannot handle utf8mb4 characters. + + ENABLE_UTF8MB4: 1 + + # This sets the max course id length. It might need to be changed depending + # on what database tables are present. Mysql allows a max table length of 64 + # characters. With the ${course_id}_global_user_achievement table that means + # the max ${course_id} is exactly 40 characters. + + # Reference: https://dev.mysql.com/doc/refman/8.0/en/identifier-length.html + maxCourseIdLength: 40 + # Set this to 1 to allow the html2xml and render_rpc endpoints to disable # cookies and thus skip two factor authentication. This should never be enabled # for a typical webwork server. This should only be enabled if you want to diff --git a/lib/WeBWorK/CourseEnvironment.pm b/lib/WeBWorK/CourseEnvironment.pm index bfab7fce82..7b3b5a0735 100644 --- a/lib/WeBWorK/CourseEnvironment.pm +++ b/lib/WeBWorK/CourseEnvironment.pm @@ -54,10 +54,12 @@ use warnings; use Carp; use Opcode qw(empty_opset); +use YAML::XS qw(LoadFile); use WeBWorK::WWSafe; use WeBWorK::Utils::Files qw(readFile); use WeBWorK::Debug; +use WeBWorK::DB::Utils qw(databaseParams); =head1 CONSTRUCTION @@ -246,6 +248,17 @@ sub new { $self->{language} = 'zh-HK' if $self->{language} eq 'zh-hk'; $self->{language} = 'en' if $self->{language} eq 'en-us'; + # Load additional configuration variables. + my $defaults_file = "$seedVars->{webwork_dir}/conf/webwork2.mojolicious.dist.yml"; + die "Cannot read the mojolicous defaults file: $defaults_file" unless -r $defaults_file; + + # If this exists, load the overrides file (replacement for local overrides): + my $config_file = "$seedVars->{webwork_dir}/conf/webwork2.mojolicious.yml"; + my $config = -r $config_file ? LoadFile($config_file) : LoadFile($defaults_file); + + # Set the database settings. + $self->set_server_settings($config); + # now that we're done, we can go ahead and return... return $self; } @@ -359,6 +372,49 @@ sub status_abbrev_has_behavior { } } +sub set_server_settings { + my ($ce, $config) = @_; + + # set the database settings: + $ce->{database_name} = $config->{database}{name}; + $ce->{database_host} = $config->{database}{host}; + $ce->{database_username} = $config->{database}{username}; + $ce->{database_password} = $config->{database}{password}; + if ($config->{database}{host} eq "localhost") { + $ce->{database_dsn} = + $config->{database}{use_socket_if_localhost} + ? "DBI:$config->{database}{driver}:database=$config->{database}{name}" + : "DBI:$config->{database}{driver}:database=$config->{database}{name};" + . "host=127.0.0.1;port=$config->{database}{port}"; + } else { + $ce->{database_dsn} = "DBI:$config->{database}{driver}:database=$config->{database}{name};" + . "host=$config->{database}{host};port=$config->{database}{port}"; + } + $ce->{dbLayoutName} = 'sql_single'; + $config->{database}{dsn} = $ce->{database_dsn}; + $config->{database}{character_set} = $config->{database}{ENABLE_UTF8MB4} ? 'utf8mb4' : 'utf8'; + $ce->{dbLayout} = databaseParams($ce->{courseName}, $config->{database}, $config->{externalPrograms}); + + $ce->{maxCourseIdLength} = $config->{database}{maxCourseIdLength}; + + # ensure that the dvipng_depth_db information is defined: + $ce->{pg}{displayModeOptions}{images}{dvipng_depth_db}{user} //= $config->{database}{username}; + $ce->{pg}{displayModeOptions}{images}{dvipng_depth_db}{passwd} //= $config->{database}{password}; + $ce->{pg}{displayModeOptions}{images}{dvipng_depth_db}{dbsource} //= $ce->{database_dsn}; + + # Problem Library SQL database connection information + $c->{problemLibrary_db} = { + dbsource => $ce->{database_dsn}, + user => $ce->{database_username}, + passwd => $ce->{database_password}, + storage_engine => 'MYISAM', + }; + + $ce->{externalPrograms} = $config->{externalPrograms}; + return; +} + +=back =head2 two_factor_authentication_enabled Usage: C<< $ce->two_factor_authentication_enabled >> diff --git a/lib/WeBWorK/DB/Utils.pm b/lib/WeBWorK/DB/Utils.pm index f2b2a9d108..4be5167575 100644 --- a/lib/WeBWorK/DB/Utils.pm +++ b/lib/WeBWorK/DB/Utils.pm @@ -38,6 +38,7 @@ our @EXPORT_OK = qw( grok_vsetID grok_setID_from_vsetID_sql grok_versionID_from_vsetID_sql + databaseParams ); use constant fakeSetName => 'Undefined_Set'; @@ -181,4 +182,275 @@ sub grok_versionID_from_vsetID_sql($) { return "(SUBSTRING($field,INSTR($field,',v')+2)+0)"; } +# This function fills database fields of the CourseEnvironment + +sub databaseParams { + my ($courseName, $db_params, $externalPrograms) = @_; + + my %sqlParams = ( + username => $db_params->{username}, + password => $db_params->{password}, + debug => $db_params->{database_debug} // 0, + # kinda hacky, but needed for table dumping + mysql_path => $externalPrograms->{mysql}, + mysqldump_path => $externalPrograms->{mysqldump}, + ); + + if ($db_params->{driver} =~ /^mysql$/i) { + # The extra UTF8 connection setting is ONLY needed for older DBD:mysql driver + # and forbidden by the newer DBD::MariaDB driver + if ($db_params->{ENABLE_UTF8MB4}) { + $sqlParams{mysql_enable_utf8mb4} = 1; # Full 4-bit UTF-8 + } else { + $sqlParams{mysql_enable_utf8} = 1; # Only the partial 3-bit mySQL UTF-8 + } + } + return { + locations => { + record => "WeBWorK::DB::Record::Locations", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, non_native => 1 }, + }, + location_addresses => { + record => "WeBWorK::DB::Record::LocationAddresses", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, non_native => 1 }, + }, + depths => { + record => "WeBWorK::DB::Record::Depths", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + params => { %sqlParams, non_native => 1 }, + }, + password => { + record => "WeBWorK::DB::Record::Password", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_password" }, + }, + permission => { + record => "WeBWorK::DB::Record::PermissionLevel", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_permission" }, + }, + key => { + record => "WeBWorK::DB::Record::Key", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_key" }, + }, + user => { + record => "WeBWorK::DB::Record::User", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_user" }, + }, + set => { + record => "WeBWorK::DB::Record::Set", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_set" }, + }, + set_user => { + record => "WeBWorK::DB::Record::UserSet", + schema => "WeBWorK::DB::Schema::NewSQL::NonVersioned", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_set_user" }, + }, + set_merged => { + record => "WeBWorK::DB::Record::UserSet", + schema => "WeBWorK::DB::Schema::NewSQL::Merge", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + depend => [qw/set_user set/], + params => { + %sqlParams, + non_native => 1, + merge => [qw/set_user set/], + }, + }, + set_version => { + record => "WeBWorK::DB::Record::SetVersion", + schema => "WeBWorK::DB::Schema::NewSQL::Versioned", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + params => { + %sqlParams, + non_native => 1, + tableOverride => "${courseName}_set_user", + + }, + }, + set_version_merged => { + record => "WeBWorK::DB::Record::SetVersion", + schema => "WeBWorK::DB::Schema::NewSQL::Merge", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + depend => [qw/set_version set_user set/], + params => { + %sqlParams, + non_native => 1, + merge => [qw/set_version set_user set/], + }, + }, + set_locations => { + record => "WeBWorK::DB::Record::SetLocations", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_set_locations" }, + }, + set_locations_user => { + record => "WeBWorK::DB::Record::UserSetLocations", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_set_locations_user" }, + }, + problem => { + record => "WeBWorK::DB::Record::Problem", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_problem" }, + }, + problem_user => { + record => "WeBWorK::DB::Record::UserProblem", + schema => "WeBWorK::DB::Schema::NewSQL::NonVersioned", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_problem_user" }, + }, + problem_merged => { + record => "WeBWorK::DB::Record::UserProblem", + schema => "WeBWorK::DB::Schema::NewSQL::Merge", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + depend => [qw/problem_user problem/], + params => { + %sqlParams, + non_native => 1, + merge => [qw/problem_user problem/], + }, + }, + problem_version => { + record => "WeBWorK::DB::Record::ProblemVersion", + schema => "WeBWorK::DB::Schema::NewSQL::Versioned", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { + %sqlParams, + non_native => 1, + tableOverride => "${courseName}_problem_user", + }, + }, + problem_version_merged => { + record => "WeBWorK::DB::Record::ProblemVersion", + schema => "WeBWorK::DB::Schema::NewSQL::Merge", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + depend => [qw/problem_version problem_user problem/], + params => { + %sqlParams, + non_native => 1, + merge => [qw/problem_version problem_user problem/], + }, + }, + setting => { + record => "WeBWorK::DB::Record::Setting", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_setting" }, + }, + achievement => { + record => "WeBWorK::DB::Record::Achievement", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_achievement" }, + }, + past_answer => { + record => "WeBWorK::DB::Record::PastAnswer", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_past_answer" }, + }, + + achievement_user => { + record => "WeBWorK::DB::Record::UserAchievement", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_achievement_user" }, + }, + global_user_achievement => { + record => "WeBWorK::DB::Record::GlobalUserAchievement", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_global_user_achievement" }, + }, + }; +} + 1; From c6d72a39aa8721ffc4e3797c47cafb51945b848f Mon Sep 17 00:00:00 2001 From: Peter Staab Date: Sat, 2 Mar 2024 07:31:32 -0500 Subject: [PATCH 2/3] Continued removal of old dbLayouts This removes all references to dbLayoutName and cases where the dbLayouts hash is used. With this, there is only the standard dbLayout, which is now defined in `WeBWorK::DB::Layout`. --- bin/addcourse | 18 +- bin/old_scripts/wwaddindexing | 9 +- bin/upgrade-database-to-utf8mb4.pl | 2 +- conf/README.md | 3 +- conf/defaults.config | 10 +- conf/localOverrides.conf.dist | 25 ++ conf/site.conf.dist | 2 - conf/webwork2.mojolicious.dist.yml | 15 +- courses.dist/modelCourse/course.conf | 11 - lib/Mojolicious/WeBWorK.pm | 3 +- lib/WeBWorK/Authen.pm | 16 +- lib/WeBWorK/ContentGenerator/CourseAdmin.pm | 169 +++++----- lib/WeBWorK/CourseEnvironment.pm | 25 +- lib/WeBWorK/DB/Layout.pm | 298 ++++++++++++++++++ lib/WeBWorK/DB/Utils.pm | 272 ---------------- lib/WeBWorK/Utils/CourseIntegrityCheck.pm | 3 +- lib/WeBWorK/Utils/CourseManagement.pm | 185 ++--------- .../Utils/CourseManagement/sql_moodle.pm | 33 -- .../Utils/CourseManagement/sql_single.pm | 275 ---------------- lib/WebworkWebservice/CourseActions.pm | 2 +- .../ContentGenerator/Base/admin_links.html.ep | 1 - .../CourseAdmin/add_course_form.html.ep | 1 - .../CourseAdmin/upgrade_course_form.html.ep | 1 - 23 files changed, 464 insertions(+), 915 deletions(-) create mode 100644 lib/WeBWorK/DB/Layout.pm delete mode 100644 lib/WeBWorK/Utils/CourseManagement/sql_moodle.pm delete mode 100644 lib/WeBWorK/Utils/CourseManagement/sql_single.pm diff --git a/bin/addcourse b/bin/addcourse index 0eeb844de3..3c1abd528a 100755 --- a/bin/addcourse +++ b/bin/addcourse @@ -90,7 +90,6 @@ sub usage_error { warn "@_\n"; warn "usage: $0 [options] COURSEID\n"; warn "Options:\n"; - warn " [--db-layout=LAYOUT]\n"; warn " [--users=FILE [--professors=USERID[,USERID]...] ]\n"; exit; } @@ -99,7 +98,6 @@ my ($dbLayout, $users, $templates_from) = ('', '', ''); my @professors; GetOptions( - "db-layout=s" => \$dbLayout, "users=s" => \$users, "professors=s" => \@professors, "templates-from=s" => \$templates_from, @@ -114,23 +112,15 @@ $ce = WeBWorK::CourseEnvironment->new({ courseName => $courseID }); die "Aborting addcourse: Course ID cannot exceed $ce->{maxCourseIdLength} characters." if length($courseID) > $ce->{maxCourseIdLength}; -if ($dbLayout) { - die "Database layout $dbLayout does not exist in the course environment.", - " (It must be defined in defaults.config.)\n" - unless exists $ce->{dbLayouts}{$dbLayout}; -} else { - $dbLayout = $ce->{dbLayoutName}; -} - usage_error("Can't specify --professors without also specifying --users.") if @professors && !$users; my @users; if ($users) { # This is a hack to create records without bringing up a DB object - my $userClass = $ce->{dbLayouts}{$dbLayout}{user}{record}; - my $passwordClass = $ce->{dbLayouts}{$dbLayout}{password}{record}; - my $permissionClass = $ce->{dbLayouts}{$dbLayout}{permission}{record}; + my $userClass = $ce->{dbLayout}{user}{record}; + my $passwordClass = $ce->{dbLayout}{password}{record}; + my $permissionClass = $ce->{dbLayout}{permission}{record}; runtime_use($userClass); runtime_use($passwordClass); @@ -190,7 +180,7 @@ eval { addCourse( courseID => $courseID, ce => $ce, - courseOptions => { dbLayoutName => $dbLayout }, + courseOptions => {}, users => \@users, %optional_arguments, ); diff --git a/bin/old_scripts/wwaddindexing b/bin/old_scripts/wwaddindexing index 9319a9d933..d2600d1a52 100755 --- a/bin/old_scripts/wwaddindexing +++ b/bin/old_scripts/wwaddindexing @@ -16,7 +16,7 @@ =head1 NAME -wwaddindexing - add indices to an existing sql_single course. +wwaddindexing - add indices to an existing course. =head1 SYNOPSIS @@ -24,8 +24,7 @@ wwaddindexing - add indices to an existing sql_single course. =head1 DESCRIPTION -Adds indices to the course named COURSEID. The course must use the sql_single -database layout. +Adds indices to the course named COURSEID. =cut @@ -74,10 +73,6 @@ my $ce = WeBWorK::CourseEnvironment->new({ courseName => $courseID, }); -# make sure the course actually uses the 'sql_single' layout -usage_error("$courseID: does not use 'sql_single' database layout.") - unless $ce->{dbLayoutName} eq "sql_single"; - # get database layout source data my %sources = dbLayoutSQLSources($ce->{dbLayout}); diff --git a/bin/upgrade-database-to-utf8mb4.pl b/bin/upgrade-database-to-utf8mb4.pl index a79eb98a06..24347b0754 100755 --- a/bin/upgrade-database-to-utf8mb4.pl +++ b/bin/upgrade-database-to-utf8mb4.pl @@ -212,7 +212,7 @@ BEGIN }, ); -my $db = new WeBWorK::DB($ce->{dbLayouts}{ $ce->{dbLayoutName} }); +my $db = new WeBWorK::DB($ce->{dbLayout}); my @table_types = sort(grep { !$db->{$_}{params}{non_native} } keys %$db); sub checkAndUpdateTableColumnTypes { diff --git a/conf/README.md b/conf/README.md index aeaeebfb4e..e439baeabb 100644 --- a/conf/README.md +++ b/conf/README.md @@ -28,7 +28,8 @@ Configuration extension files. Server configuration files. - `webwork2.mojolicious.dist.yml` contains the webwork2 Mojolicious app configuration settings. Copy this file to - `webwork2.mojolicious.yml` if you need to change those settings. You usually will need to do this. This file contains server settings, database settings and paths to external programs. + `webwork2.mojolicious.yml` if you need to change those settings. You usually will need to do this. This file + contains server settings, database settings and paths to external programs. - `webwork2.dist.service` is a systemd configuration file for linux systems that serves the webwork2 app via the Mojolicious hypnotoad server. If you need to change it, then copy it to `webwork2.service`. - `webwork2-job-queue.dist.service` is a systemd configuration file for linux systems that runs the webwork2 job queue diff --git a/conf/defaults.config b/conf/defaults.config index 4952dfa893..01adf39886 100644 --- a/conf/defaults.config +++ b/conf/defaults.config @@ -1083,12 +1083,10 @@ $pg{displayModeOptions}{images} = { # as 'baseline' or 'middle'. dvipng_align => 'baseline', - # If dbsource is set to a nonempty value, then this database connection information will be used to store dvipng - # depths. It is assumed that the 'depths' table exists in the database. - # These are set in the CourseEnvironment. - dvipng_depth_db => {} - }, -}; + # The dvipng_depth_db will be used to store dvipng depths. It is assumed that the 'depths' + # table exists in the database. If you wish to override using the standard database connections + # see localOverrides.conf +}, ##### Directories used by PG diff --git a/conf/localOverrides.conf.dist b/conf/localOverrides.conf.dist index d3e2e5c529..c27991196b 100644 --- a/conf/localOverrides.conf.dist +++ b/conf/localOverrides.conf.dist @@ -404,6 +404,31 @@ $mail{feedbackRecipients} = [ #push (@{${pg}{modules}}, [qw(LaTeXImage)]); + +############################## +# Additional Database options +############################### + +# If dbsource is set to a nonempty value, then this database connection information will be used to store dvipng +# depths. It is assumed that the 'depths' table exists in the database. + +# $pg{displayModeOptions}{images}{dvipng_depth_db} => { +# dbsource => 'dsn_database_string', +# user => 'dsn_database_username', +# passwd => 'dsn_database_password', +# }, + +# Problem Library SQL database connection information + +# $problemLibrary_db = { +# dbsource => 'database_dsn', +# user => 'database_username', +# passwd => 'database_password', +# storage_engine => 'myisam', +# }; + + + ################################################################################ # Using R with WeBWorK ################################################################################ diff --git a/conf/site.conf.dist b/conf/site.conf.dist index b1e2725c02..5e14d97f5c 100644 --- a/conf/site.conf.dist +++ b/conf/site.conf.dist @@ -93,8 +93,6 @@ $admin_course_id = 'admin'; # status. #$new_courses_hidden_status = 'hidden'; -# External Programs and database settings are now defined in webwork2.mojolicious.yml - ################################################################################# # These variables describe the locations of various components of WeBWorK on your # server. You may use the defaults unless you have things in different places. diff --git a/conf/webwork2.mojolicious.dist.yml b/conf/webwork2.mojolicious.dist.yml index aa7914c646..67fa3c1260 100644 --- a/conf/webwork2.mojolicious.dist.yml +++ b/conf/webwork2.mojolicious.dist.yml @@ -252,7 +252,7 @@ externalPrograms: mkdir: /bin/mkdir tar: /bin/tar gzip: /bin/gzip - git: /bin/git + git: /usr/bin/git curl: /usr/bin/curl mysql: /usr/bin/mysql mysqldump: /usr/bin/mysqldump @@ -266,8 +266,12 @@ externalPrograms: # use polyglossia and fontspec packages (which require xelatex or lualatex). # pdflatex: /usr/bin/xelatex --no-shell-escape - dvipng: /usr/bin/dvipng + # In order to use imagemagick convert you need to change the rights for PDF files from + # "none" to "read" in the policy file /etc/ImageMagick-6/policy.xml. This has possible + # security implications for the server. convert: /usr/bin/convert + + dvipng: /usr/bin/dvipng dvisvgm: /usr/bin/dvisvgm pdf2svg: /usr/bin/pdf2svg @@ -296,15 +300,12 @@ database: # Standard permissions command used to initialize the webwork database # GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, INDEX, LOCK TABLES ON webwork.* TO webworkWrite@localhost IDENTIFIED BY 'passwordRW'; # where webworkWrite and passwordRW must match the corresponding variables in the next section. - username: '' - password: '' + username: webworkWrite + password: passwordRW # For a DB on localhost - default to using Unix socket. Change to 0 to use a TCP connection to 127.0.0.1. use_socket_if_localhost: 1 - # Need to add the code in lines 209-217 to a perl file - # Add line 234 to same file - # The default storange engine to use is set here: storage_engine: myisam diff --git a/courses.dist/modelCourse/course.conf b/courses.dist/modelCourse/course.conf index fb80c242a5..ff74987a9a 100644 --- a/courses.dist/modelCourse/course.conf +++ b/courses.dist/modelCourse/course.conf @@ -2,17 +2,6 @@ # This file is used to override the global WeBWorK course environment for this course. -# Database Layout (global value typically defined in global.conf) -# Several database are defined in the file conf/database.conf and stored in the -# hash %dbLayouts. -# The database layout is always set here, since one should be able to change the -# default value in global.conf without disrupting existing courses. -# global.conf values: -# $dbLayoutName = 'sql_single'; -# *dbLayout = $dbLayouts{$dbLayoutName}; -$dbLayoutName = 'sql_single'; -*dbLayout = $dbLayouts{$dbLayoutName}; - # Users for whom to label problems with the PG file name # For users in this list, PG will display the source file name when rendering a problem. #$pg{specialPGEnvironmentVars}{PRINT_FILE_NAMES_FOR} = ['user_id1']; diff --git a/lib/Mojolicious/WeBWorK.pm b/lib/Mojolicious/WeBWorK.pm index 4366520fb6..a323f54030 100644 --- a/lib/Mojolicious/WeBWorK.pm +++ b/lib/Mojolicious/WeBWorK.pm @@ -173,8 +173,7 @@ sub startup ($app) { writeTimingLogEntry( $c->ce, '[' . $c->url_for . ']', - sprintf('runTime = %.3f sec', $c->timing->elapsed('content_generator_rendering')) . ' ' - . $c->ce->{dbLayoutName} + sprintf('runTime = %.3f sec', $c->timing->elapsed('content_generator_rendering')), '' ); } } diff --git a/lib/WeBWorK/Authen.pm b/lib/WeBWorK/Authen.pm index 9f2cf99bdf..87490973bd 100644 --- a/lib/WeBWorK/Authen.pm +++ b/lib/WeBWorK/Authen.pm @@ -96,26 +96,22 @@ sub class { if (exists $ce->{authen}{$type}) { if (ref $ce->{authen}{$type} eq "ARRAY") { my $authen_type = shift @{ $ce->{authen}{$type} }; + + #debug("ref of authen_type = |" . ref($authen_type) . "|"); if (ref($authen_type) eq "HASH") { - if (exists $authen_type->{ $ce->{dbLayoutName} }) { - return $authen_type->{ $ce->{dbLayoutName} }; - } elsif (exists $authen_type->{"*"}) { + if (exists $authen_type->{"*"}) { return $authen_type->{"*"}; } else { - die "authentication type '$type' in the course environment has no entry for db layout '", - $ce->{dbLayoutName}, "' and no default entry (*)"; + die "authentication type '$type' in the course environment has no default entry (*)"; } } else { return $authen_type; } } elsif (ref $ce->{authen}{$type} eq "HASH") { - if (exists $ce->{authen}{$type}{ $ce->{dbLayoutName} }) { - return $ce->{authen}{$type}{ $ce->{dbLayoutName} }; - } elsif (exists $ce->{authen}{$type}{"*"}) { + if (exists $ce->{authen}{$type}{"*"}) { return $ce->{authen}{$type}{"*"}; } else { - die "authentication type '$type' in the course environment has no entry for db layout '", - $ce->{dbLayoutName}, "' and no default entry (*)"; + die "authentication type '$type' in the course environment has no default entry (*)"; } } else { return $ce->{authen}{$type}; diff --git a/lib/WeBWorK/ContentGenerator/CourseAdmin.pm b/lib/WeBWorK/ContentGenerator/CourseAdmin.pm index 362dc8868d..5c3a6bf407 100644 --- a/lib/WeBWorK/ContentGenerator/CourseAdmin.pm +++ b/lib/WeBWorK/ContentGenerator/CourseAdmin.pm @@ -47,7 +47,7 @@ sub pre_header_initialize ($c) { # Check that the non-native tables are present in the database. # These are the tables which are not course specific. - my @table_update_messages = initNonNativeTables($ce, $ce->{dbLayoutName}); + my @table_update_messages = initNonNativeTables($ce); $c->addgoodmessage($c->c(@table_update_messages)->join($c->tag('br'))) if @table_update_messages; my @errors; @@ -232,7 +232,6 @@ sub add_course_validate ($c) { my $add_initial_firstName = trim_spaces($c->param('add_initial_firstName')) || ''; my $add_initial_lastName = trim_spaces($c->param('add_initial_lastName')) || ''; my $add_initial_email = trim_spaces($c->param('add_initial_email')) || ''; - my $add_dbLayout = trim_spaces($c->param('add_dbLayout')) || ''; my @errors; @@ -270,17 +269,6 @@ sub add_course_validate ($c) { } } - if ($add_dbLayout eq '') { - push @errors, 'You must select a database layout.'; - } else { - if (exists $ce->{dbLayouts}{$add_dbLayout}) { - # we used to check for layout-specific fields here, but there aren't any layouts that require them - # anymore. (in the future, we'll probably deal with this in layout-specific modules.) - } else { - push @errors, "The database layout $add_dbLayout doesn't exist."; - } - } - return @errors; } @@ -302,12 +290,8 @@ sub do_add_course ($c) { my $copy_from_course = trim_spaces($c->param('copy_from_course')) // ''; - my $add_dbLayout = trim_spaces($c->param('add_dbLayout')) || ''; - my $ce2 = WeBWorK::CourseEnvironment->new({ courseName => $add_courseID }); - my %courseOptions = (dbLayoutName => $add_dbLayout); - my @users; # copy users from current (admin) course if desired @@ -352,7 +336,7 @@ sub do_add_course ($c) { ); push @users, [ $User, $Password, $PermissionLevel ]; } - + my %courseOptions = (); push @{ $courseOptions{PRINT_FILE_NAMES_FOR} }, map { $_->[0]->user_id } @users; # Include any optional arguments, including a template course and the course title and course institution. @@ -491,9 +475,8 @@ sub rename_course_confirm ($c) { # Create strings confirming title and institution change. # Connect to the database to get old title and institution. - my $dbLayoutName = $ce->{dbLayoutName}; - my $db = WeBWorK::DB->new($ce->{dbLayouts}{$dbLayoutName}); - my $oldDB = WeBWorK::DB->new($ce2->{dbLayouts}{$dbLayoutName}); + my $db = WeBWorK::DB->new($ce->{dbLayout}); + my $oldDB = WeBWorK::DB->new($ce2->{dbLayout}); my $rename_oldCourseTitle = $oldDB->getSettingValue('courseTitle') // ''; my $rename_oldCourseInstitution = $oldDB->getSettingValue('courseInstitution') // ''; @@ -517,49 +500,46 @@ sub rename_course_confirm ($c) { rename_oldCourseID => $rename_oldCourseID ) unless $c->param('rename_newCourseID_checkbox'); - if ($ce2->{dbLayoutName}) { - my $CIchecker = WeBWorK::Utils::CourseIntegrityCheck->new(ce => $ce2); - - # Check database - my ($tables_ok, $dbStatus) = $CIchecker->checkCourseTables($rename_oldCourseID); + my $CIchecker = WeBWorK::Utils::CourseIntegrityCheck->new(ce => $ce2); - # Upgrade the database if requested. - my @upgrade_report; - if ($c->param('upgrade_course_tables')) { - my @schema_table_names = keys %$dbStatus; - my @tables_to_create = - grep { $dbStatus->{$_}->[0] == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A } @schema_table_names; - my @tables_to_alter = - grep { $dbStatus->{$_}->[0] == WeBWorK::Utils::CourseIntegrityCheck::DIFFER_IN_A_AND_B } - @schema_table_names; - push(@upgrade_report, $CIchecker->updateCourseTables($rename_oldCourseID, [@tables_to_create])); - for my $table_name (@tables_to_alter) { - push(@upgrade_report, $CIchecker->updateTableFields($rename_oldCourseID, $table_name)); - } + # Check database + my ($tables_ok, $dbStatus) = $CIchecker->checkCourseTables($rename_oldCourseID); - ($tables_ok, $dbStatus) = $CIchecker->checkCourseTables($rename_oldCourseID); + # Upgrade the database if requested. + my @upgrade_report; + if ($c->param('upgrade_course_tables')) { + my @schema_table_names = keys %$dbStatus; + my @tables_to_create = + grep { $dbStatus->{$_}->[0] == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A } @schema_table_names; + my @tables_to_alter = + grep { $dbStatus->{$_}->[0] == WeBWorK::Utils::CourseIntegrityCheck::DIFFER_IN_A_AND_B } + @schema_table_names; + push(@upgrade_report, $CIchecker->updateCourseTables($rename_oldCourseID, [@tables_to_create])); + for my $table_name (@tables_to_alter) { + push(@upgrade_report, $CIchecker->updateTableFields($rename_oldCourseID, $table_name)); } - # Check directories - my ($directories_ok, $directory_report) = $CIchecker->checkCourseDirectories($ce2); - - return $c->include( - 'ContentGenerator/CourseAdmin/rename_course_confirm', - upgrade_report => \@upgrade_report, - tables_ok => $tables_ok, - dbStatus => $dbStatus, - directory_report => $directory_report, - directories_ok => $directories_ok, - rename_oldCourseTitle => $rename_oldCourseTitle, - change_course_title_str => $change_course_title_str, - rename_oldCourseInstitution => $rename_oldCourseInstitution, - change_course_institution_str => $change_course_institution_str, - rename_oldCourseID => $rename_oldCourseID, - rename_newCourseID => $rename_newCourseID - ); - } else { - return $c->tag('p', class => 'text-danger fw-bold', "Unable to find database layout for $rename_oldCourseID"); + ($tables_ok, $dbStatus) = $CIchecker->checkCourseTables($rename_oldCourseID); } + + # Check directories + my ($directories_ok, $directory_report) = $CIchecker->checkCourseDirectories($ce2); + + return $c->include( + 'ContentGenerator/CourseAdmin/rename_course_confirm', + upgrade_report => \@upgrade_report, + tables_ok => $tables_ok, + dbStatus => $dbStatus, + directory_report => $directory_report, + directories_ok => $directories_ok, + rename_oldCourseTitle => $rename_oldCourseTitle, + change_course_title_str => $change_course_title_str, + rename_oldCourseInstitution => $rename_oldCourseInstitution, + change_course_institution_str => $change_course_institution_str, + rename_oldCourseID => $rename_oldCourseID, + rename_newCourseID => $rename_newCourseID + ); + return; } sub rename_course_validate ($c) { @@ -997,48 +977,45 @@ sub archive_course_confirm ($c) { my $ce2 = WeBWorK::CourseEnvironment->new({ courseName => $archive_courseID }); - if ($ce2->{dbLayoutName}) { - my $CIchecker = WeBWorK::Utils::CourseIntegrityCheck->new(ce => $ce2); - - # Check database - my ($tables_ok, $dbStatus) = $CIchecker->checkCourseTables($archive_courseID); + my $CIchecker = WeBWorK::Utils::CourseIntegrityCheck->new(ce => $ce2); - # Upgrade the database if requested. - my @upgrade_report; - if ($c->param('upgrade_course_tables')) { - my @schema_table_names = keys %$dbStatus; - my @tables_to_create = - grep { $dbStatus->{$_}->[0] == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A } @schema_table_names; - my @tables_to_alter = - grep { $dbStatus->{$_}->[0] == WeBWorK::Utils::CourseIntegrityCheck::DIFFER_IN_A_AND_B } - @schema_table_names; - push(@upgrade_report, $CIchecker->updateCourseTables($archive_courseID, [@tables_to_create])); - for my $table_name (@tables_to_alter) { - push(@upgrade_report, $CIchecker->updateTableFields($archive_courseID, $table_name)); - } + # Check database + my ($tables_ok, $dbStatus) = $CIchecker->checkCourseTables($archive_courseID); - ($tables_ok, $dbStatus) = $CIchecker->checkCourseTables($archive_courseID); + # Upgrade the database if requested. + my @upgrade_report; + if ($c->param('upgrade_course_tables')) { + my @schema_table_names = keys %$dbStatus; + my @tables_to_create = + grep { $dbStatus->{$_}->[0] == WeBWorK::Utils::CourseIntegrityCheck::ONLY_IN_A } @schema_table_names; + my @tables_to_alter = + grep { $dbStatus->{$_}->[0] == WeBWorK::Utils::CourseIntegrityCheck::DIFFER_IN_A_AND_B } + @schema_table_names; + push(@upgrade_report, $CIchecker->updateCourseTables($archive_courseID, [@tables_to_create])); + for my $table_name (@tables_to_alter) { + push(@upgrade_report, $CIchecker->updateTableFields($archive_courseID, $table_name)); } - # Update and check directories. - my $dir_update_messages = $c->param('upgrade_course_tables') ? $CIchecker->updateCourseDirectories : []; - my ($directories_ok, $directory_report) = $CIchecker->checkCourseDirectories($ce2); - - return $c->include( - 'ContentGenerator/CourseAdmin/archive_course_confirm', - ce2 => $ce2, - upgrade_report => \@upgrade_report, - tables_ok => $tables_ok, - dbStatus => $dbStatus, - dir_update_messages => $dir_update_messages, - directory_report => $directory_report, - directories_ok => $directories_ok, - archive_courseID => $archive_courseID, - archive_courseIDs => \@archive_courseIDs - ); - } else { - return $c->tag('p', class => 'text-danger fw-bold', "Unable to find database layout for $archive_courseID"); + ($tables_ok, $dbStatus) = $CIchecker->checkCourseTables($archive_courseID); } + + # Update and check directories. + my $dir_update_messages = $c->param('upgrade_course_tables') ? $CIchecker->updateCourseDirectories : []; + my ($directories_ok, $directory_report) = $CIchecker->checkCourseDirectories($ce2); + + return $c->include( + 'ContentGenerator/CourseAdmin/archive_course_confirm', + ce2 => $ce2, + upgrade_report => \@upgrade_report, + tables_ok => $tables_ok, + dbStatus => $dbStatus, + dir_update_messages => $dir_update_messages, + directory_report => $directory_report, + directories_ok => $directories_ok, + archive_courseID => $archive_courseID, + archive_courseIDs => \@archive_courseIDs + ); + return; } sub do_archive_course ($c) { diff --git a/lib/WeBWorK/CourseEnvironment.pm b/lib/WeBWorK/CourseEnvironment.pm index 7b3b5a0735..a9cc15b889 100644 --- a/lib/WeBWorK/CourseEnvironment.pm +++ b/lib/WeBWorK/CourseEnvironment.pm @@ -59,7 +59,7 @@ use YAML::XS qw(LoadFile); use WeBWorK::WWSafe; use WeBWorK::Utils::Files qw(readFile); use WeBWorK::Debug; -use WeBWorK::DB::Utils qw(databaseParams); +use WeBWorK::DB::Layout qw(layout); =head1 CONSTRUCTION @@ -390,10 +390,9 @@ sub set_server_settings { $ce->{database_dsn} = "DBI:$config->{database}{driver}:database=$config->{database}{name};" . "host=$config->{database}{host};port=$config->{database}{port}"; } - $ce->{dbLayoutName} = 'sql_single'; $config->{database}{dsn} = $ce->{database_dsn}; $config->{database}{character_set} = $config->{database}{ENABLE_UTF8MB4} ? 'utf8mb4' : 'utf8'; - $ce->{dbLayout} = databaseParams($ce->{courseName}, $config->{database}, $config->{externalPrograms}); + $ce->{dbLayout} = layout($ce->{courseName}, $config->{database}, $config->{externalPrograms}); $ce->{maxCourseIdLength} = $config->{database}{maxCourseIdLength}; @@ -403,12 +402,20 @@ sub set_server_settings { $ce->{pg}{displayModeOptions}{images}{dvipng_depth_db}{dbsource} //= $ce->{database_dsn}; # Problem Library SQL database connection information - $c->{problemLibrary_db} = { - dbsource => $ce->{database_dsn}, - user => $ce->{database_username}, - passwd => $ce->{database_password}, - storage_engine => 'MYISAM', - }; + $ce->{problemLibrary_db}{dbsource} //= $ce->{database_dsn}; + $ce->{problemLibrary_db}{user} //= $ce->{database_username}; + $ce->{problemLibrary_db}{passwd} //= $ce->{database_password}; + $ce->{problemLibrary_db}{storage_engine} //= 'myisam'; + + # image conversions utiltiies + # the source file is given on stdin, and the output expected on stdout. + + $config->{externalPrograms}{gif2eps} = $config->{externalPrograms}{giftopnm} + // $config->{externalPrograms}{ppmtopgm} // "$config->{externalPrograms}{pnmtops} -noturn 2 > /dev/null"; + $config->{externalPrograms}{png2eps} = $config->{externalPrograms}{pngtopnm} + // $config->{externalPrograms}{ppmtopgm} // "$config->{externalPrograms}{pnmtops} -noturn 2 > /dev/null"; + $config->{externalPrograms}{gif2png} = $config->{externalPrograms}{giftopnm} + // $config->{externalPrograms}{pnmtopng}; $ce->{externalPrograms} = $config->{externalPrograms}; return; diff --git a/lib/WeBWorK/DB/Layout.pm b/lib/WeBWorK/DB/Layout.pm new file mode 100644 index 0000000000..ab7abd301d --- /dev/null +++ b/lib/WeBWorK/DB/Layout.pm @@ -0,0 +1,298 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2023 The WeBWorK Project, https://github.com/openwebwork +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +package WeBWorK::DB::Layout; +use base qw(Exporter); + +=head1 NAME + +WeBWorK::DB::Layout - returns the database layout given server parameters. + +=cut + +use strict; +use warnings; + +our @EXPORT = (); +our @EXPORT_OK = qw(layout); + +sub layout { + my ($courseName, $db_params, $externalPrograms) = @_; + + my %sqlParams = ( + username => $db_params->{username}, + password => $db_params->{password}, + debug => $db_params->{database_debug} // 0, + # kinda hacky, but needed for table dumping + mysql_path => $externalPrograms->{mysql}, + mysqldump_path => $externalPrograms->{mysqldump}, + ); + + if ($db_params->{driver} =~ /^mysql$/i) { + # The extra UTF8 connection setting is ONLY needed for older DBD:mysql driver + # and forbidden by the newer DBD::MariaDB driver + if ($db_params->{ENABLE_UTF8MB4}) { + $sqlParams{mysql_enable_utf8mb4} = 1; # Full 4-bit UTF-8 + } else { + $sqlParams{mysql_enable_utf8} = 1; # Only the partial 3-bit mySQL UTF-8 + } + } + return { + locations => { + record => "WeBWorK::DB::Record::Locations", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, non_native => 1 }, + }, + location_addresses => { + record => "WeBWorK::DB::Record::LocationAddresses", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, non_native => 1 }, + }, + depths => { + record => "WeBWorK::DB::Record::Depths", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + params => { %sqlParams, non_native => 1 }, + }, + password => { + record => "WeBWorK::DB::Record::Password", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_password" }, + }, + permission => { + record => "WeBWorK::DB::Record::PermissionLevel", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_permission" }, + }, + key => { + record => "WeBWorK::DB::Record::Key", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_key" }, + }, + user => { + record => "WeBWorK::DB::Record::User", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_user" }, + }, + set => { + record => "WeBWorK::DB::Record::Set", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_set" }, + }, + set_user => { + record => "WeBWorK::DB::Record::UserSet", + schema => "WeBWorK::DB::Schema::NewSQL::NonVersioned", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_set_user" }, + }, + set_merged => { + record => "WeBWorK::DB::Record::UserSet", + schema => "WeBWorK::DB::Schema::NewSQL::Merge", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + depend => [qw/set_user set/], + params => { + %sqlParams, + non_native => 1, + merge => [qw/set_user set/], + }, + }, + set_version => { + record => "WeBWorK::DB::Record::SetVersion", + schema => "WeBWorK::DB::Schema::NewSQL::Versioned", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + params => { + %sqlParams, + non_native => 1, + tableOverride => "${courseName}_set_user", + + }, + }, + set_version_merged => { + record => "WeBWorK::DB::Record::SetVersion", + schema => "WeBWorK::DB::Schema::NewSQL::Merge", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + depend => [qw/set_version set_user set/], + params => { + %sqlParams, + non_native => 1, + merge => [qw/set_version set_user set/], + }, + }, + set_locations => { + record => "WeBWorK::DB::Record::SetLocations", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_set_locations" }, + }, + set_locations_user => { + record => "WeBWorK::DB::Record::UserSetLocations", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_set_locations_user" }, + }, + problem => { + record => "WeBWorK::DB::Record::Problem", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_problem" }, + }, + problem_user => { + record => "WeBWorK::DB::Record::UserProblem", + schema => "WeBWorK::DB::Schema::NewSQL::NonVersioned", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_problem_user" }, + }, + problem_merged => { + record => "WeBWorK::DB::Record::UserProblem", + schema => "WeBWorK::DB::Schema::NewSQL::Merge", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + depend => [qw/problem_user problem/], + params => { + %sqlParams, + non_native => 1, + merge => [qw/problem_user problem/], + }, + }, + problem_version => { + record => "WeBWorK::DB::Record::ProblemVersion", + schema => "WeBWorK::DB::Schema::NewSQL::Versioned", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { + %sqlParams, + non_native => 1, + tableOverride => "${courseName}_problem_user", + }, + }, + problem_version_merged => { + record => "WeBWorK::DB::Record::ProblemVersion", + schema => "WeBWorK::DB::Schema::NewSQL::Merge", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + depend => [qw/problem_version problem_user problem/], + params => { + %sqlParams, + non_native => 1, + merge => [qw/problem_version problem_user problem/], + }, + }, + setting => { + record => "WeBWorK::DB::Record::Setting", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_setting" }, + }, + achievement => { + record => "WeBWorK::DB::Record::Achievement", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_achievement" }, + }, + past_answer => { + record => "WeBWorK::DB::Record::PastAnswer", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_past_answer" }, + }, + + achievement_user => { + record => "WeBWorK::DB::Record::UserAchievement", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_achievement_user" }, + }, + global_user_achievement => { + record => "WeBWorK::DB::Record::GlobalUserAchievement", + schema => "WeBWorK::DB::Schema::NewSQL::Std", + driver => "WeBWorK::DB::Driver::SQL", + source => $db_params->{dsn}, + engine => $db_params->{storage_engine}, + character_set => $db_params->{character_set}, + params => { %sqlParams, tableOverride => "${courseName}_global_user_achievement" }, + }, + }; +} diff --git a/lib/WeBWorK/DB/Utils.pm b/lib/WeBWorK/DB/Utils.pm index 4be5167575..f2b2a9d108 100644 --- a/lib/WeBWorK/DB/Utils.pm +++ b/lib/WeBWorK/DB/Utils.pm @@ -38,7 +38,6 @@ our @EXPORT_OK = qw( grok_vsetID grok_setID_from_vsetID_sql grok_versionID_from_vsetID_sql - databaseParams ); use constant fakeSetName => 'Undefined_Set'; @@ -182,275 +181,4 @@ sub grok_versionID_from_vsetID_sql($) { return "(SUBSTRING($field,INSTR($field,',v')+2)+0)"; } -# This function fills database fields of the CourseEnvironment - -sub databaseParams { - my ($courseName, $db_params, $externalPrograms) = @_; - - my %sqlParams = ( - username => $db_params->{username}, - password => $db_params->{password}, - debug => $db_params->{database_debug} // 0, - # kinda hacky, but needed for table dumping - mysql_path => $externalPrograms->{mysql}, - mysqldump_path => $externalPrograms->{mysqldump}, - ); - - if ($db_params->{driver} =~ /^mysql$/i) { - # The extra UTF8 connection setting is ONLY needed for older DBD:mysql driver - # and forbidden by the newer DBD::MariaDB driver - if ($db_params->{ENABLE_UTF8MB4}) { - $sqlParams{mysql_enable_utf8mb4} = 1; # Full 4-bit UTF-8 - } else { - $sqlParams{mysql_enable_utf8} = 1; # Only the partial 3-bit mySQL UTF-8 - } - } - return { - locations => { - record => "WeBWorK::DB::Record::Locations", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, non_native => 1 }, - }, - location_addresses => { - record => "WeBWorK::DB::Record::LocationAddresses", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, non_native => 1 }, - }, - depths => { - record => "WeBWorK::DB::Record::Depths", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - params => { %sqlParams, non_native => 1 }, - }, - password => { - record => "WeBWorK::DB::Record::Password", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_password" }, - }, - permission => { - record => "WeBWorK::DB::Record::PermissionLevel", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_permission" }, - }, - key => { - record => "WeBWorK::DB::Record::Key", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_key" }, - }, - user => { - record => "WeBWorK::DB::Record::User", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_user" }, - }, - set => { - record => "WeBWorK::DB::Record::Set", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_set" }, - }, - set_user => { - record => "WeBWorK::DB::Record::UserSet", - schema => "WeBWorK::DB::Schema::NewSQL::NonVersioned", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_set_user" }, - }, - set_merged => { - record => "WeBWorK::DB::Record::UserSet", - schema => "WeBWorK::DB::Schema::NewSQL::Merge", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - depend => [qw/set_user set/], - params => { - %sqlParams, - non_native => 1, - merge => [qw/set_user set/], - }, - }, - set_version => { - record => "WeBWorK::DB::Record::SetVersion", - schema => "WeBWorK::DB::Schema::NewSQL::Versioned", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - params => { - %sqlParams, - non_native => 1, - tableOverride => "${courseName}_set_user", - - }, - }, - set_version_merged => { - record => "WeBWorK::DB::Record::SetVersion", - schema => "WeBWorK::DB::Schema::NewSQL::Merge", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - depend => [qw/set_version set_user set/], - params => { - %sqlParams, - non_native => 1, - merge => [qw/set_version set_user set/], - }, - }, - set_locations => { - record => "WeBWorK::DB::Record::SetLocations", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_set_locations" }, - }, - set_locations_user => { - record => "WeBWorK::DB::Record::UserSetLocations", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_set_locations_user" }, - }, - problem => { - record => "WeBWorK::DB::Record::Problem", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_problem" }, - }, - problem_user => { - record => "WeBWorK::DB::Record::UserProblem", - schema => "WeBWorK::DB::Schema::NewSQL::NonVersioned", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_problem_user" }, - }, - problem_merged => { - record => "WeBWorK::DB::Record::UserProblem", - schema => "WeBWorK::DB::Schema::NewSQL::Merge", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - depend => [qw/problem_user problem/], - params => { - %sqlParams, - non_native => 1, - merge => [qw/problem_user problem/], - }, - }, - problem_version => { - record => "WeBWorK::DB::Record::ProblemVersion", - schema => "WeBWorK::DB::Schema::NewSQL::Versioned", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { - %sqlParams, - non_native => 1, - tableOverride => "${courseName}_problem_user", - }, - }, - problem_version_merged => { - record => "WeBWorK::DB::Record::ProblemVersion", - schema => "WeBWorK::DB::Schema::NewSQL::Merge", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - depend => [qw/problem_version problem_user problem/], - params => { - %sqlParams, - non_native => 1, - merge => [qw/problem_version problem_user problem/], - }, - }, - setting => { - record => "WeBWorK::DB::Record::Setting", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_setting" }, - }, - achievement => { - record => "WeBWorK::DB::Record::Achievement", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_achievement" }, - }, - past_answer => { - record => "WeBWorK::DB::Record::PastAnswer", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_past_answer" }, - }, - - achievement_user => { - record => "WeBWorK::DB::Record::UserAchievement", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_achievement_user" }, - }, - global_user_achievement => { - record => "WeBWorK::DB::Record::GlobalUserAchievement", - schema => "WeBWorK::DB::Schema::NewSQL::Std", - driver => "WeBWorK::DB::Driver::SQL", - source => $db_params->{dsn}, - engine => $db_params->{storage_engine}, - character_set => $db_params->{character_set}, - params => { %sqlParams, tableOverride => "${courseName}_global_user_achievement" }, - }, - }; -} - 1; diff --git a/lib/WeBWorK/Utils/CourseIntegrityCheck.pm b/lib/WeBWorK/Utils/CourseIntegrityCheck.pm index 2b43290814..5e920a392f 100644 --- a/lib/WeBWorK/Utils/CourseIntegrityCheck.pm +++ b/lib/WeBWorK/Utils/CourseIntegrityCheck.pm @@ -66,8 +66,7 @@ sub init { $self->{verbose_sub} = $options{verbose_sub} || \&debug; $self->{confirm_sub} = $options{confirm_sub} || \&ask_permission_stdio; $self->{ce} = $options{ce}; - my $dbLayoutName = $self->{ce}->{dbLayoutName}; - $self->{db} = WeBWorK::DB->new($self->{ce}{dbLayouts}->{$dbLayoutName}); + $self->{db} = WeBWorK::DB->new($self->{ce}{dbLayout}); return; } diff --git a/lib/WeBWorK/Utils/CourseManagement.pm b/lib/WeBWorK/Utils/CourseManagement.pm index a348636b87..3cc3d99975 100644 --- a/lib/WeBWorK/Utils/CourseManagement.pm +++ b/lib/WeBWorK/Utils/CourseManagement.pm @@ -172,10 +172,9 @@ environment. $courseOptions is a reference to a hash containing the following options: - dbLayoutName => $dbLayoutName - PRINT_FILE_NAMES_FOR => $pg{specialPGEnvironmentVars}->{PRINT_FILE_NAMES_FOR} + PRINT_FILE_NAMES_FOR => $pg{specialPGEnvironmentVars}->{PRINT_FILE_NAMES_FOR} -C is required. C is a reference to an array. +C is a reference to an array. $users is a list of arrayrefs, each containing a User, Password, and PermissionLevel record for a single user: @@ -216,9 +215,6 @@ sub addCourse { debug \@users; - # get the database layout out of the options hash - my $dbLayoutName = $courseOptions{dbLayoutName}; - # collect some data my $coursesDir = $ce->{webworkDirs}->{courses}; my $courseDir = "$coursesDir/$courseID"; @@ -242,16 +238,6 @@ sub addCourse { croak "Course ID cannot exceed " . $ce->{maxCourseIdLength} . " characters." if (length($courseID) > $ce->{maxCourseIdLength}); - # if we didn't get a database layout, use the default one - if (not defined $dbLayoutName) { - $dbLayoutName = $ce->{dbLayoutName}; - } - - # fail if the database layout is invalid - if (not exists $ce->{dbLayouts}->{$dbLayoutName}) { - croak "$dbLayoutName: not found in \%dbLayouts"; - } - ##### step 1: create course directory structure ##### my %courseDirs = %{ $ce->{courseDirs} }; @@ -321,7 +307,7 @@ sub addCourse { ##### step 2: create course database ##### - my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); + my $db = new WeBWorK::DB($ce->{dbLayout}); my $create_db_result = $db->create_tables; die "$courseID: course database creation failed.\n" unless $create_db_result; @@ -340,11 +326,11 @@ sub addCourse { ) { $ce0 = WeBWorK::CourseEnvironment->new({ courseName => $sourceCourse }); - $db0 = WeBWorK::DB->new($ce0->{dbLayouts}{$dbLayoutName}); + $db0 = WeBWorK::DB->new($ce0->{dbLayout}); } # add users (users that were directly passed to addCourse() as well as those copied from a source course) - if ($ce->{dbLayouts}{$dbLayoutName}{user}{params}{non_native}) { + if ($ce->{dbLayout}{user}{params}{non_native}) { debug("not adding users to the course database: 'user' table is non-native.\n"); } else { if ($db0 && $options{copyNonStudents}) { @@ -542,16 +528,12 @@ sub renameCourse { # $fromCE ($oldCE) # $toCourseID ($newCourseID) # $toCE (construct from $oldCE) - # $dbLayoutName ($oldCE->{dbLayoutName}) my $oldCourseID = $options{courseID}; my $oldCE = $options{ce}; my $newCourseID = $options{newCourseID}; my $skipDBRename = $options{skipDBRename} || 0; - # get the database layout out of the options hash - my $dbLayoutName = $oldCE->{dbLayoutName}; - # collect some data my $coursesDir = $oldCE->{webworkDirs}->{courses}; my $oldCourseDir = "$coursesDir/$oldCourseID"; @@ -641,12 +623,12 @@ sub renameCourse { ##### step 2: rename database ##### unless ($skipDBRename) { - my $oldDB = new WeBWorK::DB($oldCE->{dbLayouts}{$dbLayoutName}); + my $oldDB = new WeBWorK::DB($oldCE->{dbLayout}); - my $rename_db_result = $oldDB->rename_tables($newCE->{dbLayouts}{$dbLayoutName}); + my $rename_db_result = $oldDB->rename_tables($newCE->{dbLayout}); die "$oldCourseID: course database renaming failed.\n" unless $rename_db_result; #update title and institution - my $newDB = new WeBWorK::DB($newCE->{dbLayouts}{$dbLayoutName}); + my $newDB = new WeBWorK::DB($newCE->{dbLayout}); eval { if (exists($options{courseTitle}) and $options{courseTitle}) { $newDB->setSettingValue('courseTitle', $options{courseTitle}); @@ -681,15 +663,13 @@ sub retitleCourse { # renameCourseHelper needs: # $courseID ($oldCourseID) # $ce ($oldCE) - # $dbLayoutName ($ce->{dbLayoutName}) # courseTitle # courseInstitution my $courseID = $options{courseID}; my $ce = $options{ce}; # get the database layout out of the options hash - my $dbLayoutName = $ce->{dbLayoutName}; - my $db = new WeBWorK::DB($ce->{dbLayouts}{$dbLayoutName}); + my $db = new WeBWorK::DB($ce->{dbLayout}); eval { if (exists($options{courseTitle}) and $options{courseTitle}) { $db->setSettingValue('courseTitle', $options{courseTitle}); @@ -756,8 +736,7 @@ sub deleteCourse { ##### step 1: delete course database (if necessary) ##### - my $dbLayoutName = $ce->{dbLayoutName}; - my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); + my $db = new WeBWorK::DB($ce->{dbLayout}); my $create_db_result = $db->delete_tables; die "$courseID: course database deletion failed.\n" unless $create_db_result; @@ -1002,10 +981,9 @@ sub unarchiveCourse { my $ce2 = WeBWorK::CourseEnvironment->new({ get_SeedCE($ce), courseName => $currCourseID }); # pull out some useful stuff - my $course_dir = $ce2->{courseDirs}{root}; - my $data_dir = $ce2->{courseDirs}{DATA}; - my $dump_dir = "$data_dir/mysqldump"; - my $old_dump_file = "$data_dir/${currCourseID}_mysql.database"; + my $course_dir = $ce2->{courseDirs}{root}; + my $data_dir = $ce2->{courseDirs}{DATA}; + my $dump_dir = "$data_dir/mysqldump"; ##### step 4: restore the database tables ##### @@ -1014,24 +992,9 @@ sub unarchiveCourse { if (-e $dump_dir) { my $db = new WeBWorK::DB($ce2->{dbLayout}); $restore_db_result = $db->restore_tables($dump_dir); - } elsif (-e $old_dump_file) { - my $dbLayoutName = $ce2->{dbLayoutName}; - if (ref getHelperRef("unarchiveCourseHelper", $dbLayoutName)) { - eval { - $restore_db_result = - unarchiveCourseHelper($currCourseID, $ce2, $dbLayoutName, unarchiveDatabasePath => $old_dump_file); - }; - if ($@) { - warn "failed to unarchive course database from dump file '$old_dump_file: $@\n"; - } - } else { - warn "course '$currCourseID' uses dbLayout '$dbLayoutName', which doesn't support " - . "restoring database tables. database tables will not be restored.\n"; - $no_database = 1; - } } else { warn "course '$currCourseID' has no database dump in its data directory " - . "(checked for $dump_dir and $old_dump_file). database tables will not be restored.\n"; + . "(checked for $dump_dir). database tables will not be restored.\n"; $no_database = 1; } @@ -1041,16 +1004,9 @@ sub unarchiveCourse { ##### step 5: delete dump_dir and/or old_dump_file ##### - if (-e $dump_dir) { - _archiveCourse_remove_dump_dir($ce, $dump_dir); - } - if (-e $old_dump_file) { - eval { path($old_dump_file)->remove }; - warn "Failed to unlink course database dump file '$old_dump_file: $@" if $@; - } + _archiveCourse_remove_dump_dir($ce, $dump_dir) if -e $dump_dir; - # Create the html_temp folder (since it isn't included in the - # tarball + # Create the html_temp folder (since it isn't included in the tarball) my $tmpDir = $ce2->{courseDirs}->{html_temp}; if (!-e $tmpDir) { eval { path($tmpDir)->make_path }; @@ -1202,46 +1158,7 @@ sub dbLayoutSQLSources { =cut -################################################################################ -# database helpers -################################################################################ - -=head1 DATABASE-LAYOUT SPECIFIC HELPER FUNCTIONS - -These functions are used to perform database-layout specific operations. - -The implementations in this class do nothing, but if an appropriate function -exists in a class with the name -WeBWorK::Utils::CourseManagement::I<$dbLayoutName>, it will be used instead. - -=over - -=item archiveCourseHelper($courseID, $ce, $dbLayoutName, %options) - -Perform database-layout specific operations for archiving the data in a course. - -=cut - -sub archiveCourseHelper { - my ($courseID, $ce, $dbLayoutName, %options) = @_; - my $result = callHelperIfExists("archiveCourseHelper", $dbLayoutName, @_); - return $result; -} - -=item unarchiveCourseHelper($courseID, $ce, $dbLayoutName, %options) - -Perform database-layout specific operations for unarchiving the data in a course -and placing it in the database. - -=cut - -sub unarchiveCourseHelper { - my ($courseID, $ce, $dbLayoutName, %options) = @_; - my $result = callHelperIfExists("unarchiveCourseHelper", $dbLayoutName, @_); - return $result; -} - -=item initNonNativeTables($ce, $db, $dbLayoutName, %options) +=item initNonNativeTables($ce, $db, %options) Perform database-layout specific operations for initializing non-native database tables that are not associated with a particular course @@ -1251,10 +1168,10 @@ that are not associated with a particular course =cut sub initNonNativeTables { - my ($ce, $dbLayoutName, %options) = @_; + my ($ce, %options) = @_; my @messages; # Create a database handler - my $db = new WeBWorK::DB($ce->{dbLayouts}->{$dbLayoutName}); + my $db = new WeBWorK::DB($ce->{dbLayout}); # lock database @@ -1330,7 +1247,7 @@ called directly. =over -=item callHelperIfExists($helperName, $dbLayoutName, @args) +=item callHelperIfExists($helperName, @args) Call a database-specific helper function, if a database-layout specific helper class exists and contains a function named "${helperName}Helper". @@ -1338,9 +1255,9 @@ class exists and contains a function named "${helperName}Helper". =cut sub callHelperIfExists { - my ($helperName, $dbLayoutName, @args) = @_; + my ($helperName, @args) = @_; - my $helperRef = getHelperRef($helperName, $dbLayoutName); + my $helperRef = getHelperRef($helperName); if (ref $helperRef) { return $helperRef->(@args); } else { @@ -1348,43 +1265,6 @@ sub callHelperIfExists { } } -=item getHelperRef($helperName, $dbLayoutName) - -Call a database-specific helper function, if a database-layout specific helper -class exists and contains a function named "${helperName}Helper". - -=cut - -sub getHelperRef { - my ($helperName, $dbLayoutName) = @_; - - my $result; - - my $package = __PACKAGE__ . "::$dbLayoutName"; - - eval { runtime_use $package }; - if ($@) { - if ($@ =~ /^Can't locate/) { - debug("No database-layout specific library for layout '$dbLayoutName'.\n"); - $result = 1; - } else { - warn "Failed to load database-layout specific library: $@\n"; - $result = 0; - } - } else { - my %syms = do { no strict 'refs'; %{ $package . "::" } }; - if (exists $syms{$helperName}) { - $result = do { no strict 'refs'; \&{ $package . "::" . $helperName } }; - } else { - debug("No helper defined for operation '$helperName'.\n"); - $result = 1; - } - } - - #warn "getHelperRef = '$result'\n"; - return $result; -} - =item protectQString($string) Protects the contents of a single-quoted Perl string. @@ -1410,9 +1290,6 @@ the pairs accepted in %courseOptions by addCourse(), above. sub writeCourseConf { my ($fh, $ce, %options) = @_; - # several options should be defined no matter what - $options{dbLayoutName} = $ce->{dbLayoutName} unless defined $options{dbLayoutName}; - print $fh <<'EOF'; #!perl @@ -1420,24 +1297,6 @@ sub writeCourseConf { EOF - print $fh <<'EOF'; -# Database Layout (global value typically defined in defaults.config) -# Several database are defined in the file conf/database.conf and stored in the -# hash %dbLayouts. -# The database layout is always set here, since one should be able to change the -# default value in localOverrides.conf without disrupting existing courses. -# defaults.config values: -EOF - - print $fh "# \t", '$dbLayoutName = \'', protectQString($ce->{dbLayoutName}), '\';', "\n"; - print $fh "# \t", '*dbLayout = $dbLayouts{$dbLayoutName};', "\n"; - - if (defined $options{dbLayoutName}) { - print $fh '$dbLayoutName = \'', protectQString($options{dbLayoutName}), '\';', "\n"; - print $fh '*dbLayout = $dbLayouts{$dbLayoutName};', "\n"; - } - print $fh "\n"; - print $fh <<'EOF'; # Users for whom to label problems with the PG file name (global value typically "professor") # For users in this list, PG will display the source file name when rendering a problem. diff --git a/lib/WeBWorK/Utils/CourseManagement/sql_moodle.pm b/lib/WeBWorK/Utils/CourseManagement/sql_moodle.pm deleted file mode 100644 index 007a35a1e0..0000000000 --- a/lib/WeBWorK/Utils/CourseManagement/sql_moodle.pm +++ /dev/null @@ -1,33 +0,0 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2023 The WeBWorK Project, https://github.com/openwebwork -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -package WeBWorK::Utils::CourseManagement::sql_moodle; - -=head1 NAME - -WeBWorK::Utils::CourseManagement::sql_moodle - create and delete courses using -the sql_moodle database layout. Delegates functionality to -WeBWorK::Utils::CourseManagement::sql_single. - -=cut - -use strict; -use warnings; -use WeBWorK::Utils::CourseManagement::sql_single; - -*archiveCourseHelper = \&WeBWorK::Utils::CourseManagement::sql_single::archiveCourseHelper; -*unarchiveCourseHelper = \&WeBWorK::Utils::CourseManagement::sql_single::unarchiveCourseHelper; - -1; diff --git a/lib/WeBWorK/Utils/CourseManagement/sql_single.pm b/lib/WeBWorK/Utils/CourseManagement/sql_single.pm deleted file mode 100644 index f79b8071e0..0000000000 --- a/lib/WeBWorK/Utils/CourseManagement/sql_single.pm +++ /dev/null @@ -1,275 +0,0 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2023 The WeBWorK Project, https://github.com/openwebwork -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -package WeBWorK::Utils::CourseManagement::sql_single; - -=head1 NAME - -WeBWorK::Utils::CourseManagement::sql_single - create and delete courses using -the sql_single database layout. - -=cut - -use strict; -use warnings; -#use Data::Dumper; -#use DBI; -use File::Temp; -use String::ShellQuote; -use WeBWorK::Debug; -use WeBWorK::Utils qw/runtime_use/; -#use WeBWorK::Utils::CourseManagement qw/dbLayoutSQLSources/; - -=for comment - -# DBFIXME this whole process should be through an abstraction layer -# DBFIXME (we shouldn't be calling mysqldump here -sub archiveCourseHelper { - my ($courseID, $ce, $dbLayoutName, %options) = @_; - debug("courseID=$courseID, ce=$ce dbLayoutName=$dbLayoutName\n"); - - ##### get list of tables to archive ##### - - my $dbLayout = $ce->{dbLayouts}->{$dbLayoutName}; - debug("dbLayout=$dbLayout\n"); - my %sources = dbLayoutSQLSources($dbLayout); - debug("fSources: ", Dumper(\%sources)); - my $source = mostPopularSource(%sources); - debug("source=$source\n"); - my %source = %{ $sources{$source} }; - my @tables = @{ $source{tables} }; - my $username = $source{username}; - my $password = $source{password}; - my $archiveDatabasePath = $options{archiveDatabasePath}; - - ##### construct SQL statements to copy the data in each table ##### - - my @stmts; - my @dataTables = (); - foreach my $table (@tables) { - debug("Table: $table\n"); - - if ($dbLayout->{$table}{params}{non_native}) { - debug("$table: marked non-native, skipping\n"); - next; - } - - my $table = do { - my $paramsRef = $dbLayout->{$table}->{params}; - if ($paramsRef) { - if (exists $paramsRef->{tableOverride}) { - $paramsRef->{tableOverride} - } else { - ""; # no override - } - } else { - ""; # no params - } - } || $table; - debug("sql \"real\" table name: $table\n"); - - - # this method would be mysql specific but it's a start - # mysqldump --user=$username --password=$password database tables -# my $stmt = "DUMP SELECT * FROM `$fromTable`"; -# debug("stmt = $stmt\n"); -# push @stmts, $stmt; - push @dataTables, $table; - } - debug("Database tables to export are ",join(" ", @dataTables)); - # this method would be mysql specific but it's a start - my $mysqldumpCommand = $ce->{externalPrograms}{mysqldump}; - my $exportStatement = " $mysqldumpCommand --user=$username ". - "--password=$password " . - " webwork ". - join(" ", @dataTables). - " >$archiveDatabasePath"; - debug($exportStatement); - my $exportResult = system $exportStatement; - $exportResult and die "Failed to export database with command: '$exportStatement ' (errno: $exportResult): $! - \n\n Check server error log for more information."; - - ##### issue SQL statements ##### - -# my $dbh = DBI->connect($source, $username, $password); -# unless (defined $dbh) { -# die "sql_single: failed to connect to DBI source '$source': $DBI::errstr\n"; -# } -# -# foreach my $stmt (@stmts) { -# my $rows = $dbh->do($stmt); -# unless (defined $rows) { -# die "sql_single: failed to execute SQL statement '$stmt': $DBI::errstr\n"; -# } -# } -# -# $dbh->disconnect; - - return 1; -} - -=cut - -=for comment - -# DBFIXME this whole process should be through an abstraction layer -# DBFIXME (we shouldn't be calling mysqldump here!) -sub unarchiveCourseHelper { - my ($courseID, $ce, $dbLayoutName, %options) = @_; - debug("courseID=$courseID, ce=$ce dbLayoutName=$dbLayoutName\n"); - - ##### get list of tables to archive ##### - - my $dbLayout = $ce->{dbLayouts}->{$dbLayoutName}; - debug("dbLayout=$dbLayout\n"); - my %sources = dbLayoutSQLSources($dbLayout); - debug("fSources: ", Dumper(\%sources)); - my $source = mostPopularSource(%sources); - debug("source=$source\n"); - my %source = %{ $sources{$source} }; - my @tables = @{ $source{tables} }; - my $username = $source{username}; - my $password = $source{password}; - my $unarchiveDatabasePath = $options{unarchiveDatabasePath}; - debug( "unarchive database Path is $unarchiveDatabasePath"); - ##### construct SQL statements to copy the data in each table ##### - - - # this method would be mysql specific but it's a start - my $mysqlCommand = $ce->{externalPrograms}{mysql}; - my $importStatement = " $mysqlCommand --user=$username ". - "--password=$password " . - "-D webwork". # specifies database name - " <$unarchiveDatabasePath"; - debug($importStatement); - my $importResult = system $importStatement; - $importResult and die "
Failed to import database with command: \n
-	'$importStatement ' \n
-	(errno: $importResult): $!
-	\n Check server error log for more information.\n
"; - #FIXME -- what should the return be?? - return 1; -} - -=cut - -# TOTALLY STOLEN FROM NewSQL::Std. -sub unarchiveCourseHelper { - my ($courseID, $ce, $dbLayoutName, %options) = @_; - my $dumpfile_path = $options{unarchiveDatabasePath}; - - my ($my_cnf, $database) = _get_db_info($ce); - my $mysql = $ce->{externalPrograms}{mysql}; - - my $restore_cmd = "2>&1 " - . shell_quote($mysql) - . " --defaults-extra-file=" - . shell_quote($my_cnf->filename) . " " - . shell_quote($database) . " < " - . shell_quote($dumpfile_path); - my $restore_out = readpipe $restore_cmd; - if ($?) { - my $exit = $? >> 8; - my $signal = $? & 127; - my $core = $? & 128; - die - "Failed to restore database for course '$courseID' with command '$restore_cmd' (exit=$exit signal=$signal core=$core): $restore_out\n"; - } - - return 1; -} - -# TOTALLY STOLEN FROM NewSQL::Std. -sub _get_db_info { - my ($ce) = @_; - my $dsn = $ce->{database_dsn}; - my $username = $ce->{database_username}; - my $password = $ce->{database_password}; - - my %dsn; - if ($dsn =~ m/^dbi:mariadb:/i || $dsn =~ m/^dbi:mysql:/i) { - # Expect DBI:MariaDB:database=webwork;host=db;port=3306 - # or DBI:mysql:database=webwork;host=db;port=3306 - # The host and port are optional. - my ($dbi, $dbtype, $dsn_opts) = split(':', $dsn); - while (length($dsn_opts)) { - if ($dsn_opts =~ /^([^=]*)=([^;]*);(.*)$/) { - $dsn{$1} = $2; - $dsn_opts = $3; - } else { - my ($var, $val) = $dsn_opts =~ /^([^=]*)=([^;]*)$/; - $dsn{$var} = $val; - $dsn_opts = ''; - } - } - } else { - die "Can't call dump_table or restore_table on a table with a non-MySQL/MariaDB source"; - } - - die "no database specified in DSN!" unless defined $dsn{database}; - - my $mysqldump = $self->{params}{mysqldump_path}; -# Conditionally add column-statistics=0 as MariaDB databases do not support it -# see: https://serverfault.com/questions/912162/mysqldump-throws-unknown-table-column-statistics-in-information-schema-1109 -# https://github.com/drush-ops/drush/issues/4410 - - my $column_statistics_off = ""; - my $test_for_column_statistics = `$mysqldump_command --help | grep 'column-statistics'`; - if ($test_for_column_statistics) { - $column_statistics_off = "[mysqldump]\ncolumn-statistics=0\n"; - #warn "Setting in the temporary mysql config file for table dump/restore:\n$column_statistics_off\n\n"; - } - - # doing this securely is kind of a hassle... - my $my_cnf = new File::Temp; - $my_cnf->unlink_on_destroy(1); - chmod 0600, $my_cnf or die "failed to chmod 0600 $my_cnf: $!"; # File::Temp objects stringify with ->filename - print $my_cnf "[client]\n"; - print $my_cnf "user=$username\n" if defined $username and length($username) > 0; - print $my_cnf "password=$password\n" if defined $password and length($password) > 0; - print $my_cnf "host=$dsn{host}\n" if defined $dsn{host} and length($dsn{host}) > 0; - print $my_cnf "port=$dsn{port}\n" if defined $dsn{port} and length($dsn{port}) > 0; - print $my_cnf "$column_statistics_off" if $test_for_column_statistics; - - return ($my_cnf, $dsn{database}); -} - -=for comment - -# returns the name of the source with the most tables -sub mostPopularSource { - my (%sources) = @_; - - my $source; - if (keys %sources > 1) { - # more than one -- warn and select the most popular source - debug("more than one SQL source defined.\n"); - foreach my $curr (keys %sources) { - $source = $curr if not defined $source or @{ $sources{$curr}->{tables} } > @{ $sources{$source}->{tables} }; - } - debug("only handling tables with source \"$source\".\n"); - debug("others will have to be handled manually (or not at all).\n"); - } else { - # there's only one - ($source) = keys %sources; - } - - return $source; -} - -=cut - -1; - diff --git a/lib/WebworkWebservice/CourseActions.pm b/lib/WebworkWebservice/CourseActions.pm index dc3dd81a17..4130a82e58 100644 --- a/lib/WebworkWebservice/CourseActions.pm +++ b/lib/WebworkWebservice/CourseActions.pm @@ -63,7 +63,7 @@ sub createCourse { addCourse( courseID => $params->{name}, ce => $ce, - courseOptions => { dbLayoutName => $ce->{dbLayoutName} }, + courseOptions => {}, users => \@users ); addLog($ce, "New course created: $params->{name}"); diff --git a/templates/ContentGenerator/Base/admin_links.html.ep b/templates/ContentGenerator/Base/admin_links.html.ep index 3a242b28c3..33266a8cb1 100644 --- a/templates/ContentGenerator/Base/admin_links.html.ep +++ b/templates/ContentGenerator/Base/admin_links.html.ep @@ -18,7 +18,6 @@ % add_admin_users => 1, % copy_from_course => $ce->{siteDefaults}{default_copy_from_course} || '', % copy_component => 'copyTemplatesHtml', - % add_dbLayout => 'sql_single' % } % ], % [ 'rename_course', maketext('Rename Courses') ], diff --git a/templates/ContentGenerator/CourseAdmin/add_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/add_course_form.html.ep index 5d258f7ab9..b5ece298bc 100644 --- a/templates/ContentGenerator/CourseAdmin/add_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/add_course_form.html.ep @@ -189,6 +189,5 @@ - <%= hidden_field add_dbLayout => 'sql_single' =%> <%= submit_button maketext('Add Course'), name => 'add_course', class => 'btn btn-primary' =%> <% end =%> diff --git a/templates/ContentGenerator/CourseAdmin/upgrade_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/upgrade_course_form.html.ep index 2219923131..34b7c1cf0d 100644 --- a/templates/ContentGenerator/CourseAdmin/upgrade_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/upgrade_course_form.html.ep @@ -42,7 +42,6 @@ % } <%= link_to $courseID => 'set_list' => { courseID => $courseID } =%> - <%= $tempCE->{dbLayoutName} %> % if (!$directories_ok) { <%= maketext('Directory structure or permissions need to be repaired.') =%> From 58fa953d561d9e0325c63758ebdfb52a66a5251f Mon Sep 17 00:00:00 2001 From: Peter Staab Date: Fri, 22 Mar 2024 07:00:46 -0400 Subject: [PATCH 3/3] Fix merge conflicts. --- conf/webwork2.mojolicious.dist.yml | 14 +++++++------- lib/WeBWorK/CourseEnvironment.pm | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/conf/webwork2.mojolicious.dist.yml b/conf/webwork2.mojolicious.dist.yml index 67fa3c1260..ad16075615 100644 --- a/conf/webwork2.mojolicious.dist.yml +++ b/conf/webwork2.mojolicious.dist.yml @@ -240,6 +240,13 @@ hardcopy: # If 1, don't delete temporary files created when a hardcopy is generated. preserve_temp_files: 0 +# Set this to 1 to allow the html2xml and render_rpc endpoints to disable +# cookies and thus skip two factor authentication. This should never be enabled +# for a typical webwork server. This should only be enabled if you want to +# allow serving content via these endpoints to links in external websites with +# usernames and passwords embedded in them such as for PreTeXt textbooks. +allow_unsecured_rpc: 0 + # External Programs # These applications are often found in /bin, but sometimes in /usr/bin or even in /opt/local/bin. @@ -327,10 +334,3 @@ database: # Reference: https://dev.mysql.com/doc/refman/8.0/en/identifier-length.html maxCourseIdLength: 40 - -# Set this to 1 to allow the html2xml and render_rpc endpoints to disable -# cookies and thus skip two factor authentication. This should never be enabled -# for a typical webwork server. This should only be enabled if you want to -# allow serving content via these endpoints to links in external websites with -# usernames and passwords embedded in them such as for PreTeXt textbooks. -allow_unsecured_rpc: 0 diff --git a/lib/WeBWorK/CourseEnvironment.pm b/lib/WeBWorK/CourseEnvironment.pm index a9cc15b889..9cbcc93575 100644 --- a/lib/WeBWorK/CourseEnvironment.pm +++ b/lib/WeBWorK/CourseEnvironment.pm @@ -421,7 +421,6 @@ sub set_server_settings { return; } -=back =head2 two_factor_authentication_enabled Usage: C<< $ce->two_factor_authentication_enabled >>