Merge branch 'MDL-71337-master' of git://github.com/junpataleta/moodle
authorSara Arjona <sara@moodle.com>
Thu, 15 Apr 2021 06:30:08 +0000 (08:30 +0200)
committerSara Arjona <sara@moodle.com>
Thu, 15 Apr 2021 06:30:08 +0000 (08:30 +0200)
277 files changed:
.eslintignore
.github/workflows/push.yml
.stylelintignore
.travis.yml
admin/purgecaches.php
admin/tool/oauth2/issuers.php
admin/tool/oauth2/lang/en/tool_oauth2.php
admin/tool/oauth2/tests/behat/basic_settings.feature
badges/backpack-connect.php
badges/classes/oauth2/client.php
badges/renderer.php
contentbank/amd/build/upload.min.js [new file with mode: 0644]
contentbank/amd/build/upload.min.js.map [new file with mode: 0644]
contentbank/amd/src/upload.js [new file with mode: 0644]
contentbank/classes/form/upload_files.php [new file with mode: 0644]
contentbank/files_form.php [deleted file]
contentbank/index.php
contentbank/templates/bankcontent/toolbar.mustache
contentbank/upgrade.txt
contentbank/upload.php [deleted file]
contentbank/view.php
grade/grading/form/guide/db/services.php
grade/grading/form/rubric/db/services.php
grade/grading/manage.php
grade/report/user/externallib.php
lib/adodb/LICENSE.md
lib/adodb/README.md
lib/adodb/adodb-active-record.inc.php
lib/adodb/adodb-active-recordx.inc.php
lib/adodb/adodb-csvlib.inc.php
lib/adodb/adodb-datadict.inc.php
lib/adodb/adodb-error.inc.php
lib/adodb/adodb-errorhandler.inc.php
lib/adodb/adodb-errorpear.inc.php
lib/adodb/adodb-exceptions.inc.php
lib/adodb/adodb-iterator.inc.php
lib/adodb/adodb-lib.inc.php
lib/adodb/adodb-loadbalancer.inc.php [new file with mode: 0644]
lib/adodb/adodb-memcache.lib.inc.php
lib/adodb/adodb-pager.inc.php
lib/adodb/adodb-pear.inc.php
lib/adodb/adodb-perf.inc.php
lib/adodb/adodb-php4.inc.php
lib/adodb/adodb-time.inc.php
lib/adodb/adodb-xmlschema.inc.php
lib/adodb/adodb-xmlschema03.inc.php
lib/adodb/adodb.inc.php
lib/adodb/datadict/datadict-access.inc.php
lib/adodb/datadict/datadict-db2.inc.php
lib/adodb/datadict/datadict-firebird.inc.php
lib/adodb/datadict/datadict-generic.inc.php
lib/adodb/datadict/datadict-ibase.inc.php
lib/adodb/datadict/datadict-informix.inc.php
lib/adodb/datadict/datadict-mssql.inc.php
lib/adodb/datadict/datadict-mssqlnative.inc.php
lib/adodb/datadict/datadict-mysql.inc.php
lib/adodb/datadict/datadict-oci8.inc.php
lib/adodb/datadict/datadict-postgres.inc.php
lib/adodb/datadict/datadict-sapdb.inc.php
lib/adodb/datadict/datadict-sqlite.inc.php
lib/adodb/datadict/datadict-sybase.inc.php
lib/adodb/drivers/adodb-access.inc.php
lib/adodb/drivers/adodb-ado.inc.php
lib/adodb/drivers/adodb-ado5.inc.php
lib/adodb/drivers/adodb-ado_access.inc.php
lib/adodb/drivers/adodb-ado_mssql.inc.php
lib/adodb/drivers/adodb-ads.inc.php
lib/adodb/drivers/adodb-borland_ibase.inc.php
lib/adodb/drivers/adodb-csv.inc.php
lib/adodb/drivers/adodb-db2.inc.php
lib/adodb/drivers/adodb-db2oci.inc.php
lib/adodb/drivers/adodb-db2ora.inc.php
lib/adodb/drivers/adodb-fbsql.inc.php
lib/adodb/drivers/adodb-firebird.inc.php
lib/adodb/drivers/adodb-ibase.inc.php
lib/adodb/drivers/adodb-informix.inc.php
lib/adodb/drivers/adodb-informix72.inc.php
lib/adodb/drivers/adodb-ldap.inc.php
lib/adodb/drivers/adodb-mssql.inc.php
lib/adodb/drivers/adodb-mssql_n.inc.php
lib/adodb/drivers/adodb-mssqlnative.inc.php
lib/adodb/drivers/adodb-mssqlpo.inc.php
lib/adodb/drivers/adodb-mysql.inc.php
lib/adodb/drivers/adodb-mysqli.inc.php
lib/adodb/drivers/adodb-mysqlpo.inc.php
lib/adodb/drivers/adodb-mysqlt.inc.php
lib/adodb/drivers/adodb-netezza.inc.php
lib/adodb/drivers/adodb-oci8.inc.php
lib/adodb/drivers/adodb-oci805.inc.php
lib/adodb/drivers/adodb-oci8po.inc.php
lib/adodb/drivers/adodb-oci8quercus.inc.php
lib/adodb/drivers/adodb-odbc.inc.php
lib/adodb/drivers/adodb-odbc_db2.inc.php
lib/adodb/drivers/adodb-odbc_mssql.inc.php
lib/adodb/drivers/adodb-odbc_mssql2012.inc.php [new file with mode: 0644]
lib/adodb/drivers/adodb-odbc_oracle.inc.php
lib/adodb/drivers/adodb-odbtp.inc.php
lib/adodb/drivers/adodb-odbtp_unicode.inc.php
lib/adodb/drivers/adodb-oracle.inc.php
lib/adodb/drivers/adodb-pdo.inc.php
lib/adodb/drivers/adodb-pdo_dblib.inc.php [new file with mode: 0644]
lib/adodb/drivers/adodb-pdo_firebird.inc.php [new file with mode: 0644]
lib/adodb/drivers/adodb-pdo_mssql.inc.php
lib/adodb/drivers/adodb-pdo_mysql.inc.php
lib/adodb/drivers/adodb-pdo_oci.inc.php
lib/adodb/drivers/adodb-pdo_pgsql.inc.php
lib/adodb/drivers/adodb-pdo_sqlite.inc.php
lib/adodb/drivers/adodb-pdo_sqlsrv.inc.php
lib/adodb/drivers/adodb-postgres.inc.php
lib/adodb/drivers/adodb-postgres64.inc.php
lib/adodb/drivers/adodb-postgres7.inc.php
lib/adodb/drivers/adodb-postgres8.inc.php
lib/adodb/drivers/adodb-postgres9.inc.php
lib/adodb/drivers/adodb-proxy.inc.php
lib/adodb/drivers/adodb-sapdb.inc.php
lib/adodb/drivers/adodb-sqlanywhere.inc.php
lib/adodb/drivers/adodb-sqlite.inc.php
lib/adodb/drivers/adodb-sqlite3.inc.php
lib/adodb/drivers/adodb-sqlitepo.inc.php
lib/adodb/drivers/adodb-sybase.inc.php
lib/adodb/drivers/adodb-sybase_ase.inc.php
lib/adodb/drivers/adodb-text.inc.php [new file with mode: 0644]
lib/adodb/drivers/adodb-vfp.inc.php
lib/adodb/perf/perf-db2.inc.php
lib/adodb/perf/perf-informix.inc.php
lib/adodb/perf/perf-mssql.inc.php
lib/adodb/perf/perf-mssqlnative.inc.php
lib/adodb/perf/perf-mysql.inc.php
lib/adodb/perf/perf-oci8.inc.php
lib/adodb/perf/perf-postgres.inc.php
lib/adodb/pivottable.inc.php
lib/adodb/readme_moodle.txt
lib/adodb/rsfilter.inc.php
lib/adodb/toexport.inc.php
lib/adodb/tohtml.inc.php
lib/adodb/xmlschema03.dtd
lib/amd/build/toast.min.js.map
lib/amd/src/toast.js
lib/classes/event/base.php
lib/classes/navigation/views/primary.php [new file with mode: 0644]
lib/classes/oauth2/api.php
lib/classes/oauth2/issuer.php
lib/classes/oauth2/service/facebook.php [new file with mode: 0644]
lib/classes/oauth2/service/imsobv2p1.php
lib/classes/oauth2/service/microsoft.php [new file with mode: 0644]
lib/classes/oauth2/service/nextcloud.php [new file with mode: 0644]
lib/db/services.php
lib/filelib.php
lib/pagelib.php
lib/templates/local/toast/message.mustache
lib/tests/navigation/views/primary_test.php [new file with mode: 0644]
lib/thirdpartylibs.xml
lib/upgrade.txt
lib/upgradelib.php
media/player/videojs/db/services.php
message/output/airnotifier/checkconfiguration.php [new file with mode: 0644]
message/output/airnotifier/classes/manager.php
message/output/airnotifier/lang/en/message_airnotifier.php
message/output/airnotifier/settings.php
message/output/airnotifier/tests/manager_test.php [new file with mode: 0644]
mod/glossary/db/services.php
mod/h5pactivity/db/services.php
mod/workshop/lang/en/workshop.php
mod/workshop/tests/behat/workshop_assessment.feature
mod/workshop/view.php
payment/gateway/paypal/db/services.php
repository/googledocs/classes/googledocs_content.php [new file with mode: 0644]
repository/googledocs/classes/googledocs_content_search.php [new file with mode: 0644]
repository/googledocs/classes/helper.php [new file with mode: 0644]
repository/googledocs/classes/local/browser/googledocs_drive_content.php [new file with mode: 0644]
repository/googledocs/classes/local/browser/googledocs_root_content.php [new file with mode: 0644]
repository/googledocs/classes/local/browser/googledocs_shared_drives_content.php [new file with mode: 0644]
repository/googledocs/classes/local/node/file_node.php [new file with mode: 0644]
repository/googledocs/classes/local/node/folder_node.php [new file with mode: 0644]
repository/googledocs/classes/local/node/node.php [new file with mode: 0644]
repository/googledocs/classes/rest.php
repository/googledocs/lang/en/repository_googledocs.php
repository/googledocs/lib.php
repository/googledocs/tests/googledocs_content_testcase.php [new file with mode: 0644]
repository/googledocs/tests/googledocs_search_content_test.php [new file with mode: 0644]
repository/googledocs/tests/helper_test.php [new file with mode: 0644]
repository/googledocs/tests/local/browser/googledocs_drive_content_test.php [new file with mode: 0644]
repository/googledocs/tests/local/browser/googledocs_root_content_test.php [new file with mode: 0644]
repository/googledocs/tests/local/browser/googledocs_shared_drives_content_test.php [new file with mode: 0644]
repository/googledocs/tests/local/node/file_node_test.php [new file with mode: 0644]
repository/googledocs/tests/local/node/folder_node_test.php [new file with mode: 0644]
repository/googledocs/tests/repository_googledocs_testcase.php [new file with mode: 0644]
repository/googledocs/version.php
repository/upgrade.txt
theme/boost/amd/build/bootstrap/alert.min.js
theme/boost/amd/build/bootstrap/alert.min.js.map
theme/boost/amd/build/bootstrap/button.min.js
theme/boost/amd/build/bootstrap/button.min.js.map
theme/boost/amd/build/bootstrap/carousel.min.js
theme/boost/amd/build/bootstrap/carousel.min.js.map
theme/boost/amd/build/bootstrap/collapse.min.js
theme/boost/amd/build/bootstrap/collapse.min.js.map
theme/boost/amd/build/bootstrap/dropdown.min.js
theme/boost/amd/build/bootstrap/dropdown.min.js.map
theme/boost/amd/build/bootstrap/index.min.js [deleted file]
theme/boost/amd/build/bootstrap/index.min.js.map [deleted file]
theme/boost/amd/build/bootstrap/modal.min.js
theme/boost/amd/build/bootstrap/modal.min.js.map
theme/boost/amd/build/bootstrap/popover.min.js
theme/boost/amd/build/bootstrap/popover.min.js.map
theme/boost/amd/build/bootstrap/scrollspy.min.js
theme/boost/amd/build/bootstrap/scrollspy.min.js.map
theme/boost/amd/build/bootstrap/tab.min.js
theme/boost/amd/build/bootstrap/tab.min.js.map
theme/boost/amd/build/bootstrap/toast.min.js
theme/boost/amd/build/bootstrap/toast.min.js.map
theme/boost/amd/build/bootstrap/tools/sanitizer.min.js.map
theme/boost/amd/build/bootstrap/tooltip.min.js
theme/boost/amd/build/bootstrap/tooltip.min.js.map
theme/boost/amd/build/bootstrap/util.min.js.map
theme/boost/amd/build/index.min.js [new file with mode: 0644]
theme/boost/amd/build/index.min.js.map [new file with mode: 0644]
theme/boost/amd/build/loader.min.js
theme/boost/amd/build/loader.min.js.map
theme/boost/amd/src/bootstrap/alert.js
theme/boost/amd/src/bootstrap/button.js
theme/boost/amd/src/bootstrap/carousel.js
theme/boost/amd/src/bootstrap/collapse.js
theme/boost/amd/src/bootstrap/dropdown.js
theme/boost/amd/src/bootstrap/index.js [deleted file]
theme/boost/amd/src/bootstrap/modal.js
theme/boost/amd/src/bootstrap/popover.js
theme/boost/amd/src/bootstrap/scrollspy.js
theme/boost/amd/src/bootstrap/tab.js
theme/boost/amd/src/bootstrap/toast.js
theme/boost/amd/src/bootstrap/tools/sanitizer.js
theme/boost/amd/src/bootstrap/tooltip.js
theme/boost/amd/src/bootstrap/util.js
theme/boost/amd/src/index.js [new file with mode: 0644]
theme/boost/amd/src/loader.js
theme/boost/readme_moodle.txt
theme/boost/scss/bootstrap/_alert.scss
theme/boost/scss/bootstrap/_breadcrumb.scss
theme/boost/scss/bootstrap/_card.scss
theme/boost/scss/bootstrap/_carousel.scss
theme/boost/scss/bootstrap/_custom-forms.scss
theme/boost/scss/bootstrap/_dropdown.scss
theme/boost/scss/bootstrap/_functions.scss
theme/boost/scss/bootstrap/_grid.scss
theme/boost/scss/bootstrap/_input-group.scss
theme/boost/scss/bootstrap/_list-group.scss
theme/boost/scss/bootstrap/_modal.scss
theme/boost/scss/bootstrap/_nav.scss
theme/boost/scss/bootstrap/_navbar.scss
theme/boost/scss/bootstrap/_pagination.scss
theme/boost/scss/bootstrap/_progress.scss
theme/boost/scss/bootstrap/_reboot.scss
theme/boost/scss/bootstrap/_root.scss
theme/boost/scss/bootstrap/_spinners.scss
theme/boost/scss/bootstrap/_toasts.scss
theme/boost/scss/bootstrap/_type.scss
theme/boost/scss/bootstrap/_variables.scss
theme/boost/scss/bootstrap/bootstrap-grid.scss
theme/boost/scss/bootstrap/bootstrap-reboot.scss
theme/boost/scss/bootstrap/bootstrap.scss
theme/boost/scss/bootstrap/mixins/_border-radius.scss
theme/boost/scss/bootstrap/mixins/_forms.scss
theme/boost/scss/bootstrap/mixins/_grid-framework.scss
theme/boost/scss/bootstrap/mixins/_grid.scss
theme/boost/scss/bootstrap/mixins/_image.scss
theme/boost/scss/bootstrap/mixins/_screen-reader.scss
theme/boost/scss/bootstrap/mixins/_transition.scss
theme/boost/scss/bootstrap/utilities/_borders.scss
theme/boost/scss/bootstrap/utilities/_text.scss
theme/boost/scss/moodle.scss
theme/boost/scss/moodle/toasts.scss [new file with mode: 0644]
theme/boost/scss/preset/default.scss
theme/boost/style/moodle.css
theme/boost/thirdpartylibs.xml
theme/classic/scss/preset/default.scss
theme/classic/style/moodle.css
version.php

index a6d5413..2262b1f 100644 (file)
@@ -84,7 +84,6 @@ theme/boost/amd/src/bootstrap/button.js
 theme/boost/amd/src/bootstrap/carousel.js
 theme/boost/amd/src/bootstrap/collapse.js
 theme/boost/amd/src/bootstrap/dropdown.js
-theme/boost/amd/src/bootstrap/index.js
 theme/boost/amd/src/bootstrap/modal.js
 theme/boost/amd/src/bootstrap/popover.js
 theme/boost/amd/src/bootstrap/tools/sanitizer.js
@@ -93,4 +92,5 @@ theme/boost/amd/src/bootstrap/tab.js
 theme/boost/amd/src/bootstrap/toast.js
 theme/boost/amd/src/bootstrap/tooltip.js
 theme/boost/amd/src/bootstrap/util.js
-theme/boost/scss/fontawesome/
\ No newline at end of file
+theme/boost/amd/src/index.js
+theme/boost/scss/fontawesome/
index 35fd4a7..0c4f1ba 100644 (file)
@@ -1,6 +1,12 @@
 name: Core
 
-on: [push]
+on:
+  push:
+    branches-ignore:
+      - master
+      - MOODLE_[0-9]+_STABLE
+    tags-ignore:
+      - v[0-9]+.[0-9]+.[0-9]+*
 
 env:
   php: 7.4
index 0207e41..2d28f80 100644 (file)
@@ -84,7 +84,6 @@ theme/boost/amd/src/bootstrap/button.js
 theme/boost/amd/src/bootstrap/carousel.js
 theme/boost/amd/src/bootstrap/collapse.js
 theme/boost/amd/src/bootstrap/dropdown.js
-theme/boost/amd/src/bootstrap/index.js
 theme/boost/amd/src/bootstrap/modal.js
 theme/boost/amd/src/bootstrap/popover.js
 theme/boost/amd/src/bootstrap/tools/sanitizer.js
@@ -93,4 +92,5 @@ theme/boost/amd/src/bootstrap/tab.js
 theme/boost/amd/src/bootstrap/toast.js
 theme/boost/amd/src/bootstrap/tooltip.js
 theme/boost/amd/src/bootstrap/util.js
-theme/boost/scss/fontawesome/
\ No newline at end of file
+theme/boost/amd/src/index.js
+theme/boost/scss/fontawesome/
index 0c565f3..6d52469 100644 (file)
@@ -19,6 +19,12 @@ services:
 addons:
   postgresql: "9.6"
 
+branches:
+  except:
+    - master
+    - /MOODLE_[0-9]+_STABLE/
+    - /^v[0-9]+\.[0-9]+\.[0-9]+.*/
+
 jobs:
     # Enable fast finish.
     # This will fail the build if a single job fails (except those in allow_failures).
index b4401c4..b43aa77 100644 (file)
@@ -52,8 +52,14 @@ if ($data = $form->get_data()) {
     $message = get_string('purgecachesfinished', 'admin');
 }
 
+// Redirect and/or show notification message confirming cache(s) were purged.
 if (isset($message)) {
-    redirect($returnurl, $message);
+    if (!$PAGE->url->compare($returnurl, URL_MATCH_BASE)) {
+        redirect($returnurl, $message);
+    }
+
+    // We are already on the purge caches page, add the notification.
+    \core\notification::add($message, \core\output\notification::NOTIFY_INFO);
 }
 
 // Otherwise, show a form to actually purge the caches.
index cc5da40..6383ef2 100644 (file)
@@ -53,7 +53,7 @@ if ($action == 'edit') {
     if ($issuer) {
         $PAGE->navbar->add(get_string('editissuer', 'tool_oauth2', s($issuer->get('name'))));
     } else {
-        $PAGE->navbar->add(get_string('createnewservice', 'tool_oauth2') . get_string('custom_service', 'tool_oauth2'));
+        $PAGE->navbar->add(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string('custom_service', 'tool_oauth2'));
     }
 
     $showrequireconfirm = false;
@@ -96,7 +96,7 @@ if ($mform && $mform->is_cancelled()) {
         if ($issuer) {
             echo $OUTPUT->heading(get_string('editissuer', 'tool_oauth2', s($issuer->get('name'))));
         } else {
-            echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . get_string('custom_service', 'tool_oauth2'));
+            echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string('custom_service', 'tool_oauth2'));
         }
         $mform->display();
         echo $OUTPUT->footer();
@@ -115,7 +115,7 @@ if ($mform && $mform->is_cancelled()) {
         redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
     } else {
         echo $OUTPUT->header();
-        echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . get_string($type . '_service', 'tool_oauth2'));
+        echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string($type . '_service', 'tool_oauth2'));
         $mform->display();
         echo $OUTPUT->footer();
     }
@@ -130,9 +130,9 @@ if ($mform && $mform->is_cancelled()) {
     $mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer, 'type' => $type,
         'showrequireconfirm' => $showrequireconfirm]);
 
-    $PAGE->navbar->add(get_string('createnewservice', 'tool_oauth2') . get_string($type . '_service', 'tool_oauth2'));
+    $PAGE->navbar->add(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string($type . '_service', 'tool_oauth2'));
     echo $OUTPUT->header();
-    echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . get_string($type . '_service', 'tool_oauth2'));
+    echo $OUTPUT->heading(get_string('createnewservice', 'tool_oauth2') . ' ' . get_string($type . '_service', 'tool_oauth2'));
     $mform->display();
     echo $OUTPUT->footer();
 
@@ -199,7 +199,7 @@ if ($mform && $mform->is_cancelled()) {
     echo $renderer->issuers_table($issuers);
 
     echo $renderer->container_start();
-    echo get_string('createnewservice', 'tool_oauth2');
+    echo get_string('createnewservice', 'tool_oauth2') . ' ';
 
     // Google template.
     $docs = 'admin/tool/oauth2/issuers/google';
index 938c576..9ff13af 100644 (file)
@@ -31,7 +31,7 @@ $string['connectsystemaccount'] = 'Connect to a system account';
 $string['createfromtemplate'] = 'Create an OAuth 2 service from a template';
 $string['createfromtemplatedesc'] = 'Choose one of the OAuth 2 service templates below to create an OAuth service with a valid configuration for one of the known service types. This will create the OAuth 2 service, with all the correct end points and parameters required for authentication, though you will still need to enter the client ID and secret for the new service before it can be used.';
 $string['createnewendpoint'] = 'Create new endpoint for issuer "{$a}"';
-$string['createnewservice'] = 'Create new service: ';
+$string['createnewservice'] = 'Create new service:';
 $string['createnewuserfieldmapping'] = 'Create new user field mapping for issuer "{$a}"';
 $string['custom_service'] = 'Custom';
 $string['deleteconfirm'] = 'Are you sure you want to delete the identity issuer "{$a}"? Any plugins relying on this issuer will stop working.';
@@ -53,7 +53,7 @@ $string['endpointurl_help'] = 'URL for this endpoint. Must use https:// protocol
 $string['endpointurl'] = 'URL';
 $string['facebook_service'] = 'Facebook';
 $string['google_service'] = 'Google';
-$string['imsobv2p1_service'] = 'IMS OBv2.1';
+$string['imsobv2p1_service'] = 'OpenBadges';
 $string['issuersetup'] = 'Detailed instructions on configuring the common OAuth 2 services';
 $string['issuersetuptype'] = 'Detailed instructions on setting up the {$a} OAuth 2 provider';
 $string['issueralloweddomains_help'] = 'If set, this setting is a comma separated list of domains that logins will be restricted to when using this provider.';
index 2eb3345..429d371 100644 (file)
@@ -142,29 +142,29 @@ Feature: Basic OAuth2 functionality
     And I should see "Identity issuer deleted"
     And I should not see "Testing service modified"
 
-  Scenario: Create, edit and delete standard service for IMS OBv2.1
-    Given I press "IMS OBv2.1"
-    And I should see "Create new service: IMS OBv2.1"
+  Scenario: Create, edit and delete standard service for OpenBadges
+    Given I press "OpenBadges"
+    And I should see "Create new service: OpenBadges"
     And I set the following fields to these values:
       | Client ID                  | thisistheclientid                         |
       | Client secret              | supersecret                               |
       | Service base URL           | https://dc.imsglobal.org/                 |
     When I press "Save changes"
     Then I should see "Changes saved"
-    And I should see "IMS OBv2.1"
-    And "Configured" "icon" should exist in the "IMS OBv2.1" "table_row"
-    And "Do not allow login" "icon" should exist in the "IMS OBv2.1" "table_row"
-    And "Service discovery successful" "icon" should exist in the "IMS OBv2.1" "table_row"
+    And I should see "OpenBadges"
+    And "Configured" "icon" should exist in the "OpenBadges" "table_row"
+    And "Do not allow login" "icon" should exist in the "OpenBadges" "table_row"
+    And "Service discovery successful" "icon" should exist in the "OpenBadges" "table_row"
     And the "src" attribute of "table.admintable th img" "css_element" should contain "IMS-Global-Logo.png"
-    And I click on "Configure endpoints" "link" in the "IMS OBv2.1" "table_row"
+    And I click on "Configure endpoints" "link" in the "OpenBadges" "table_row"
     And I should see "https://dc.imsglobal.org/.well-known/badgeconnect.json" in the "discovery_endpoint" "table_row"
     And I should see "authorization_endpoint"
     And I follow "OAuth 2 services"
-    And I click on "Configure user field mappings" "link" in the "IMS OBv2.1" "table_row"
+    And I click on "Configure user field mappings" "link" in the "OpenBadges" "table_row"
     And I should not see "given_name"
     And I should not see "middle_name"
     And I follow "OAuth 2 services"
-    And I click on "Edit" "link" in the "IMS OBv2.1" "table_row"
+    And I click on "Edit" "link" in the "OpenBadges" "table_row"
     And I set the following fields to these values:
       | Name                       | IMS Global                                |
     And I press "Save changes"
index ff77f49..0dceb33 100644 (file)
@@ -43,6 +43,10 @@ if ($persistedissuer) {
     $returnurl = new moodle_url('/badges/backpack-connect.php',
         ['action' => 'authorization', 'sesskey' => sesskey(), 'backpackid' => $backpackid]);
 
+    // If scope is not passed as parameter, use the issuer supported scopes.
+    if (empty($scope)) {
+        $scope = $issuer->get('scopessupported');
+    }
     $client = new core_badges\oauth2\client($issuer, $returnurl, $scope, $externalbackpack);
     if ($client) {
         if (!$client->is_logged_in()) {
index 575985b..8e635bc 100644 (file)
@@ -222,6 +222,7 @@ class client extends \core\oauth2\client {
         $callbackurl = self::callback_url();
 
         if ($granttype == 'authorization_code') {
+            $this->basicauth = true;
             $params = array('code' => $code,
                 'grant_type' => $granttype,
                 'redirect_uri' => $callbackurl->out(false),
@@ -236,7 +237,7 @@ class client extends \core\oauth2\client {
             );
         }
         if ($this->basicauth) {
-            $idsecret = urlencode($this->clientid) . ':' . urlencode($this->clientsecret);
+            $idsecret = $this->clientid . ':' . $this->clientsecret;
             $this->setHeader('Authorization: Basic ' . base64_encode($idsecret));
         } else {
             $params['client_id'] = $this->clientid;
@@ -244,11 +245,13 @@ class client extends \core\oauth2\client {
         }
         // Requests can either use http GET or POST.
         $response = $this->post($this->token_url(), $this->build_post_data($params));
-        $r = json_decode($response);
         if ($this->info['http_code'] !== 200) {
-            throw new moodle_exception('Could not upgrade oauth token');
+            $debuginfo = !empty($this->error) ? $this->error : $response;
+            throw new moodle_exception('oauth2refreshtokenerror', 'core_error', '', $this->info['http_code'], $debuginfo);
         }
 
+        $r = json_decode($response);
+
         if (is_null($r)) {
             throw new moodle_exception("Could not decode JSON token response");
         }
index 1afadf4..ba16434 100644 (file)
@@ -350,18 +350,15 @@ class core_badges_renderer extends plugin_renderer_base {
             if (!empty($CFG->badges_allowexternalbackpack) && ($expiration > $now)
                 && $userbackpack = badges_get_user_backpack($USER->id)) {
 
-                $assertion = null;
                 if (badges_open_badges_backpack_api($userbackpack->id) == OPEN_BADGES_V2P1) {
                     $assertion = new moodle_url('/badges/backpack-export.php', array('hash' => $ibadge->hash));
                 } else {
                     $assertion = new moodle_url('/badges/backpack-add.php', array('hash' => $ibadge->hash));
                 }
 
-                if (!is_null(assertion)) {
-                    $attributes = ['class' => 'btn btn-secondary m-1', 'role' => 'button'];
-                    $tobackpack = html_writer::link($assertion, get_string('addtobackpack', 'badges'), $attributes);
-                    $output .= $tobackpack;
-                }
+                $attributes = ['class' => 'btn btn-secondary m-1', 'role' => 'button'];
+                $tobackpack = html_writer::link($assertion, get_string('addtobackpack', 'badges'), $attributes);
+                $output .= $tobackpack;
             }
         }
         $output .= html_writer::end_tag('div');
diff --git a/contentbank/amd/build/upload.min.js b/contentbank/amd/build/upload.min.js
new file mode 100644 (file)
index 0000000..9df927d
Binary files /dev/null and b/contentbank/amd/build/upload.min.js differ
diff --git a/contentbank/amd/build/upload.min.js.map b/contentbank/amd/build/upload.min.js.map
new file mode 100644 (file)
index 0000000..c14d2d7
Binary files /dev/null and b/contentbank/amd/build/upload.min.js.map differ
diff --git a/contentbank/amd/src/upload.js b/contentbank/amd/src/upload.js
new file mode 100644 (file)
index 0000000..fb1f7c8
--- /dev/null
@@ -0,0 +1,52 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle 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 the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Module to handle AJAX interactions with content bank upload files.
+ *
+ * @module     core_contentbank/upload
+ * @copyright  2021 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+import ModalForm from 'core_form/modalform';
+import {get_string as getString} from 'core/str';
+
+/**
+ * Initialize upload files to the content bank form as Modal form.
+ *
+ * @param {String} elementSelector
+ * @param {String} formClass
+ * @param {Integer} contextId
+ * @param {Integer} contentId
+ */
+export const initModal = (elementSelector, formClass, contextId, contentId) => {
+    const element = document.querySelector(elementSelector);
+    element.addEventListener('click', function(e) {
+        e.preventDefault();
+        const form = new ModalForm({
+            formClass,
+            args: {
+                contextid: contextId,
+                id: contentId,
+            },
+            modalConfig: {title: getString('upload', 'contentbank')},
+            returnFocus: e.target,
+        });
+        form.addEventListener(form.events.FORM_SUBMITTED, (event) => {
+            document.location = event.detail.returnurl;
+        });
+        form.show();
+    });
+};
diff --git a/contentbank/classes/form/upload_files.php b/contentbank/classes/form/upload_files.php
new file mode 100644 (file)
index 0000000..91f8d3a
--- /dev/null
@@ -0,0 +1,209 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle 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 the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+namespace core_contentbank\form;
+
+/**
+ * Upload files to content bank form
+ *
+ * @package    core_contentbank
+ * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class upload_files extends \core_form\dynamic_form {
+
+    /**
+     * Add elements to this form.
+     */
+    public function definition() {
+        $mform = $this->_form;
+
+        $mform->addElement('hidden', 'contextid');
+        $mform->setType('contextid', PARAM_INT);
+
+        $mform->addElement('hidden', 'id');
+        $mform->setType('id', PARAM_INT);
+
+        $mform->addElement('filepicker', 'file', get_string('file', 'core_contentbank'), null, $this->get_options());
+        $mform->addHelpButton('file', 'file', 'core_contentbank');
+        $mform->addRule('file', null, 'required');
+    }
+
+    /**
+     * Validate incoming data.
+     *
+     * @param array $data
+     * @param array $files
+     * @return array
+     */
+    public function validation($data, $files) {
+        $errors = array();
+        $draftitemid = $data['file'];
+        $options = $this->get_options();
+        if (file_is_draft_area_limit_reached($draftitemid, $options['areamaxbytes'])) {
+            $errors['file'] = get_string('userquotalimit', 'error');
+        }
+        return $errors;
+    }
+
+    /**
+     * Check if current user has access to this form, otherwise throw exception
+     *
+     * Sometimes permission check may depend on the action and/or id of the entity.
+     * If necessary, form data is available in $this->_ajaxformdata or
+     * by calling $this->optional_param()
+     */
+    protected function check_access_for_dynamic_submission(): void {
+        require_capability('moodle/contentbank:upload', $this->get_context_for_dynamic_submission());
+
+        // Check the context used by the content bank is allowed.
+        $cb = new \core_contentbank\contentbank();
+        if (!$cb->is_context_allowed($this->get_context_for_dynamic_submission())) {
+            throw new \moodle_exception('contextnotallowed', 'core_contentbank');
+        }
+
+        // If $id is defined, the file content will be replaced (instead of uploading a new one).
+        // Check that the user has the right permissions to replace this content file.
+        $id = $this->optional_param('id', null, PARAM_INT);
+        if ($id) {
+            $content = $cb->get_content_from_id($id);
+            $contenttype = $content->get_content_type_instance();
+            if (!$contenttype->can_manage($content) || !$contenttype->can_upload()) {
+                throw new \moodle_exception('nopermissions', 'error', '', null, get_string('replacecontent', 'contentbank'));
+            }
+        }
+    }
+
+    /**
+     * Returns form context
+     *
+     * If context depends on the form data, it is available in $this->_ajaxformdata or
+     * by calling $this->optional_param()
+     *
+     * @return \context
+     */
+    protected function get_context_for_dynamic_submission(): \context {
+        $contextid = $this->optional_param('contextid', null, PARAM_INT);
+        return \context::instance_by_id($contextid, MUST_EXIST);
+    }
+
+    /**
+     * File upload options
+     *
+     * @return array
+     * @throws \coding_exception
+     */
+    protected function get_options(): array {
+        global $CFG;
+
+        $maxbytes = $CFG->userquota;
+        $maxareabytes = $CFG->userquota;
+        if (has_capability('moodle/user:ignoreuserquota', $this->get_context_for_dynamic_submission())) {
+            $maxbytes = USER_CAN_IGNORE_FILE_SIZE_LIMITS;
+            $maxareabytes = FILE_AREA_MAX_BYTES_UNLIMITED;
+        }
+
+        $cb = new \core_contentbank\contentbank();
+        $id = $this->optional_param('id', null, PARAM_INT);
+        if ($id) {
+            $content = $cb->get_content_from_id($id);
+            $contenttype = $content->get_content_type_instance();
+            $extensions = $contenttype->get_manageable_extensions();
+            $acceptedtypes = implode(',', $extensions);
+        } else {
+            $acceptedtypes = $cb->get_supported_extensions_as_string($this->get_context_for_dynamic_submission());
+        }
+
+        return ['subdirs' => 1, 'maxbytes' => $maxbytes, 'maxfiles' => -1, 'accepted_types' => $acceptedtypes,
+            'areamaxbytes' => $maxareabytes];
+    }
+
+    /**
+     * Process the form submission, used if form was submitted via AJAX
+     *
+     * This method can return scalar values or arrays that can be json-encoded, they will be passed to the caller JS.
+     *
+     * Submission data can be accessed as: $this->get_data()
+     *
+     * @return mixed
+     */
+    public function process_dynamic_submission() {
+        global $USER;
+
+        // Get the file and create the content based on it.
+        $usercontext = \context_user::instance($USER->id);
+        $fs = get_file_storage();
+        $files = $fs->get_area_files($usercontext->id, 'user', 'draft', $this->get_data()->file, 'itemid, filepath,
+            filename', false);
+        if (!empty($files)) {
+            $file = reset($files);
+            $cb = new \core_contentbank\contentbank();
+            if ($this->get_data()->id) {
+                $content = $cb->get_content_from_id($this->get_data()->id);
+                $contenttype = $content->get_content_type_instance();
+                $content = $contenttype->replace_content($file, $content);
+            } else {
+                $content = $cb->create_content_from_file($this->get_context_for_dynamic_submission(), $USER->id, $file);
+            }
+            $params = ['id' => $content->get_id(), 'contextid' => $this->get_context_for_dynamic_submission()->id];
+            $viewurl = new \moodle_url('/contentbank/view.php', $params);
+            return ['returnurl' => $viewurl->out(false)];
+        }
+
+        return null;
+    }
+
+    /**
+     * Load in existing data as form defaults
+     *
+     * Can be overridden to retrieve existing values from db by entity id and also
+     * to preprocess editor and filemanager elements
+     *
+     * Example:
+     *     $this->set_data(get_entity($this->_ajaxformdata['id']));
+     */
+    public function set_data_for_dynamic_submission(): void {
+        $data = (object)[
+            'contextid' => $this->optional_param('contextid', null, PARAM_INT),
+            'id' => $this->optional_param('id', null, PARAM_INT),
+        ];
+        $this->set_data($data);
+    }
+
+    /**
+     * Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
+     *
+     * This is used in the form elements sensitive to the page url, such as Atto autosave in 'editor'
+     *
+     * If the form has arguments (such as 'id' of the element being edited), the URL should
+     * also have respective argument.
+     *
+     * @return \moodle_url
+     */
+    protected function get_page_url_for_dynamic_submission(): \moodle_url {
+        $params = ['contextid' => $this->get_context_for_dynamic_submission()->id];
+
+        $id = $this->optional_param('id', null, PARAM_INT);
+        if ($id) {
+            $url = '/contentbank/view.php';
+            $params['id'] = $id;
+        } else {
+            $url = '/contentbank/index.php';
+        }
+
+        return new \moodle_url($url, $params);
+    }
+}
diff --git a/contentbank/files_form.php b/contentbank/files_form.php
deleted file mode 100644 (file)
index 6a001f0..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Upload files to content bank form
- *
- * @package    core_contentbank
- * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-require_once("$CFG->libdir/formslib.php");
-
-/**
- * Class contentbank_files_form
- *
- * @package    core_contentbank
- * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class contentbank_files_form extends moodleform {
-
-    /**
-     * Add elements to this form.
-     */
-    public function definition() {
-        $mform = $this->_form;
-
-        $mform->addElement('hidden', 'contextid', $this->_customdata['contextid']);
-        $mform->setType('contextid', PARAM_INT);
-
-        if (!empty($this->_customdata['id'])) {
-            $mform->addElement('hidden', 'id', $this->_customdata['id']);
-            $mform->setType('id', PARAM_INT);
-        }
-
-        $options = $this->_customdata['options'];
-        $mform->addElement('filepicker', 'file', get_string('file', 'core_contentbank'), null, $options);
-        $mform->addHelpButton('file', 'file', 'core_contentbank');
-        $mform->addRule('file', null, 'required');
-
-        $this->add_action_buttons(true, get_string('savechanges'));
-
-        $data = $this->_customdata['data'];
-        $this->set_data($data);
-    }
-
-    /**
-     * Validate incoming data.
-     *
-     * @param array $data
-     * @param array $files
-     * @return array
-     */
-    public function validation($data, $files) {
-        $errors = array();
-        $draftitemid = $data['file'];
-        if (file_is_draft_area_limit_reached($draftitemid, $this->_customdata['options']['areamaxbytes'])) {
-            $errors['file'] = get_string('userquotalimit', 'error');
-        }
-        return $errors;
-    }
-}
index c4d2242..7845f58 100644 (file)
@@ -88,13 +88,18 @@ if (has_capability('moodle/contentbank:upload', $context)) {
     // Don' show upload button if there's no plugin to support any file extension.
     $accepted = $cb->get_supported_extensions_as_string($context);
     if (!empty($accepted)) {
-        $importurl = new moodle_url('/contentbank/upload.php', ['contextid' => $contextid]);
+        $importurl = new moodle_url('/contentbank/index.php', ['contextid' => $contextid]);
         $toolbar[] = [
             'name' => get_string('upload', 'contentbank'),
-            'link' => $importurl,
+            'link' => $importurl->out(false),
             'icon' => 'i/upload',
             'action' => 'upload'
         ];
+        $PAGE->requires->js_call_amd(
+            'core_contentbank/upload',
+            'initModal',
+            ['[data-action=upload]', \core_contentbank\form\upload_files::class, $contextid]
+        );
     }
 }
 
index 4d590ce..d1fbb7d 100644 (file)
@@ -59,7 +59,7 @@
         {{>core_contentbank/bankcontent/toolbar_dropdown}}
     {{/dropdown}}
     {{^dropdown}}
-        <a href="{{{ link }}}" class="icon-no-margin btn btn-secondary" title="{{{ name }}}">
+        <a href="{{ link }}" class="icon-no-margin btn btn-secondary" title="{{ name }}" data-action="{{ action }}">
             {{#pix}} {{{ icon }}} {{/pix}} {{{ name }}}
         </a>
     {{/dropdown}}
index 8eca72b..aa7d8ed 100644 (file)
@@ -4,3 +4,5 @@ information provided here is intended especially for developers.
 === 3.11 ===
 * Added "get_uses()" method to content class to return places where a content is used.
 * Added set_visibility()/get_visibility() methods to let users decide if their content should be listed in the content bank.
+* The contentbank/upload.php page for displaying the upload files form has been removed. The form for uploading/replacing
+files now is displayed in a modal.
diff --git a/contentbank/upload.php b/contentbank/upload.php
deleted file mode 100644 (file)
index 81a7870..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle 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 the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Upload a file to content bank.
- *
- * @package    core_contentbank
- * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require('../config.php');
-require_once("$CFG->dirroot/contentbank/files_form.php");
-
-use core\output\notification;
-
-require_login();
-
-$contextid = optional_param('contextid', \context_system::instance()->id, PARAM_INT);
-$context = context::instance_by_id($contextid, MUST_EXIST);
-
-$cb = new \core_contentbank\contentbank();
-if (!$cb->is_context_allowed($context)) {
-    print_error('contextnotallowed', 'core_contentbank');
-}
-
-require_capability('moodle/contentbank:upload', $context);
-
-$id = optional_param('id', null, PARAM_INT);
-if ($id) {
-    $content = $cb->get_content_from_id($id);
-    $contenttype = $content->get_content_type_instance();
-    if (!$contenttype->can_manage($content) || !$contenttype->can_upload()) {
-        print_error('nopermissions', 'error', $returnurl, get_string('replacecontent', 'contentbank'));
-    }
-}
-
-$title = get_string('contentbank');
-\core_contentbank\helper::get_page_ready($context, $title, true);
-if ($PAGE->course) {
-    require_login($PAGE->course->id);
-}
-$returnurl = new \moodle_url('/contentbank/index.php', ['contextid' => $contextid]);
-
-$PAGE->set_url('/contentbank/upload.php');
-$PAGE->set_context($context);
-$PAGE->navbar->add(get_string('upload', 'contentbank'));
-$PAGE->set_title($title);
-$PAGE->set_heading($title);
-$PAGE->set_pagetype('contentbank');
-
-$maxbytes = $CFG->userquota;
-$maxareabytes = $CFG->userquota;
-if (has_capability('moodle/user:ignoreuserquota', $context)) {
-    $maxbytes = USER_CAN_IGNORE_FILE_SIZE_LIMITS;
-    $maxareabytes = FILE_AREA_MAX_BYTES_UNLIMITED;
-}
-
-if ($id) {
-    $extensions = $contenttype->get_manageable_extensions();
-    $accepted = implode(',', $extensions);
-} else {
-    $accepted = $cb->get_supported_extensions_as_string($context);
-}
-
-$data = new stdClass();
-$options = array(
-    'subdirs' => 1,
-    'maxbytes' => $maxbytes,
-    'maxfiles' => -1,
-    'accepted_types' => $accepted,
-    'areamaxbytes' => $maxareabytes
-);
-file_prepare_standard_filemanager($data, 'files', $options, $context, 'contentbank', 'public', 0);
-
-$mform = new contentbank_files_form(null, ['contextid' => $contextid, 'data' => $data, 'options' => $options, 'id' => $id]);
-
-$error = '';
-
-if ($mform->is_cancelled()) {
-    redirect($returnurl);
-} else if ($formdata = $mform->get_data()) {
-    require_sesskey();
-    // Get the file and create the content based on it.
-    $usercontext = \context_user::instance($USER->id);
-    $fs = get_file_storage();
-    $files = $fs->get_area_files($usercontext->id, 'user', 'draft', $formdata->file, 'itemid, filepath, filename', false);
-    if (!empty($files)) {
-        $file = reset($files);
-        if ($id) {
-            $content = $contenttype->replace_content($file, $content);
-        } else {
-            $content = $cb->create_content_from_file($context, $USER->id, $file);
-        }
-        $viewurl = new \moodle_url('/contentbank/view.php', ['id' => $content->get_id(), 'contextid' => $contextid]);
-        redirect($viewurl);
-    } else {
-        $error = get_string('errornofile', 'contentbank');
-    }
-}
-
-echo $OUTPUT->header();
-echo $OUTPUT->box_start('generalbox');
-
-if (!empty($error)) {
-    echo $OUTPUT->notification($error, notification::NOTIFY_ERROR);
-}
-
-$mform->display();
-
-echo $OUTPUT->box_end();
-echo $OUTPUT->footer();
index 8258fb2..48519f4 100644 (file)
@@ -123,11 +123,17 @@ if ($contenttype->can_manage($content)) {
 
     if ($contenttype->can_upload()) {
         $actionmenu->add_secondary_action(new action_menu_link(
-            new moodle_url('/contentbank/upload.php', ['contextid' => $context->id, 'id' => $content->get_id()]),
+            new moodle_url('/contentbank/view.php', ['contextid' => $context->id, 'id' => $content->get_id()]),
             new pix_icon('i/upload', get_string('upload')),
             get_string('replacecontent', 'contentbank'),
-            false
+            false,
+            ['data-action' => 'upload']
         ));
+        $PAGE->requires->js_call_amd(
+            'core_contentbank/upload',
+            'initModal',
+            ['[data-action=upload]', \core_contentbank\form\upload_files::class, $context->id, $content->get_id()]
+        );
     }
 }
 if ($contenttype->can_download($content)) {
index 88ea5fd..545558c 100644 (file)
@@ -27,7 +27,6 @@ defined('MOODLE_INTERNAL') || die;
 $functions = [
     'gradingform_guide_grader_gradingpanel_fetch' => [
         'classname' => 'gradingform_guide\\grades\\grader\\gradingpanel\\external\\fetch',
-        'methodname' => 'execute',
         'description' => 'Fetch the data required to display the grader grading panel, ' .
             'creating the grade item if required',
         'type' => 'write',
@@ -35,7 +34,6 @@ $functions = [
     ],
     'gradingform_guide_grader_gradingpanel_store' => [
         'classname' => 'gradingform_guide\\grades\\grader\\gradingpanel\\external\\store',
-        'methodname' => 'execute',
         'description' => 'Store the grading data for a user from the grader grading panel.',
         'type' => 'write',
         'ajax' => true,
index 08b90a9..7cdd0bd 100644 (file)
@@ -27,7 +27,6 @@ defined('MOODLE_INTERNAL') || die;
 $functions = [
     'gradingform_rubric_grader_gradingpanel_fetch' => [
         'classname' => 'gradingform_rubric\\grades\\grader\\gradingpanel\\external\\fetch',
-        'methodname' => 'execute',
         'description' => 'Fetch the data required to display the grader grading panel, ' .
             'creating the grade item if required',
         'type' => 'write',
@@ -35,7 +34,6 @@ $functions = [
     ],
     'gradingform_rubric_grader_gradingpanel_store' => [
         'classname' => 'gradingform_rubric\\grades\\grader\\gradingpanel\\external\\store',
-        'methodname' => 'execute',
         'description' => 'Store the grading data for a user from the grader grading panel.',
         'type' => 'write',
         'ajax' => true,
index 2871a76..0491d89 100644 (file)
@@ -231,7 +231,7 @@ if (!empty($method)) {
         } else {
             $tag = html_writer::tag('span', get_string('statusdraft', 'core_grading'), array('class' => 'status draft'));
         }
-        echo $output->heading(s($definition->name) . ' ' . $tag, 3, 'definition-name');
+        echo $output->heading(format_string($definition->name) . ' ' . $tag, 3, 'definition-name');
         echo $output->box($controller->render_preview($PAGE), 'definition-preview');
     }
 }
index 9e163ef..14e701d 100644 (file)
@@ -148,10 +148,11 @@ class gradereport_user_external extends external_api {
 
         $gpr = new grade_plugin_return(
             array(
-                'type' => 'report',
-                'plugin' => 'user',
-                'courseid' => $course->id,
-                'userid' => $userid)
+                'type'           => 'report',
+                'plugin'         => 'user',
+                'courseid'       => $course->id,
+                'courseidnumber' => $course->idnumber,
+                'userid'         => $userid)
             );
 
         $reportdata = array();
@@ -162,11 +163,12 @@ class gradereport_user_external extends external_api {
             $report->fill_table();
 
             $gradeuserdata = array(
-                'courseid'      => $course->id,
-                'userid'        => $user->id,
-                'userfullname'  => fullname($user),
-                'useridnumber'  => $user->idnumber,
-                'maxdepth'      => $report->maxdepth,
+                'courseid'       => $course->id,
+                'courseidnumber' => $course->idnumber,
+                'userid'         => $user->id,
+                'userfullname'   => fullname($user),
+                'useridnumber'   => $user->idnumber,
+                'maxdepth'       => $report->maxdepth,
             );
             if ($tabledata) {
                 $gradeuserdata['tabledata'] = $report->tabledata;
@@ -189,11 +191,12 @@ class gradereport_user_external extends external_api {
                 $report->fill_table();
 
                 $gradeuserdata = array(
-                    'courseid'      => $course->id,
-                    'userid'        => $currentuser->id,
-                    'userfullname'  => fullname($currentuser),
-                    'useridnumber'  => $currentuser->idnumber,
-                    'maxdepth'      => $report->maxdepth,
+                    'courseid'       => $course->id,
+                    'courseidnumber' => $course->idnumber,
+                    'userid'         => $currentuser->id,
+                    'userfullname'   => fullname($currentuser),
+                    'useridnumber'   => $currentuser->idnumber,
+                    'maxdepth'       => $report->maxdepth,
                 );
                 if ($tabledata) {
                     $gradeuserdata['tabledata'] = $report->tabledata;
@@ -478,6 +481,7 @@ class gradereport_user_external extends external_api {
                     new external_single_structure(
                         array(
                             'courseid' => new external_value(PARAM_INT, 'course id'),
+                            'courseidnumber' => new external_value(PARAM_TEXT, 'course idnumber'),
                             'userid'   => new external_value(PARAM_INT, 'user id'),
                             'userfullname' => new external_value(PARAM_TEXT, 'user fullname'),
                             'useridnumber' => new external_value(
index 26956d3..d00e2b1 100644 (file)
@@ -10,7 +10,7 @@ In plain English, you do not need to distribute your application in source code
 nor do you need to distribute ADOdb source code, provided you follow the rest of
 terms of the BSD license.
 
-For more information about ADOdb, visit http://adodb.org/
+For more information about ADOdb, visit https://adodb.org/
 
 BSD 3-Clause License
 --------------------
index 273c24c..e432b2f 100644 (file)
@@ -1,4 +1,4 @@
-ADOdb Library for PHP5
+ADOdb Library for PHP
 ======================
 
 [![Join chat on Gitter](https://img.shields.io/gitter/room/form-data/form-data.svg)](https://gitter.im/adodb/adodb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@@ -16,7 +16,7 @@ or, at your option, any later version.
 This means you can use it in proprietary products;
 see [License](https://github.com/ADOdb/ADOdb/blob/master/LICENSE.md) for details.
 
-Home page: http://adodb.org/
+Home page: https://adodb.org/
 
 
 Introduction
@@ -27,8 +27,8 @@ need for a database class library to hide the differences between the
 different databases (encapsulate the differences) so we can easily
 switch databases.
 
-The library currently supports MySQL, Interbase, Sybase, PostgreSQL, Oracle,
-Microsoft SQL server,  Foxpro ODBC, Access ODBC, Informix, DB2,
+The library currently supports MySQL, Firebird & Interbase, PostgreSQL, SQLite3, Oracle,
+Microsoft SQL Server, Foxpro ODBC, Access ODBC, Informix, DB2, Sybase,
 Sybase SQL Anywhere, generic ODBC and Microsoft's ADO.
 
 We hope more people will contribute drivers to support other databases.
@@ -48,12 +48,12 @@ You can debug using:
 <?php
 include('adodb/adodb.inc.php');
 
-$db = ADONewConnection($driver); # eg. 'mysql' or 'oci8'
+$db = adoNewConnection($driver); # eg. 'mysqli' or 'oci8'
 $db->debug = true;
-$db->Connect($server, $user, $password, $database);
-$rs = $db->Execute('select * from some_small_table');
+$db->connect($server, $user, $password, $database);
+$rs = $db->execute('select * from some_small_table');
 print "<pre>";
-print_r($rs->GetRows());
+print_r($rs->getRows());
 print "</pre>";
 ```
 
@@ -61,14 +61,14 @@ print "</pre>";
 Documentation and Examples
 ==========================
 
-Refer to the [ADOdb website](http://adodb.org/) for library documentation and examples. The documentation can also be [downloaded for offline viewing](https://sourceforge.net/projects/adodb/files/Documentation/).
+Refer to the [ADOdb website](https://adodb.org/) for library documentation and examples. The documentation can also be [downloaded for offline viewing](https://sourceforge.net/projects/adodb/files/Documentation/).
 
-- [Main documentation](http://adodb.org/dokuwiki/doku.php?id=v5:userguide:userguide_index): Query, update and insert records using a portable API.
-- [Data dictionary](http://adodb.org/dokuwiki/doku.php?id=v5:dictionary:dictionary_index) describes how to create database tables and indexes in a portable manner.
-- [Database performance monitoring](http://adodb.org/dokuwiki/doku.php?id=v5:performance:performance_index) allows you to perform health checks, tune and monitor your database.
-- [Database-backed sessions](http://adodb.org/dokuwiki/doku.php?id=v5:session:session_index).
+- [Main documentation](https://adodb.org/dokuwiki/doku.php?id=v5:userguide:userguide_index): Query, update and insert records using a portable API.
+- [Data dictionary](https://adodb.org/dokuwiki/doku.php?id=v5:dictionary:dictionary_index) describes how to create database tables and indexes in a portable manner.
+- [Database performance monitoring](https://adodb.org/dokuwiki/doku.php?id=v5:performance:performance_index) allows you to perform health checks, tune and monitor your database.
+- [Database-backed sessions](https://adodb.org/dokuwiki/doku.php?id=v5:session:session_index).
 
-There is also a [tutorial](http://adodb.org/dokuwiki/doku.php?id=v5:userguide:mysql_tutorial) that contrasts ADOdb code with PHP native MySQL code.
+There is also a [tutorial](https://adodb.org/dokuwiki/doku.php?id=v5:userguide:mysql_tutorial) that contrasts ADOdb code with PHP native MySQL code.
 
 
 Files
@@ -96,7 +96,6 @@ https://github.com/ADOdb/ADOdb/issues
 
 You may also find legacy issues in
 
-- the old [ADOdb forums](http://phplens.com/lens/lensforum/topics.php?id=4) on phplens.com
 - the [SourceForge tickets section](http://sourceforge.net/p/adodb/_list/tickets)
 
 However, please note that they are not actively monitored and should
index 304ff06..aea7be7 100644 (file)
@@ -1,10 +1,10 @@
 <?php
 /*
 
-@version   v5.20.16  12-Jan-2020
+@version   v5.21.0  2021-02-27
 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
 @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
-  Latest version is available at http://adodb.org/
+  Latest version is available at https://adodb.org/
 
   Released under both BSD license and Lesser GPL library license.
   Whenever there is any discrepancy between the two licenses,
@@ -46,40 +46,37 @@ class ADODB_Active_Table {
 
 // $db = database connection
 // $index = name of index - can be associative, for an example see
-//    http://phplens.com/lens/lensforum/msgs.php?id=17790
+//    PHPLens Issue No: 17790
 // returns index into $_ADODB_ACTIVE_DBS
 function ADODB_SetDatabaseAdapter(&$db, $index=false)
 {
        global $_ADODB_ACTIVE_DBS;
 
-               foreach($_ADODB_ACTIVE_DBS as $k => $d) {
-                       if (PHP_VERSION >= 5) {
-                               if ($d->db === $db) {
-                                       return $k;
-                               }
-                       } else {
-                               if ($d->db->_connectionID === $db->_connectionID && $db->database == $d->db->database) {
-                                       return $k;
-                               }
-                       }
+       foreach($_ADODB_ACTIVE_DBS as $k => $d) {
+               if($d->db === $db) {
+                       return $k;
                }
+       }
 
-               $obj = new ADODB_Active_DB();
-               $obj->db = $db;
-               $obj->tables = array();
+       $obj = new ADODB_Active_DB();
+       $obj->db = $db;
+       $obj->tables = array();
 
-               if ($index == false) {
-                       $index = sizeof($_ADODB_ACTIVE_DBS);
-               }
+       if ($index == false) {
+               $index = sizeof($_ADODB_ACTIVE_DBS);
+       }
 
-               $_ADODB_ACTIVE_DBS[$index] = $obj;
+       $_ADODB_ACTIVE_DBS[$index] = $obj;
 
-               return sizeof($_ADODB_ACTIVE_DBS)-1;
+       return sizeof($_ADODB_ACTIVE_DBS)-1;
 }
 
 
 class ADODB_Active_Record {
        static $_changeNames = true; // dynamically pluralize table names
+       /*
+       * Optional parameter that duplicates the ADODB_QUOTE_FIELDNAMES
+       */
        static $_quoteNames = false;
 
        static $_foreignSuffix = '_id'; //
@@ -499,7 +496,6 @@ class ADODB_Active_Record {
                        break;
                default:
                        foreach($cols as $name => $fldobj) {
-                               $name = ($fldobj->name);
 
                                if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) {
                                        $this->$name = $fldobj->default_value;
@@ -510,7 +506,7 @@ class ADODB_Active_Record {
                                $attr[$name] = $fldobj;
                        }
                        foreach($pkeys as $k => $name) {
-                               $keys[$name] = $cols[$name]->name;
+                               $keys[$name] = $cols[strtoupper($name)]->name;
                        }
                        break;
                }
@@ -522,7 +518,7 @@ class ADODB_Active_Record {
                        $activetab->_created = time();
                        $s = serialize($activetab);
                        if (!function_exists('adodb_write_file')) {
-                               include(ADODB_DIR.'/adodb-csvlib.inc.php');
+                               include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
                        }
                        adodb_write_file($fname,$s);
                }
@@ -700,9 +696,14 @@ class ADODB_Active_Record {
                        $val = false;
                }
 
-               if (is_null($val) || $val === false) {
+               if (is_null($val) || $val === false)
+               {
+                       $SQL = sprintf("SELECT MAX(%s) FROM %s",
+                                                  $this->nameQuoter($db,$fieldname),
+                                                  $this->nameQuoter($db,$this->_table)
+                                                  );
                        // this might not work reliably in multi-user environment
-                       return $db->GetOne("select max(".$fieldname.") from ".$this->_table);
+                       return $db->GetOne($SQL);
                }
                return $val;
        }
@@ -749,10 +750,11 @@ class ADODB_Active_Record {
                foreach($keys as $k) {
                        $f = $table->flds[$k];
                        if ($f) {
-                               $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type));
+                               $columnName = $this->nameQuoter($db,$k);
+                               $parr[] = $columnName.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type));
                        }
                }
-               return implode(' and ', $parr);
+               return implode(' AND ', $parr);
        }
 
 
@@ -774,7 +776,7 @@ class ADODB_Active_Record {
 
        function Load($where=null,$bindarr=false, $lock = false)
        {
-       global $ADODB_FETCH_MODE;
+               global $ADODB_FETCH_MODE;
 
                $db = $this->DB();
                if (!$db) {
@@ -788,7 +790,9 @@ class ADODB_Active_Record {
                        $savem = $db->SetFetchMode(false);
                }
 
-               $qry = "select * from ".$this->_table;
+               $qry = sprintf("SELECT * FROM %s",
+                                          $this->nameQuoter($db,$this->_table)
+                                          );
 
                if($where) {
                        $qry .= ' WHERE '.$where;
@@ -813,7 +817,7 @@ class ADODB_Active_Record {
        }
 
        # useful for multiple record inserts
-       # see http://phplens.com/lens/lensforum/msgs.php?id=17795
+       # see PHPLens Issue No: 17795
        function Reset()
        {
                $this->_where=null;
@@ -862,7 +866,7 @@ class ADODB_Active_Record {
                        $val = $this->$name;
                        if(!is_array($val) || !is_null($val) || !array_key_exists($name, $table->keys)) {
                                $valarr[] = $val;
-                               $names[] = $this->_QName($name,$db);
+                               $names[] = $this->nameQuoter($db,$name);
                                $valstr[] = $db->Param($cnt);
                                $cnt += 1;
                        }
@@ -871,12 +875,18 @@ class ADODB_Active_Record {
                if (empty($names)){
                        foreach($table->flds as $name=>$fld) {
                                $valarr[] = null;
-                               $names[] = $name;
+                               $names[] = $this->nameQuoter($db,$name);
                                $valstr[] = $db->Param($cnt);
                                $cnt += 1;
                        }
                }
-               $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')';
+               
+               $tableName = $this->nameQuoter($db,$this->_table);
+               $sql = sprintf('INSERT INTO %s (%s) VALUES (%s)',
+                                          $tableName,
+                                          implode(',',$names),
+                                          implode(',',$valstr)
+                                          );
                $ok = $db->Execute($sql,$valarr);
 
                if ($ok) {
@@ -907,7 +917,14 @@ class ADODB_Active_Record {
                $table = $this->TableInfo();
 
                $where = $this->GenWhere($db,$table);
-               $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where;
+
+               $tableName = $this->nameQuoter($db,$this->_table);
+               
+               $sql = sprintf('DELETE FROM %s WHERE %s',
+                                          $tableName,
+                                          $where
+                                          );
+
                $ok = $db->Execute($sql);
 
                return $ok ? true : false;
@@ -978,8 +995,20 @@ class ADODB_Active_Record {
                                }
                                break;
                }
-
-               $ok = $db->Replace($this->_table,$arr,$pkey);
+               
+               $newArr = array();
+               foreach($arr as $k=>$v)
+                       $newArr[$this->nameQuoter($db,$k)] = $v;
+               $arr = $newArr;
+               
+               $newPkey = array();
+               foreach($pkey as $k=>$v)
+                       $newPkey[$k] = $this->nameQuoter($db,$v);
+               $pkey = $newPkey;
+               
+               $tableName = $this->nameQuoter($db,$this->_table);
+               
+               $ok = $db->Replace($tableName,$arr,$pkey);
                if ($ok) {
                        $this->_saved = true; // 1= update 2=insert
                        if ($ok == 2) {
@@ -1051,7 +1080,7 @@ class ADODB_Active_Record {
                        }
 
                        $valarr[] = $val;
-                       $pairs[] = $this->_QName($name,$db).'='.$db->Param($cnt);
+                       $pairs[] = $this->nameQuoter($db,$name).'='.$db->Param($cnt);
                        $cnt += 1;
                }
 
@@ -1060,7 +1089,13 @@ class ADODB_Active_Record {
                        return -1;
                }
 
-               $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where;
+               $tableName = $this->nameQuoter($db,$this->_table);
+
+               $sql = sprintf('UPDATE %s SET %s WHERE %s',
+                                          $tableName,
+                                          implode(',',$pairs),
+                                          $where);
+               
                $ok = $db->Execute($sql,$valarr);
                if ($ok) {
                        $this->_original = $neworig;
@@ -1078,6 +1113,66 @@ class ADODB_Active_Record {
                return array_keys($table->flds);
        }
 
+       /**
+       * Quotes the table and column and field names
+       *
+       * this honours the ADODB_QUOTE_FIELDNAMES directive. The routines that
+       * use it should really just call _adodb_getinsertsql and _adodb_getupdatesql
+       * which is a nice easy project if you are interested
+       *
+       * @param        obj             $db             The database connection
+       * @param        string  $name   The table or column name to quote
+       *
+       * @return       string  The quoted name
+       */
+       private function nameQuoter($db,$string)
+       {
+               global $ADODB_QUOTE_FIELDNAMES;
+               
+               if (!$ADODB_QUOTE_FIELDNAMES && !$this->_quoteNames)
+                       /*
+                       * Nothing to be done
+                       */
+                       return $string;
+               
+               if ($this->_quoteNames == 'NONE')
+                       /*
+                       * Force no quoting when ADODB_QUOTE_FIELDNAMES is set
+                       */
+                       return $string;
+               
+               if ($this->_quoteNames)
+                       /*
+                       * Internal setting takes precedence
+                       */
+                       $quoteMethod = $this->_quoteNames;
+                       
+               else
+                       $quoteMethod = $ADODB_QUOTE_FIELDNAMES;
+               
+               switch ($quoteMethod)
+               {
+               case 'LOWER':
+                       $string = strtolower($string);
+                       break;
+               case 'NATIVE':
+                       /*
+                       * Nothing to be done
+                       */
+                       break;
+               case 'UPPER':
+               default:
+                       $string = strtoupper($string);
+               }
+                       
+               $string = sprintf(      '%s%s%s',
+                                                       $db->nameQuote,
+                                                       $string,
+                                                       $db->nameQuote
+                                         );
+               return $string;
+       }
+
 };
 
 function adodb_GetActiveRecordsClass(&$db, $class, $table,$whereOrderBy,$bindarr, $primkeyArr,
@@ -1087,6 +1182,7 @@ global $_ADODB_ACTIVE_DBS;
 
 
        $save = $db->SetFetchMode(ADODB_FETCH_NUM);
+       
        $qry = "select * from ".$table;
 
        if (!empty($whereOrderBy)) {
@@ -1125,7 +1221,7 @@ global $_ADODB_ACTIVE_DBS;
        // arrRef will be the structure that knows about our objects.
        // It is an associative array.
        // We will, however, return arr, preserving regular 0.. order so that
-       // obj[0] can be used by app developpers.
+       // obj[0] can be used by app developers.
        $arrRef = array();
        $bTos = array(); // Will store belongTo's indices if any
        foreach($rows as $row) {
index 7598f1d..a9356ee 100644 (file)
@@ -1,10 +1,10 @@
 <?php
 /*
 
-@version   v5.20.16  12-Jan-2020
+@version   v5.21.0  2021-02-27
 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
 @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
-  Latest version is available at http://adodb.org/
+  Latest version is available at https://adodb.org/
 
   Released under both BSD license and Lesser GPL library license.
   Whenever there is any discrepancy between the two licenses,
@@ -66,25 +66,19 @@ function ADODB_SetDatabaseAdapter(&$db)
 {
        global $_ADODB_ACTIVE_DBS;
 
-               foreach($_ADODB_ACTIVE_DBS as $k => $d) {
-                       if (PHP_VERSION >= 5) {
-                               if ($d->db === $db) {
-                                       return $k;
-                               }
-                       } else {
-                               if ($d->db->_connectionID === $db->_connectionID && $db->database == $d->db->database) {
-                                       return $k;
-                               }
-                       }
+       foreach($_ADODB_ACTIVE_DBS as $k => $d) {
+               if ($d->db === $db) {
+                       return $k;
                }
+       }
 
-               $obj = new ADODB_Active_DB();
-               $obj->db = $db;
-               $obj->tables = array();
+       $obj = new ADODB_Active_DB();
+       $obj->db = $db;
+       $obj->tables = array();
 
-               $_ADODB_ACTIVE_DBS[] = $obj;
+       $_ADODB_ACTIVE_DBS[] = $obj;
 
-               return sizeof($_ADODB_ACTIVE_DBS)-1;
+       return sizeof($_ADODB_ACTIVE_DBS)-1;
 }
 
 
@@ -551,7 +545,7 @@ class ADODB_Active_Record {
                        $activetab->_created = time();
                        $s = serialize($activetab);
                        if (!function_exists('adodb_write_file')) {
-                               include(ADODB_DIR.'/adodb-csvlib.inc.php');
+                               include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
                        }
                        adodb_write_file($fname,$s);
                }
@@ -1298,7 +1292,7 @@ function adodb_GetActiveRecordsClass(&$db, $class, $tableObj,$whereOrderBy,$bind
 
 
                if (!isset($_ADODB_ACTIVE_DBS)) {
-                       include(ADODB_DIR.'/adodb-active-record.inc.php');
+                       include_once(ADODB_DIR.'/adodb-active-record.inc.php');
                }
                if (!class_exists($class)) {
                        $db->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
@@ -1309,7 +1303,7 @@ function adodb_GetActiveRecordsClass(&$db, $class, $tableObj,$whereOrderBy,$bind
                // arrRef will be the structure that knows about our objects.
                // It is an associative array.
                // We will, however, return arr, preserving regular 0.. order so that
-               // obj[0] can be used by app developpers.
+               // obj[0] can be used by app developers.
                $arrRef = array();
                $bTos = array(); // Will store belongTo's indices if any
                foreach($rows as $row) {
index f141258..8774278 100644 (file)
@@ -8,7 +8,7 @@ $ADODB_INCLUDED_CSV = 1;
 
 /*
 
-  @version   v5.20.16  12-Jan-2020
+  @version   v5.21.0  2021-02-27
   @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
   @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
   Released under both BSD license and Lesser GPL library license.
@@ -16,7 +16,7 @@ $ADODB_INCLUDED_CSV = 1;
   the BSD license will take precedence. See License.txt.
   Set tabs to 4 for best viewing.
 
-  Latest version is available at http://adodb.org/
+  Latest version is available at https://adodb.org/
 
   Library for CSV serialization. This is used by the csv/proxy driver and is the
   CacheExecute() serialization format.
@@ -27,7 +27,7 @@ $ADODB_INCLUDED_CSV = 1;
         *
         * @param rs    the recordset
         *
-        * @return      the CSV formated data
+        * @return      the CSV formatted data
         */
        function _rs2serialize(&$rs,$conn=false,$sql='')
        {
@@ -85,7 +85,7 @@ $ADODB_INCLUDED_CSV = 1;
 * @param err           returns the error message
 * @param timeout       dispose if recordset has been alive for $timeout secs
 *
-* @return              recordset, or false if error occured. If no
+* @return              recordset, or false if error occurred. If no
 *                      error occurred in sql INSERT/UPDATE/DELETE,
 *                      empty recordset is returned
 */
@@ -245,10 +245,8 @@ $ADODB_INCLUDED_CSV = 1;
 
                fclose($fp);
                @$arr = unserialize($text);
-               //var_dump($arr);
                if (!is_array($arr)) {
                        $err = "Recordset had unexpected EOF (in serialized recordset)";
-                       if (get_magic_quotes_runtime()) $err .= ". Magic Quotes Runtime should be disabled!";
                        return $false;
                }
                $rs = new $rsclass();
index f8a5b7e..4a96aa8 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
-  @version   v5.20.16  12-Jan-2020
+  @version   v5.21.0  2021-02-27
   @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
   @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
   Released under both BSD license and Lesser GPL library license.
 // security - hide paths
 if (!defined('ADODB_DIR')) die();
 
-function Lens_ParseTest()
+function lens_ParseTest()
 {
 $str = "`zcol ACOL` NUMBER(32,2) DEFAULT 'The \"cow\" (and Jim''s dog) jumps over the moon' PRIMARY, INTI INT AUTO DEFAULT 0, zcol2\"afs ds";
 print "<p>$str</p>";
-$a= Lens_ParseArgs($str);
+$a= lens_ParseArgs($str);
 print "<pre>";
 print_r($a);
 print "</pre>";
@@ -53,7 +53,7 @@ if (!function_exists('ctype_alnum')) {
        @param tokenchars     Include the following characters in tokens apart from A-Z and 0-9
        @returns 2 dimensional array containing parsed tokens.
 */
-function Lens_ParseArgs($args,$endstmtchar=',',$tokenchars='_.-')
+function lens_ParseArgs($args,$endstmtchar=',',$tokenchars='_.-')
 {
        $pos = 0;
        $intoken = false;
@@ -179,45 +179,58 @@ class ADODB_DataDict {
        var $serverInfo = array();
        var $autoIncrement = false;
        var $dataProvider;
-       var $invalidResizeTypes4 = array('CLOB','BLOB','TEXT','DATE','TIME'); // for changetablesql
+       var $invalidResizeTypes4 = array('CLOB','BLOB','TEXT','DATE','TIME'); // for changeTableSQL
        var $blobSize = 100;    /// any varchar/char field this size or greater is treated as a blob
-                                                       /// in other words, we use a text area for editting.
+                                                       /// in other words, we use a text area for editing.
 
-       function GetCommentSQL($table,$col)
+       /*
+       * Indicates whether a BLOB/CLOB field will allow a NOT NULL setting
+       * The type is whatever is matched to an X or X2 or B type. We must 
+       * explicitly set the value in the driver to switch the behaviour on
+       */
+       public $blobAllowsNotNull;
+       /*
+       * Indicates whether a BLOB/CLOB field will allow a DEFAULT set
+       * The type is whatever is matched to an X or X2 or B type. We must 
+       * explicitly set the value in the driver to switch the behaviour on
+       */
+       public $blobAllowsDefaultValue;
+
+       function getCommentSQL($table,$col)
        {
                return false;
        }
 
-       function SetCommentSQL($table,$col,$cmt)
+       function setCommentSQL($table,$col,$cmt)
        {
                return false;
        }
 
-       function MetaTables()
+       function metaTables()
        {
-               if (!$this->connection->IsConnected()) return array();
-               return $this->connection->MetaTables();
+               if (!$this->connection->isConnected()) return array();
+               return $this->connection->metaTables();
        }
 
-       function MetaColumns($tab, $upper=true, $schema=false)
+       function metaColumns($tab, $upper=true, $schema=false)
        {
-               if (!$this->connection->IsConnected()) return array();
-               return $this->connection->MetaColumns($this->TableName($tab), $upper, $schema);
+               if (!$this->connection->isConnected()) return array();
+               return $this->connection->metaColumns($this->tableName($tab), $upper, $schema);
        }
 
-       function MetaPrimaryKeys($tab,$owner=false,$intkey=false)
+       function metaPrimaryKeys($tab,$owner=false,$intkey=false)
        {
-               if (!$this->connection->IsConnected()) return array();
-               return $this->connection->MetaPrimaryKeys($this->TableName($tab), $owner, $intkey);
+               if (!$this->connection->isConnected()) return array();
+               return $this->connection->metaPrimaryKeys($this->tableName($tab), $owner, $intkey);
        }
 
-       function MetaIndexes($table, $primary = false, $owner = false)
+       function metaIndexes($table, $primary = false, $owner = false)
        {
-               if (!$this->connection->IsConnected()) return array();
-               return $this->connection->MetaIndexes($this->TableName($table), $primary, $owner);
+               if (!$this->connection->isConnected()) return array();
+               return $this->connection->metaIndexes($this->tableName($table), $primary, $owner);
        }
 
-       function MetaType($t,$len=-1,$fieldobj=false)
+       function metaType($t,$len=-1,$fieldobj=false)
        {
                static $typeMap = array(
                'VARCHAR' => 'C',
@@ -323,15 +336,15 @@ class ADODB_DataDict {
                "SQLBOOL" => 'L'
                );
 
-               if (!$this->connection->IsConnected()) {
+               if (!$this->connection->isConnected()) {
                        $t = strtoupper($t);
                        if (isset($typeMap[$t])) return $typeMap[$t];
-                       return 'N';
+                       return ADODB_DEFAULT_METATYPE;
                }
-               return $this->connection->MetaType($t,$len,$fieldobj);
+               return $this->connection->metaType($t,$len,$fieldobj);
        }
 
-       function NameQuote($name = NULL,$allowBrackets=false)
+       function nameQuote($name = NULL,$allowBrackets=false)
        {
                if (!is_string($name)) {
                        return FALSE;
@@ -360,16 +373,16 @@ class ADODB_DataDict {
                return $name;
        }
 
-       function TableName($name)
+       function tableName($name)
        {
                if ( $this->schema ) {
-                       return $this->NameQuote($this->schema) .'.'. $this->NameQuote($name);
+                       return $this->nameQuote($this->schema) .'.'. $this->nameQuote($name);
                }
-               return $this->NameQuote($name);
+               return $this->nameQuote($name);
        }
 
-       // Executes the sql array returned by GetTableSQL and GetIndexSQL
-       function ExecuteSQLArray($sql, $continueOnError = true)
+       // Executes the sql array returned by getTableSQL and getIndexSQL
+       function executeSQLArray($sql, $continueOnError = true)
        {
                $rez = 2;
                $conn = $this->connection;
@@ -377,10 +390,10 @@ class ADODB_DataDict {
                foreach($sql as $line) {
 
                        if ($this->debug) $conn->debug = true;
-                       $ok = $conn->Execute($line);
+                       $ok = $conn->execute($line);
                        $conn->debug = $saved;
                        if (!$ok) {
-                               if ($this->debug) ADOConnection::outp($conn->ErrorMsg());
+                               if ($this->debug) ADOConnection::outp($conn->errorMsg());
                                if (!$continueOnError) return 0;
                                $rez = 1;
                        }
@@ -406,17 +419,17 @@ class ADODB_DataDict {
                N:  Numeric or decimal number
        */
 
-       function ActualType($meta)
+       function actualType($meta)
        {
                return $meta;
        }
 
-       function CreateDatabase($dbname,$options=false)
+       function createDatabase($dbname,$options=false)
        {
-               $options = $this->_Options($options);
+               $options = $this->_options($options);
                $sql = array();
 
-               $s = 'CREATE DATABASE ' . $this->NameQuote($dbname);
+               $s = 'CREATE DATABASE ' . $this->nameQuote($dbname);
                if (isset($options[$this->upperName]))
                        $s .= ' '.$options[$this->upperName];
 
@@ -427,7 +440,7 @@ class ADODB_DataDict {
        /*
         Generates the SQL to create index. Returns an array of sql strings.
        */
-       function CreateIndexSQL($idxname, $tabname, $flds, $idxoptions = false)
+       function createIndexSQL($idxname, $tabname, $flds, $idxoptions = false)
        {
                if (!is_array($flds)) {
                        $flds = explode(',',$flds);
@@ -435,27 +448,27 @@ class ADODB_DataDict {
 
                foreach($flds as $key => $fld) {
                        # some indexes can use partial fields, eg. index first 32 chars of "name" with NAME(32)
-                       $flds[$key] = $this->NameQuote($fld,$allowBrackets=true);
+                       $flds[$key] = $this->nameQuote($fld,$allowBrackets=true);
                }
 
-               return $this->_IndexSQL($this->NameQuote($idxname), $this->TableName($tabname), $flds, $this->_Options($idxoptions));
+               return $this->_indexSQL($this->nameQuote($idxname), $this->tableName($tabname), $flds, $this->_options($idxoptions));
        }
 
-       function DropIndexSQL ($idxname, $tabname = NULL)
+       function dropIndexSQL ($idxname, $tabname = NULL)
        {
-               return array(sprintf($this->dropIndex, $this->NameQuote($idxname), $this->TableName($tabname)));
+               return array(sprintf($this->dropIndex, $this->nameQuote($idxname), $this->tableName($tabname)));
        }
 
-       function SetSchema($schema)
+       function setSchema($schema)
        {
                $this->schema = $schema;
        }
 
-       function AddColumnSQL($tabname, $flds)
+       function addColumnSQL($tabname, $flds)
        {
-               $tabname = $this->TableName ($tabname);
+               $tabname = $this->tableName($tabname);
                $sql = array();
-               list($lines,$pkey,$idxs) = $this->_GenFields($flds);
+               list($lines,$pkey,$idxs) = $this->_genFields($flds);
                // genfields can return FALSE at times
                if ($lines  == null) $lines = array();
                $alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' ';
@@ -464,7 +477,7 @@ class ADODB_DataDict {
                }
                if (is_array($idxs)) {
                        foreach($idxs as $idx => $idxdef) {
-                               $sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
+                               $sql_idxs = $this->createIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
                                $sql = array_merge($sql, $sql_idxs);
                        }
                }
@@ -474,19 +487,19 @@ class ADODB_DataDict {
        /**
         * Change the definition of one column
         *
-        * As some DBM's can't do that on there own, you need to supply the complete defintion of the new table,
-        * to allow, recreating the table and copying the content over to the new table
+        * As some DBMs can't do that on their own, you need to supply the complete definition of the new table,
+        * to allow recreating the table and copying the content over to the new table
         * @param string $tabname table-name
         * @param string $flds column-name and type for the changed column
-        * @param string $tableflds='' complete defintion of the new table, eg. for postgres, default ''
-        * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default ''
+        * @param string $tableflds='' complete definition of the new table, eg. for postgres, default ''
+        * @param array/string $tableoptions='' options for the new table see createTableSQL, default ''
         * @return array with SQL strings
         */
-       function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
+       function alterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
        {
-               $tabname = $this->TableName ($tabname);
+               $tabname = $this->tableName($tabname);
                $sql = array();
-               list($lines,$pkey,$idxs) = $this->_GenFields($flds);
+               list($lines,$pkey,$idxs) = $this->_genFields($flds);
                // genfields can return FALSE at times
                if ($lines == null) $lines = array();
                $alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' ';
@@ -495,7 +508,7 @@ class ADODB_DataDict {
                }
                if (is_array($idxs)) {
                        foreach($idxs as $idx => $idxdef) {
-                               $sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
+                               $sql_idxs = $this->createIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
                                $sql = array_merge($sql, $sql_idxs);
                        }
 
@@ -506,71 +519,71 @@ class ADODB_DataDict {
        /**
         * Rename one column
         *
-        * Some DBM's can only do this together with changeing the type of the column (even if that stays the same, eg. mysql)
+        * Some DBMs can only do this together with changeing the type of the column (even if that stays the same, eg. mysql)
         * @param string $tabname table-name
         * @param string $oldcolumn column-name to be renamed
         * @param string $newcolumn new column-name
-        * @param string $flds='' complete column-defintion-string like for AddColumnSQL, only used by mysql atm., default=''
+        * @param string $flds='' complete column-definition-string like for addColumnSQL, only used by mysql atm., default=''
         * @return array with SQL strings
         */
-       function RenameColumnSQL($tabname,$oldcolumn,$newcolumn,$flds='')
+       function renameColumnSQL($tabname,$oldcolumn,$newcolumn,$flds='')
        {
-               $tabname = $this->TableName ($tabname);
+               $tabname = $this->tableName($tabname);
                if ($flds) {
-                       list($lines,$pkey,$idxs) = $this->_GenFields($flds);
+                       list($lines,$pkey,$idxs) = $this->_genFields($flds);
                        // genfields can return FALSE at times
                        if ($lines == null) $lines = array();
                        $first  = current($lines);
                        list(,$column_def) = preg_split("/[\t ]+/",$first,2);
                }
-               return array(sprintf($this->renameColumn,$tabname,$this->NameQuote($oldcolumn),$this->NameQuote($newcolumn),$column_def));
+               return array(sprintf($this->renameColumn,$tabname,$this->nameQuote($oldcolumn),$this->nameQuote($newcolumn),$column_def));
        }
 
        /**
         * Drop one column
         *
-        * Some DBM's can't do that on there own, you need to supply the complete defintion of the new table,
+        * Some DBM's can't do that on their own, you need to supply the complete definition of the new table,
         * to allow, recreating the table and copying the content over to the new table
         * @param string $tabname table-name
         * @param string $flds column-name and type for the changed column
-        * @param string $tableflds='' complete defintion of the new table, eg. for postgres, default ''
-        * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default ''
+        * @param string $tableflds='' complete definition of the new table, eg. for postgres, default ''
+        * @param array/string $tableoptions='' options for the new table see createTableSQL, default ''
         * @return array with SQL strings
         */
-       function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
+       function dropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
        {
-               $tabname = $this->TableName ($tabname);
+               $tabname = $this->tableName($tabname);
                if (!is_array($flds)) $flds = explode(',',$flds);
                $sql = array();
                $alter = 'ALTER TABLE ' . $tabname . $this->dropCol . ' ';
                foreach($flds as $v) {
-                       $sql[] = $alter . $this->NameQuote($v);
+                       $sql[] = $alter . $this->nameQuote($v);
                }
                return $sql;
        }
 
-       function DropTableSQL($tabname)
+       function dropTableSQL($tabname)
        {
-               return array (sprintf($this->dropTable, $this->TableName($tabname)));
+               return array (sprintf($this->dropTable, $this->tableName($tabname)));
        }
 
-       function RenameTableSQL($tabname,$newname)
+       function renameTableSQL($tabname,$newname)
        {
-               return array (sprintf($this->renameTable, $this->TableName($tabname),$this->TableName($newname)));
+               return array (sprintf($this->renameTable, $this->tableName($tabname),$this->tableName($newname)));
        }
 
        /**
         Generate the SQL to create table. Returns an array of sql strings.
        */
-       function CreateTableSQL($tabname, $flds, $tableoptions=array())
+       function createTableSQL($tabname, $flds, $tableoptions=array())
        {
-               list($lines,$pkey,$idxs) = $this->_GenFields($flds, true);
+               list($lines,$pkey,$idxs) = $this->_genFields($flds, true);
                // genfields can return FALSE at times
                if ($lines == null) $lines = array();
 
-               $taboptions = $this->_Options($tableoptions);
-               $tabname = $this->TableName ($tabname);
-               $sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions);
+               $taboptions = $this->_options($tableoptions);
+               $tabname = $this->tableName($tabname);
+               $sql = $this->_tableSQL($tabname,$lines,$pkey,$taboptions);
 
                // ggiunta - 2006/10/12 - KLUDGE:
         // if we are on autoincrement, and table options includes REPLACE, the
@@ -579,12 +592,12 @@ class ADODB_DataDict {
         // creating sql that double-drops the sequence
         if ($this->autoIncrement && isset($taboptions['REPLACE']))
                unset($taboptions['REPLACE']);
-               $tsql = $this->_Triggers($tabname,$taboptions);
+               $tsql = $this->_triggers($tabname,$taboptions);
                foreach($tsql as $s) $sql[] = $s;
 
                if (is_array($idxs)) {
                        foreach($idxs as $idx => $idxdef) {
-                               $sql_idxs = $this->CreateIndexSql($idx, $tabname,  $idxdef['cols'], $idxdef['opts']);
+                               $sql_idxs = $this->createIndexSql($idx, $tabname,  $idxdef['cols'], $idxdef['opts']);
                                $sql = array_merge($sql, $sql_idxs);
                        }
                }
@@ -594,13 +607,13 @@ class ADODB_DataDict {
 
 
 
-       function _GenFields($flds,$widespacing=false)
+       function _genFields($flds,$widespacing=false)
        {
                if (is_string($flds)) {
                        $padding = '     ';
                        $txt = $flds.$padding;
                        $flds = array();
-                       $flds0 = Lens_ParseArgs($txt,',');
+                       $flds0 = lens_ParseArgs($txt,',');
                        $hasparam = false;
                        foreach($flds0 as $f0) {
                                $f1 = array();
@@ -643,8 +656,8 @@ class ADODB_DataDict {
                $pkey = array();
                $idxs = array();
                foreach($flds as $fld) {
-                       $fld = _array_change_key_case($fld);
-
+                       if (is_array($fld))
+                               $fld = array_change_key_case($fld,CASE_UPPER);
                        $fname = false;
                        $fdefault = false;
                        $fautoinc = false;
@@ -660,18 +673,23 @@ class ADODB_DataDict {
                        $funsigned = false;
                        $findex = '';
                        $funiqueindex = false;
+                       $fOptions         = array();
 
                        //-----------------
                        // Parse attributes
                        foreach($fld as $attr => $v) {
-                               if ($attr == 2 && is_numeric($v)) $attr = 'SIZE';
-                               else if (is_numeric($attr) && $attr > 1 && !is_numeric($v)) $attr = strtoupper($v);
+                               if ($attr == 2 && is_numeric($v)) 
+                                       $attr = 'SIZE';
+                               elseif ($attr == 2 && strtoupper($ftype) == 'ENUM') 
+                                       $attr = 'ENUM';
+                               else if (is_numeric($attr) && $attr > 1 && !is_numeric($v)) 
+                                       $attr = strtoupper($v);
 
                                switch($attr) {
                                case '0':
                                case 'NAME':    $fname = $v; break;
                                case '1':
-                               case 'TYPE':    $ty = $v; $ftype = $this->ActualType(strtoupper($v)); break;
+                               case 'TYPE':    $ty = $v; $ftype = $this->actualType(strtoupper($v)); break;
 
                                case 'SIZE':
                                                                $dotat = strpos($v,'.'); if ($dotat === false) $dotat = strpos($v,',');
@@ -697,6 +715,8 @@ class ADODB_DataDict {
                                // let INDEX keyword create a 'very standard' index on column
                                case 'INDEX': $findex = $v; break;
                                case 'UNIQUE': $funiqueindex = true; break;
+                               case 'ENUM':
+                                       $fOptions['ENUM'] = $v; break;
                                } //switch
                        } // foreach $fld
 
@@ -708,7 +728,7 @@ class ADODB_DataDict {
                        }
 
                        $fid = strtoupper(preg_replace('/^`(.+)`$/', '$1', $fname));
-                       $fname = $this->NameQuote($fname);
+                       $fname = $this->nameQuote($fname);
 
                        if (!strlen($ftype)) {
                                if ($this->debug) ADOConnection::outp("Undefined TYPE for field '$fname'");
@@ -717,14 +737,24 @@ class ADODB_DataDict {
                                $ftype = strtoupper($ftype);
                        }
 
-                       $ftype = $this->_GetSize($ftype, $ty, $fsize, $fprec);
+                       $ftype = $this->_getSize($ftype, $ty, $fsize, $fprec, $fOptions);
 
-                       if ($ty == 'X' || $ty == 'X2' || $ty == 'B') $fnotnull = false; // some blob types do not accept nulls
+                       if (($ty == 'X' || $ty == 'X2' || $ty == 'XL' || $ty == 'B') && !$this->blobAllowsNotNull)
+                               /*
+                               * some blob types do not accept nulls, so we override the
+                               * previously defined value
+                               */
+                               $fnotnull = false; 
 
-                       if ($fprimary) $pkey[] = $fname;
+                       if ($fprimary) 
+                               $pkey[] = $fname;
 
-                       // some databases do not allow blobs to have defaults
-                       if ($ty == 'X') $fdefault = false;
+                       if (($ty == 'X' || $ty == 'X2' || $ty == 'XL' || $ty == 'B') && !$this->blobAllowsDefaultValue)
+                               /*
+                               * some databases do not allow blobs to have defaults, so we
+                               * override the previously defined value
+                               */
+                               $fdefault = false;
 
                        // build list of indexes
                        if ($findex != '') {
@@ -769,11 +799,11 @@ class ADODB_DataDict {
                                                // convert default date into database-aware code
                                                if ($ty == 'T')
                                                {
-                                                       $fdefault = $this->connection->DBTimeStamp($fdefault);
+                                                       $fdefault = $this->connection->dbTimeStamp($fdefault);
                                                }
                                                else
                                                {
-                                                       $fdefault = $this->connection->DBDate($fdefault);
+                                                       $fdefault = $this->connection->dbDate($fdefault);
                                                }
                                        }
                                        else
@@ -783,7 +813,7 @@ class ADODB_DataDict {
                                                $fdefault = $this->connection->qstr($fdefault);
                                }
                        }
-                       $suffix = $this->_CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned);
+                       $suffix = $this->_createSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned);
 
                        // add index creation
                        if ($widespacing) $fname = str_pad($fname,24);
@@ -806,19 +836,38 @@ class ADODB_DataDict {
                        $ftype is the actual type
                        $ty is the type defined originally in the DDL
        */
-       function _GetSize($ftype, $ty, $fsize, $fprec)
+       function _getSize($ftype, $ty, $fsize, $fprec, $options=false)
        {
                if (strlen($fsize) && $ty != 'X' && $ty != 'B' && strpos($ftype,'(') === false) {
                        $ftype .= "(".$fsize;
                        if (strlen($fprec)) $ftype .= ",".$fprec;
                        $ftype .= ')';
                }
+               
+               /*
+               * Handle additional options
+               */
+               if (is_array($options))
+               {
+                       foreach($options as $type=>$value)
+                       {
+                               switch ($type)
+                               {
+                                       case 'ENUM':
+                                       $ftype .= '(' . $value . ')';
+                                       break;
+                                       
+                                       default:
+                               }
+                       }
+               }
+               
                return $ftype;
        }
 
 
        // return string must begin with space
-       function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+       function _createSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
        {
                $suffix = '';
                if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
@@ -827,7 +876,7 @@ class ADODB_DataDict {
                return $suffix;
        }
 
-       function _IndexSQL($idxname, $tabname, $flds, $idxoptions)
+       function _indexSQL($idxname, $tabname, $flds, $idxoptions)
        {
                $sql = array();
 
@@ -856,25 +905,26 @@ class ADODB_DataDict {
                return $sql;
        }
 
-       function _DropAutoIncrement($tabname)
+       function _dropAutoIncrement($tabname)
        {
                return false;
        }
 
-       function _TableSQL($tabname,$lines,$pkey,$tableoptions)
+       function _tableSQL($tabname,$lines,$pkey,$tableoptions)
        {
                $sql = array();
 
                if (isset($tableoptions['REPLACE']) || isset ($tableoptions['DROP'])) {
                        $sql[] = sprintf($this->dropTable,$tabname);
                        if ($this->autoIncrement) {
-                               $sInc = $this->_DropAutoIncrement($tabname);
+                               $sInc = $this->_dropAutoIncrement($tabname);
                                if ($sInc) $sql[] = $sInc;
                        }
                        if ( isset ($tableoptions['DROP']) ) {
                                return $sql;
                        }
                }
+               
                $s = "CREATE TABLE $tabname (\n";
                $s .= implode(",\n", $lines);
                if (sizeof($pkey)>0) {
@@ -898,7 +948,7 @@ class ADODB_DataDict {
                GENERATE TRIGGERS IF NEEDED
                used when table has auto-incrementing field that is emulated using triggers
        */
-       function _Triggers($tabname,$taboptions)
+       function _triggers($tabname,$taboptions)
        {
                return array();
        }
@@ -906,7 +956,7 @@ class ADODB_DataDict {
        /**
                Sanitize options, so that array elements with no keys are promoted to keys
        */
-       function _Options($opts)
+       function _options($opts)
        {
                if (!is_array($opts)) return array();
                $newopts = array();
@@ -938,25 +988,25 @@ class ADODB_DataDict {
        This function changes/adds new fields to your table. You don't
        have to know if the col is new or not. It will check on its own.
        */
-       function ChangeTableSQL($tablename, $flds, $tableoptions = false, $dropOldFlds=false)
+       function changeTableSQL($tablename, $flds, $tableoptions = false, $dropOldFlds=false)
        {
        global $ADODB_FETCH_MODE;
 
                $save = $ADODB_FETCH_MODE;
                $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
-               if ($this->connection->fetchMode !== false) $savem = $this->connection->SetFetchMode(false);
+               if ($this->connection->fetchMode !== false) $savem = $this->connection->setFetchMode(false);
 
                // check table exists
                $save_handler = $this->connection->raiseErrorFn;
                $this->connection->raiseErrorFn = '';
-               $cols = $this->MetaColumns($tablename);
+               $cols = $this->metaColumns($tablename);
                $this->connection->raiseErrorFn = $save_handler;
 
-               if (isset($savem)) $this->connection->SetFetchMode($savem);
+               if (isset($savem)) $this->connection->setFetchMode($savem);
                $ADODB_FETCH_MODE = $save;
 
                if ( empty($cols)) {
-                       return $this->CreateTableSQL($tablename, $flds, $tableoptions);
+                       return $this->createTableSQL($tablename, $flds, $tableoptions);
                }
 
                if (is_array($flds)) {
@@ -976,7 +1026,7 @@ class ADODB_DataDict {
 
                                        $c = $cols[$k];
                                        $ml = $c->max_length;
-                                       $mt = $this->MetaType($c->type,$ml);
+                                       $mt = $this->metaType($c->type,$ml);
 
                                        if (isset($c->scale)) $sc = $c->scale;
                                        else $sc = 99; // always force change if scale not known.
@@ -998,16 +1048,16 @@ class ADODB_DataDict {
 
 
                // already exists, alter table instead
-               list($lines,$pkey,$idxs) = $this->_GenFields($flds);
+               list($lines,$pkey,$idxs) = $this->_genFields($flds);
                // genfields can return FALSE at times
                if ($lines == null) $lines = array();
-               $alter = 'ALTER TABLE ' . $this->TableName($tablename);
+               $alter = 'ALTER TABLE ' . $this->tableName($tablename);
                $sql = array();
 
                foreach ( $lines as $id => $v ) {
                        if ( isset($cols[$id]) && is_object($cols[$id]) ) {
 
-                               $flds = Lens_ParseArgs($v,',');
+                               $flds = lens_ParseArgs($v,',');
 
                                //  We are trying to change the size of the field, if not allowed, simply ignore the request.
                                // $flds[1] holds the type, $flds[2] holds the size -postnuke addition
index dbec57e..b5a352c 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * @version   v5.20.16  12-Jan-2020
+ * @version   v5.21.0  2021-02-27
  * @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
  * @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
  * Released under both BSD license and Lesser GPL library license.
@@ -70,6 +70,10 @@ function adodb_error($provider,$dbType,$errno)
        case 'oracle':
        case 'oci8': $map = adodb_error_oci8(); break;
 
+       // As discussed in https://github.com/ADOdb/ADOdb/issues/201#issuecomment-188154980
+       // firebird uses the ibase error handler for now. This may change if and
+       // when the PHP driver is updated to use the new SQLSTATE error codes
+       case 'firebird':
        case 'ibase': $map = adodb_error_ibase(); break;
 
        case 'odbc': $map = adodb_error_odbc(); break;
@@ -245,9 +249,9 @@ static $MAP = array(
            1006 => DB_ERROR_CANNOT_CREATE,
            1007 => DB_ERROR_ALREADY_EXISTS,
            1008 => DB_ERROR_CANNOT_DROP,
-                  1045 => DB_ERROR_ACCESS_VIOLATION,
+          1045 => DB_ERROR_ACCESS_VIOLATION,
            1046 => DB_ERROR_NODBSELECTED,
-                  1049 => DB_ERROR_NOSUCHDB,
+          1049 => DB_ERROR_NOSUCHDB,
            1050 => DB_ERROR_ALREADY_EXISTS,
            1051 => DB_ERROR_NOSUCHTABLE,
            1054 => DB_ERROR_NOSUCHFIELD,
@@ -257,8 +261,8 @@ static $MAP = array(
            1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
            1146 => DB_ERROR_NOSUCHTABLE,
            1048 => DB_ERROR_CONSTRAINT,
-                   2002 => DB_ERROR_CONNECT_FAILED,
-                       2005 => DB_ERROR_CONNECT_FAILED
+          2002 => DB_ERROR_CONNECT_FAILED,
+           2005 => DB_ERROR_CONNECT_FAILED
        );
 
        return $MAP;
index a66f848..3b5f8ce 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * @version   v5.20.16  12-Jan-2020
+ * @version   v5.21.0  2021-02-27
  * @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
  * @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
  * Released under both BSD license and Lesser GPL library license.
@@ -9,7 +9,7 @@
  *
  * Set tabs to 4 for best viewing.
  *
- * Latest version is available at http://adodb.org/
+ * Latest version is available at https://adodb.org/
  *
 */
 
index f59f51d..d2daa75 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * @version   v5.20.16  12-Jan-2020
+ * @version   v5.21.0  2021-02-27
  * @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
  * @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
  * Released under both BSD license and Lesser GPL library license.
@@ -9,7 +9,7 @@
  *
  * Set tabs to 4 for best viewing.
  *
- * Latest version is available at http://adodb.org/
+ * Latest version is available at https://adodb.org/
  *
 */
 include_once('PEAR.php');
@@ -78,7 +78,7 @@ global $ADODB_Last_PEAR_Error;
 
 /**
 * Returns last PEAR_Error object. This error might be for an error that
-* occured several sql statements ago.
+* occurred several sql statements ago.
 */
 function ADODB_PEAR_Error()
 {
index 77d16f2..0583883 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * @version   v5.20.16  12-Jan-2020
+ * @version   v5.21.0  2021-02-27
  * @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
  * @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
  * Released under both BSD license and Lesser GPL library license.
@@ -10,7 +10,7 @@
  *
  * Set tabs to 4 for best viewing.
  *
- * Latest version is available at http://adodb.org/
+ * Latest version is available at https://adodb.org/
  *
  * Exception-handling code using PHP5 exceptions (try-catch-throw).
  */
@@ -60,14 +60,14 @@ var $database = '';
 }
 
 /**
-* Default Error Handler. This will be called with the following params
+* Default Error Handler.
 *
-* @param $dbms         the RDBMS you are connecting to
-* @param $fn           the name of the calling function (in uppercase)
-* @param $errno                the native error number from the database
-* @param $errmsg       the native error msg from the database
-* @param $p1           $fn specific parameter - see below
-* @param $P2           $fn specific parameter - see below
+* @param string $dbms    the RDBMS you are connecting to
+* @param string $fn      the name of the calling function (in uppercase)
+* @param int    $errno   the native error number from the database
+* @param string $errmsg  the native error msg from the database
+* @param mixed  $p1      $fn specific parameter - see below
+* @param mixed  $p2      $fn specific parameter - see below
 */
 
 function adodb_throw($dbms, $fn, $errno, $errmsg, $p1, $p2, $thisConnection)
index f8c4fb0..b5c9133 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /*
-  @version   v5.20.16  12-Jan-2020
+  @version   v5.21.0  2021-02-27
   @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
   @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
   Released under both BSD license and Lesser GPL library license.
index b9cfd3f..2dc4af4 100644 (file)
@@ -6,7 +6,7 @@ global $ADODB_INCLUDED_LIB;
 $ADODB_INCLUDED_LIB = 1;
 
 /*
-  @version   v5.20.16  12-Jan-2020
+  @version   v5.21.0  2021-02-27
   @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
   @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
   Released under both BSD license and Lesser GPL library license.
@@ -19,7 +19,12 @@ $ADODB_INCLUDED_LIB = 1;
 
 function adodb_strip_order_by($sql)
 {
-       $rez = preg_match('/(\sORDER\s+BY\s(?:[^)](?!LIMIT))*)/is', $sql, $arr);
+       $rez = preg_match_all('/(\sORDER\s+BY\s(?:[^)](?!LIMIT))*)/is', $sql, $arr);
+       if ($arr) 
+       {
+               $tmp = array_pop($arr);
+               $arr = [1=>array_pop($tmp)];
+       }
        if ($arr)
                if (strpos($arr[1], '(') !== false) {
                        $at = strpos($sql, $arr[1]);
@@ -39,6 +44,7 @@ function adodb_strip_order_by($sql)
                } else {
                        $sql = str_replace($arr[1], '', $sql);
                }
+
        return $sql;
 }
 
@@ -118,98 +124,79 @@ function  adodb_transpose(&$arr, &$newarr, &$hdr, &$fobjs)
        }
 }
 
-// Force key to upper.
-// See also http://www.php.net/manual/en/function.array-change-key-case.php
-function _array_change_key_case($an_array)
-{
-       if (is_array($an_array)) {
-               $new_array = array();
-               foreach($an_array as $key=>$value)
-                       $new_array[strtoupper($key)] = $value;
-
-               return $new_array;
-   }
-
-       return $an_array;
-}
 
 function _adodb_replace(&$zthis, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc)
 {
-               if (count($fieldArray) == 0) return 0;
-               $first = true;
-               $uSet = '';
+       // Add Quote around table name to support use of spaces / reserved keywords
+       $table=sprintf('%s%s%s', $zthis->nameQuote,$table,$zthis->nameQuote);
 
-               if (!is_array($keyCol)) {
-                       $keyCol = array($keyCol);
-               }
-               foreach($fieldArray as $k => $v) {
-                       if ($v === null) {
-                               $v = 'NULL';
-                               $fieldArray[$k] = $v;
-                       } else if ($autoQuote && /*!is_numeric($v) /*and strncmp($v,"'",1) !== 0 -- sql injection risk*/ strcasecmp($v,$zthis->null2null)!=0) {
-                               $v = $zthis->qstr($v);
-                               $fieldArray[$k] = $v;
-                       }
-                       if (in_array($k,$keyCol)) continue; // skip UPDATE if is key
+       if (count($fieldArray) == 0) return 0;
 
-                       if ($first) {
-                               $first = false;
-                               $uSet = "$k=$v";
-                       } else
-                               $uSet .= ",$k=$v";
+       if (!is_array($keyCol)) {
+               $keyCol = array($keyCol);
+       }
+       $uSet = '';
+       foreach($fieldArray as $k => $v) {
+               if ($v === null) {
+                       $v = 'NULL';
+                       $fieldArray[$k] = $v;
+               } else if ($autoQuote && /*!is_numeric($v) /*and strncmp($v,"'",1) !== 0 -- sql injection risk*/ strcasecmp($v,$zthis->null2null)!=0) {
+                       $v = $zthis->qstr($v);
+                       $fieldArray[$k] = $v;
                }
+               if (in_array($k,$keyCol)) continue; // skip UPDATE if is key
 
-               $where = false;
-               foreach ($keyCol as $v) {
-                       if (isset($fieldArray[$v])) {
-                               if ($where) $where .= ' and '.$v.'='.$fieldArray[$v];
-                               else $where = $v.'='.$fieldArray[$v];
-                       }
-               }
+               // Add Quote around column name to support use of spaces / reserved keywords
+               $uSet .= sprintf(',%s%s%s=%s',$zthis->nameQuote,$k,$zthis->nameQuote,$v);
+       }
+       $uSet = ltrim($uSet, ',');
 
-               if ($uSet && $where) {
-                       $update = "UPDATE $table SET $uSet WHERE $where";
+       // Add Quote around column name in where clause
+       $where = '';
+       foreach ($keyCol as $v) {
+               if (isset($fieldArray[$v])) {
+                       $where .= sprintf(' and %s%s%s=%s ', $zthis->nameQuote,$v,$zthis->nameQuote,$fieldArray[$v]);
+               }
+       }
+       if ($where) {
+               $where = substr($where, 5);
+       }
 
-                       $rs = $zthis->Execute($update);
+       if ($uSet && $where) {
+               $update = "UPDATE $table SET $uSet WHERE $where";
+               $rs = $zthis->Execute($update);
 
+               if ($rs) {
+                       if ($zthis->poorAffectedRows) {
+                               // The Select count(*) wipes out any errors that the update would have returned.
+                               // PHPLens Issue No: 5696
+                               if ($zthis->ErrorNo()<>0) return 0;
 
-                       if ($rs) {
-                               if ($zthis->poorAffectedRows) {
-                               /*
-                                The Select count(*) wipes out any errors that the update would have returned.
-                               http://phplens.com/lens/lensforum/msgs.php?id=5696
-                               */
-                                       if ($zthis->ErrorNo()<>0) return 0;
+                               // affected_rows == 0 if update field values identical to old values
+                               // for mysql - which is silly.
+                               $cnt = $zthis->GetOne("select count(*) from $table where $where");
+                               if ($cnt > 0) return 1; // record already exists
+                       } else {
+                               if (($zthis->Affected_Rows()>0)) return 1;
+                       }
+               } else
+                       return 0;
+       }
 
-                               # affected_rows == 0 if update field values identical to old values
-                               # for mysql - which is silly.
+       $iCols = $iVals = '';
+       foreach($fieldArray as $k => $v) {
+               if ($has_autoinc && in_array($k,$keyCol)) continue; // skip autoinc col
 
-                                       $cnt = $zthis->GetOne("select count(*) from $table where $where");
-                                       if ($cnt > 0) return 1; // record already exists
-                               } else {
-                                       if (($zthis->Affected_Rows()>0)) return 1;
-                               }
-                       } else
-                               return 0;
-               }
-
-       //      print "<p>Error=".$this->ErrorNo().'<p>';
-               $first = true;
-               foreach($fieldArray as $k => $v) {
-                       if ($has_autoinc && in_array($k,$keyCol)) continue; // skip autoinc col
+               // Add Quote around Column Name
+               $iCols .= sprintf(',%s%s%s',$zthis->nameQuote,$k,$zthis->nameQuote);
+               $iVals .= ",$v";
+       }
+       $iCols = ltrim($iCols, ',');
+       $iVals = ltrim($iVals, ',');
 
-                       if ($first) {
-                               $first = false;
-                               $iCols = "$k";
-                               $iVals = "$v";
-                       } else {
-                               $iCols .= ",$k";
-                               $iVals .= ",$v";
-                       }
-               }
-               $insert = "INSERT INTO $table ($iCols) VALUES ($iVals)";
-               $rs = $zthis->Execute($insert);
-               return ($rs) ? 2 : 0;
+       $insert = "INSERT INTO $table ($iCols) VALUES ($iVals)";
+       $rs = $zthis->Execute($insert);
+       return ($rs) ? 2 : 0;
 }
 
 function _adodb_getmenu(&$zthis, $name,$defstr='',$blank1stItem=true,$multiple=false,
@@ -441,7 +428,7 @@ function _adodb_getcount(&$zthis, $sql,$inputarr=false,$secs2cache=0)
                }
                // fix by alexander zhukov, alex#unipack.ru, because count(*) and 'order by' fails
                // with mssql, access and postgresql. Also a good speedup optimization - skips sorting!
-               // also see http://phplens.com/lens/lensforum/msgs.php?id=12752
+               // also see PHPLens Issue No: 12752
                $rewritesql = adodb_strip_order_by($rewritesql);
        }
 
@@ -476,18 +463,11 @@ function _adodb_getcount(&$zthis, $sql,$inputarr=false,$secs2cache=0)
                if (!$rstest) $rstest = $zthis->Execute($sql,$inputarr);
        }
        if ($rstest) {
-                       $qryRecs = $rstest->RecordCount();
+               $qryRecs = $rstest->RecordCount();
                if ($qryRecs == -1) {
-               global $ADODB_EXTENSION;
-               // some databases will return -1 on MoveLast() - change to MoveNext()
-                       if ($ADODB_EXTENSION) {
-                               while(!$rstest->EOF) {
-                                       adodb_movenext($rstest);
-                               }
-                       } else {
-                               while(!$rstest->EOF) {
-                                       $rstest->MoveNext();
-                               }
+                       // some databases will return -1 on MoveLast() - change to MoveNext()
+                       while(!$rstest->EOF) {
+                               $rstest->MoveNext();
                        }
                        $qryRecs = $rstest->_currentRow;
                }
@@ -650,58 +630,59 @@ function _adodb_pageexecute_no_last_page(&$zthis, $sql, $nrows, $page, $inputarr
        return $rsreturn;
 }
 
-function _adodb_getupdatesql(&$zthis,&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=2)
+function _adodb_getupdatesql(&$zthis, &$rs, $arrFields, $forceUpdate=false, $force=2)
 {
        global $ADODB_QUOTE_FIELDNAMES;
 
-               if (!$rs) {
-                       printf(ADODB_BAD_RS,'GetUpdateSQL');
-                       return false;
-               }
-
-               $fieldUpdatedCount = 0;
-               $arrFields = _array_change_key_case($arrFields);
-
-               $hasnumeric = isset($rs->fields[0]);
-               $setFields = '';
+       if (!$rs) {
+               printf(ADODB_BAD_RS,'GetUpdateSQL');
+               return false;
+       }
 
-               // Loop through all of the fields in the recordset
-               for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
-                       // Get the field from the recordset
-                       $field = $rs->FetchField($i);
+       $fieldUpdatedCount = 0;
+       if (is_array($arrFields))
+               $arrFields = array_change_key_case($arrFields,CASE_UPPER);
 
-                       // If the recordset field is one
-                       // of the fields passed in then process.
-                       $upperfname = strtoupper($field->name);
-                       if (adodb_key_exists($upperfname,$arrFields,$force)) {
+       $hasnumeric = isset($rs->fields[0]);
+       $setFields = '';
 
-                               // If the existing field value in the recordset
-                               // is different from the value passed in then
-                               // go ahead and append the field name and new value to
-                               // the update query.
+       // Loop through all of the fields in the recordset
+       for ($i=0, $max=$rs->fieldCount(); $i < $max; $i++) {
+               // Get the field from the recordset
+               $field = $rs->fetchField($i);
 
-                               if ($hasnumeric) $val = $rs->fields[$i];
-                               else if (isset($rs->fields[$upperfname])) $val = $rs->fields[$upperfname];
-                               else if (isset($rs->fields[$field->name])) $val =  $rs->fields[$field->name];
-                               else if (isset($rs->fields[strtolower($upperfname)])) $val =  $rs->fields[strtolower($upperfname)];
-                               else $val = '';
+               // If the recordset field is one
+               // of the fields passed in then process.
+               $upperfname = strtoupper($field->name);
+               if (adodb_key_exists($upperfname, $arrFields, $force)) {
 
+                       // If the existing field value in the recordset
+                       // is different from the value passed in then
+                       // go ahead and append the field name and new value to
+                       // the update query.
 
-                               if ($forceUpdate || strcmp($val, $arrFields[$upperfname])) {
-                                       // Set the counter for the number of fields that will be updated.
-                                       $fieldUpdatedCount++;
+                       if ($hasnumeric) $val = $rs->fields[$i];
+                       else if (isset($rs->fields[$upperfname])) $val = $rs->fields[$upperfname];
+                       else if (isset($rs->fields[$field->name])) $val =  $rs->fields[$field->name];
+                       else if (isset($rs->fields[strtolower($upperfname)])) $val =  $rs->fields[strtolower($upperfname)];
+                       else $val = '';
 
-                                       // Based on the datatype of the field
-                                       // Format the value properly for the database
-                                       $type = $rs->MetaType($field->type);
+                       if ($forceUpdate || strcmp($val, $arrFields[$upperfname])) {
+                               // Set the counter for the number of fields that will be updated.
+                               $fieldUpdatedCount++;
 
+                               // Based on the datatype of the field
+                               // Format the value properly for the database
+                               $type = $rs->metaType($field->type);
 
-                                       if ($type == 'null') {
-                                               $type = 'C';
-                                       }
+                               if ($type == 'null') {
+                                       $type = 'C';
+                               }
 
-                                       if ((strpos($upperfname,' ') !== false) || ($ADODB_QUOTE_FIELDNAMES)) {
-                                               switch ($ADODB_QUOTE_FIELDNAMES) {
+                               if ((strpos($upperfname,' ') !== false) || ($ADODB_QUOTE_FIELDNAMES)) {
+                                       switch ($ADODB_QUOTE_FIELDNAMES) {
+                                               case 'BRACKETS':
+                                                       $fnameq = $zthis->leftBracket.$upperfname.$zthis->rightBracket;break;
                                                case 'LOWER':
                                                        $fnameq = $zthis->nameQuote.strtolower($field->name).$zthis->nameQuote;break;
                                                case 'NATIVE':
@@ -709,89 +690,107 @@ function _adodb_getupdatesql(&$zthis,&$rs, $arrFields,$forceUpdate=false,$magicq
                                                case 'UPPER':
                                                default:
                                                        $fnameq = $zthis->nameQuote.$upperfname.$zthis->nameQuote;break;
-                                               }
-                                       } else
-                                               $fnameq = $upperfname;
+                                       }
+                               } else {
+                                       $fnameq = $upperfname;
+                               }
 
-                //********************************************************//
-                if (is_null($arrFields[$upperfname])
+                               //********************************************************//
+                               if (is_null($arrFields[$upperfname])
                                        || (empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0)
-                    || $arrFields[$upperfname] === $zthis->null2null
-                    )
-                {
-                    switch ($force) {
+                                       || $arrFields[$upperfname] === $zthis->null2null
+                                       ) {
 
-                        //case 0:
-                        //    //Ignore empty values. This is allready handled in "adodb_key_exists" function.
-                        //break;
+                                       switch ($force) {
 
-                        case 1:
-                            //Set null
-                            $setFields .= $field->name . " = null, ";
-                        break;
+                                               //case 0:
+                                               //      // Ignore empty values. This is already handled in "adodb_key_exists" function.
+                                               //      break;
+
+                                               case 1:
+                                                       // set null
+                                                       $setFields .= $fnameq . " = null, ";
+                                                       break;
+
+                                               case 2:
+                                                       // set empty
+                                                       $arrFields[$upperfname] = "";
+                                                       $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields);
+                                                       break;
 
-                        case 2:
-                            //Set empty
-                            $arrFields[$upperfname] = "";
-                            $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,$arrFields, $magicq);
-                        break;
                                                default:
-                        case 3:
-                            //Set the value that was given in array, so you can give both null and empty values
-                            if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === $zthis->null2null) {
-                                $setFields .= $field->name . " = null, ";
-                            } else {
-                                $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,$arrFields, $magicq);
-                            }
-                        break;
-                    }
-                //********************************************************//
-                } else {
-                                               //we do this so each driver can customize the sql for
-                                               //DB specific column types.
-                                               //Oracle needs BLOB types to be handled with a returning clause
-                                               //postgres has special needs as well
-                                               $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq,
-                                                                                                                 $arrFields, $magicq);
+                                               case 3:
+                                                       // set the value that was given in array, so you can give both null and empty values
+                                                       if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === $zthis->null2null) {
+                                                               $setFields .= $fnameq . " = null, ";
+                                                       } else {
+                                                               $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields);
+                                                       }
+                                                       break;
+
+                                               case ADODB_FORCE_NULL_AND_ZERO:
+
+                                                       switch ($type) {
+                                                               case 'N':
+                                                               case 'I':
+                                                               case 'L':
+                                                                       $setFields .= $fnameq . ' = 0, ';
+                                                                       break;
+                                                               default:
+                                                                       $setFields .= $fnameq . ' = null, ';
+                                                                       break;
+                                                       }
+                                                       break;
+
                                        }
+                               //********************************************************//
+                               } else {
+                                       // we do this so each driver can customize the sql for
+                                       // DB specific column types.
+                                       // Oracle needs BLOB types to be handled with a returning clause
+                                       // postgres has special needs as well
+                                       $setFields .= _adodb_column_sql($zthis, 'U', $type, $upperfname, $fnameq, $arrFields);
                                }
                        }
                }
+       }
 
-               // If there were any modified fields then build the rest of the update query.
-               if ($fieldUpdatedCount > 0 || $forceUpdate) {
-                                       // Get the table name from the existing query.
-                       if (!empty($rs->tableName)) $tableName = $rs->tableName;
-                       else {
-                               preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName);
-                               $tableName = $tableName[1];
-                       }
-                       // Get the full where clause excluding the word "WHERE" from
-                       // the existing query.
-                       preg_match('/\sWHERE\s(.*)/is', $rs->sql, $whereClause);
-
-                       $discard = false;
-                       // not a good hack, improvements?
-                       if ($whereClause) {
-                       #var_dump($whereClause);
-                               if (preg_match('/\s(ORDER\s.*)/is', $whereClause[1], $discard));
-                               else if (preg_match('/\s(LIMIT\s.*)/is', $whereClause[1], $discard));
-                               else if (preg_match('/\s(FOR UPDATE.*)/is', $whereClause[1], $discard));
-                               else preg_match('/\s.*(\) WHERE .*)/is', $whereClause[1], $discard); # see https://sourceforge.net/p/adodb/bugs/37/
-                       } else
-                               $whereClause = array(false,false);
+       // If there were any modified fields then build the rest of the update query.
+       if ($fieldUpdatedCount > 0 || $forceUpdate) {
+               // Get the table name from the existing query.
+               if (!empty($rs->tableName)) {
+                       $tableName = $rs->tableName;
+               } else {
+                       preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName);
+                       $tableName = $tableName[1];
+               }
 
-                       if ($discard)
-                               $whereClause[1] = substr($whereClause[1], 0, strlen($whereClause[1]) - strlen($discard[1]));
+               // Get the full where clause excluding the word "WHERE" from the existing query.
+               preg_match('/\sWHERE\s(.*)/is', $rs->sql, $whereClause);
 
-                       $sql = 'UPDATE '.$tableName.' SET '.substr($setFields, 0, -2);
-                       if (strlen($whereClause[1]) > 0)
-                               $sql .= ' WHERE '.$whereClause[1];
+               $discard = false;
+               // not a good hack, improvements?
+               if ($whereClause) {
+                       #var_dump($whereClause);
+                       if (preg_match('/\s(ORDER\s.*)/is', $whereClause[1], $discard));
+                       else if (preg_match('/\s(LIMIT\s.*)/is', $whereClause[1], $discard));
+                       else if (preg_match('/\s(FOR UPDATE.*)/is', $whereClause[1], $discard));
+                       else preg_match('/\s.*(\) WHERE .*)/is', $whereClause[1], $discard); # see https://sourceforge.net/p/adodb/bugs/37/
+               } else {
+                       $whereClause = array(false, false);
+               }
 
-                       return $sql;
+               if ($discard) {
+                       $whereClause[1] = substr($whereClause[1], 0, strlen($whereClause[1]) - strlen($discard[1]));
+               }
 
-               } else {
-                       return false;
+               $sql = 'UPDATE '.$tableName.' SET '.substr($setFields, 0, -2);
+               if (strlen($whereClause[1]) > 0) {
+                       $sql .= ' WHERE '.$whereClause[1];
+               }
+               return $sql;
+       } else {
+               return false;
        }
 }
 
@@ -802,10 +801,10 @@ function adodb_key_exists($key, &$arr,$force=2)
                return (!empty($arr[$key])) || (isset($arr[$key]) && strlen($arr[$key])>0);
        }
 
-       if (isset($arr[$key])) return true;
+       if (isset($arr[$key])) 
+               return true;
        ## null check below
-       if (ADODB_PHPVER >= 0x4010) return array_key_exists($key,$arr);
-       return false;
+       return array_key_exists($key,$arr);
 }
 
 /**
@@ -815,7 +814,7 @@ function adodb_key_exists($key, &$arr,$force=2)
  *
  *
  */
-function _adodb_getinsertsql(&$zthis,&$rs,$arrFields,$magicq=false,$force=2)
+function _adodb_getinsertsql(&$zthis, &$rs, $arrFields, $force=2)
 {
 static $cacheRS = false;
 static $cacheSig = 0;
@@ -826,7 +825,8 @@ static $cacheCols;
        $values = '';
        $fields = '';
        $recordSet = null;
-       $arrFields = _array_change_key_case($arrFields);
+       if (is_array($arrFields))
+               $arrFields = array_change_key_case($arrFields,CASE_UPPER);
        $fieldInsertedCount = 0;
 
        if (is_string($rs)) {
@@ -872,6 +872,8 @@ static $cacheCols;
                        $bad = false;
                        if ((strpos($upperfname,' ') !== false) || ($ADODB_QUOTE_FIELDNAMES)) {
                                switch ($ADODB_QUOTE_FIELDNAMES) {
+                               case 'BRACKETS':
+                                       $fnameq = $zthis->leftBracket.$upperfname.$zthis->rightBracket;break;
                                case 'LOWER':
                                        $fnameq = $zthis->nameQuote.strtolower($field->name).$zthis->nameQuote;break;
                                case 'NATIVE':
@@ -893,29 +895,44 @@ static $cacheCols;
                {
                     switch ($force) {
 
-                        case 0: // we must always set null if missing
+                        case ADODB_FORCE_IGNORE: // we must always set null if missing
                                                        $bad = true;
                                                        break;
 
-                        case 1:
+                        case ADODB_FORCE_NULL:
                             $values  .= "null, ";
                         break;
 
-                        case 2:
+                        case ADODB_FORCE_EMPTY:
                             //Set empty
                             $arrFields[$upperfname] = "";
-                            $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq,$arrFields, $magicq);
+                                                       $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields);
                         break;
 
                                                default:
-                        case 3:
+                        case ADODB_FORCE_VALUE:
                             //Set the value that was given in array, so you can give both null and empty values
                                                        if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] === $zthis->null2null) {
                                                                $values  .= "null, ";
                                                        } else {
-                                       $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields, $magicq);
+                                                               $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields);
                                        }
                                break;
+
+                                               case ADODB_FORCE_NULL_AND_ZERO:
+                                                       switch ($type)
+                                                       {
+                                                               case 'N':
+                                                               case 'I':
+                                                               case 'L':
+                                                                       $values .= '0, ';
+                                                                       break;
+                                                               default:
+                                                                       $values .= "null, ";
+                                                                       break;
+                                                       }
+                                               break;
+
                        } // switch
 
             /*********************************************************/
@@ -924,8 +941,7 @@ static $cacheCols;
                                //DB specific column types.
                                //Oracle needs BLOB types to be handled with a returning clause
                                //postgres has special needs as well
-                               $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq,
-                                                                                          $arrFields, $magicq);
+                               $values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq, $arrFields);
                        }
 
                        if ($bad) continue;
@@ -976,7 +992,7 @@ static $cacheCols;
  * @return string
  *
  */
-function _adodb_column_sql_oci8(&$zthis,$action, $type, $fname, $fnameq, $arrFields, $magicq)
+function _adodb_column_sql_oci8(&$zthis,$action, $type, $fname, $fnameq, $arrFields)
 {
     $sql = '';
 
@@ -1008,7 +1024,7 @@ function _adodb_column_sql_oci8(&$zthis,$action, $type, $fname, $fnameq, $arrFie
         } else {
             //this is to maintain compatibility
             //with older adodb versions.
-            $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq,false);
+                       $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, false);
         }
         break;
 
@@ -1031,19 +1047,19 @@ function _adodb_column_sql_oci8(&$zthis,$action, $type, $fname, $fnameq, $arrFie
         } else {
             //this is to maintain compatibility
             //with older adodb versions.
-            $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq,false);
+                       $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq, $arrFields, false);
         }
         break;
 
     default:
-        $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq,  $arrFields, $magicq,false);
+               $sql = _adodb_column_sql($zthis, $action, $type, $fname, $fnameq,  $arrFields, false);
         break;
     }
 
     return $sql;
 }
 
-function _adodb_column_sql(&$zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq, $recurse=true)
+function _adodb_column_sql(&$zthis, $action, $type, $fname, $fnameq, $arrFields, $recurse=true)
 {
 
        if ($recurse) {
@@ -1052,7 +1068,7 @@ function _adodb_column_sql(&$zthis, $action, $type, $fname, $fnameq, $arrFields,
                        if ($type == 'L') $type = 'C';
                        break;
                case 'oci8':
-                       return _adodb_column_sql_oci8($zthis, $action, $type, $fname, $fnameq, $arrFields, $magicq);
+                       return _adodb_column_sql_oci8($zthis, $action, $type, $fname, $fnameq, $arrFields);
 
                }
        }
@@ -1061,7 +1077,7 @@ function _adodb_column_sql(&$zthis, $action, $type, $fname, $fnameq, $arrFields,
                case "C":
                case "X":
                case 'B':
-                       $val = $zthis->qstr($arrFields[$fname],$magicq);
+                       $val = $zthis->qstr($arrFields[$fname]);
                        break;
 
                case "D":
@@ -1091,9 +1107,7 @@ function _adodb_column_sql(&$zthis, $action, $type, $fname, $fnameq, $arrFields,
 
        if ($action == 'I') return $val . ", ";
 
-
        return $fnameq . "=" . $val  . ", ";
-
 }
 
 
diff --git a/lib/adodb/adodb-loadbalancer.inc.php b/lib/adodb/adodb-loadbalancer.inc.php
new file mode 100644 (file)
index 0000000..c0e3de0
--- /dev/null
@@ -0,0 +1,773 @@
+<?php
+/**
+ * ADOdb Load Balancer
+ *
+ * ADOdbLoadBalancer is a class that allows the user to do read/write splitting
+ * and load balancing across multiple servers. It can handle and load balance
+ * any number of write capable (AKA: master) or readonly (AKA: slave) connections,
+ * including dealing with connection failures and retrying queries on a different
+ * connection instead.
+ *
+ * Released under both BSD license and Lesser GPL library license.
+ * Whenever there is any discrepancy between the two licenses,
+ * the BSD license will take precedence. See LICENSE.md.
+ *
+ * Latest version is available at https://adodb.org/
+ *
+ * @package   ADOdb
+ * @version   v5.21.0  2021-02-27
+ * @author    Mike Benoit
+ * @copyright (c) 2016 Mike Benoit and the ADOdb community
+ * @license   BSD-3-Clause
+ * @license   GNU Lesser General Public License (LGPL) v2.1 or later
+ * @link      https://adodb.org/
+ * @since     v5.21.0
+ */
+
+/**
+ * Class ADOdbLoadBalancer
+ */
+class ADOdbLoadBalancer
+{
+    /**
+     * @var bool    Once a write or readonly connection is made, stick to that connection for the entire request.
+     */
+    public $enable_sticky_sessions = true;
+
+
+    /**
+     * @var bool|array    All connections to each database.
+     */
+    protected $connections = false;
+
+    /**
+     * @var bool|array    Just connections to the write capable database.
+     */
+    protected $connections_write = false;
+
+    /**
+     * @var bool|array    Just connections to the readonly database.
+     */
+    protected $connections_readonly = false;
+
+    /**
+     * @var array    Counts of all connections and their types.
+     */
+    protected $total_connections = array('all' => 0, 'write' => 0, 'readonly' => 0);
+
+    /**
+     * @var array    Weights of all connections for each type.
+     */
+    protected $total_connection_weights = array('all' => 0, 'write' => 0, 'readonly' => 0);
+
+    /**
+     * @var bool    When in transactions, always use this connection.
+     */
+    protected $pinned_connection_id = false;
+
+    /**
+     * @var array    Last connection_id for each database type.
+     */
+    protected $last_connection_id = array('write' => false, 'readonly' => false, 'all' => false);
+
+    /**
+     * @var bool    Session variables that must be maintained across all connections, ie: SET TIME ZONE.
+     */
+    protected $session_variables = false;
+
+    /**
+     * @var bool    Called immediately after connecting to any DB.
+     */
+    protected $user_defined_session_init_sql = false;
+
+
+    /**
+     * Defines SQL queries that are executed each time a new database connection is established.
+     *
+     * @param  $sql
+     * @return bool
+     */
+    public function setSessionInitSQL($sql)
+    {
+        $this->user_defined_session_init_sql[] = $sql;
+
+        return true;
+    }
+
+    /**
+     * Adds a new database connection to the pool, but no actual connection is made until its needed.
+     *
+     * @param  $obj
+     * @return bool
+     * @throws Exception
+     */
+    public function addConnection($obj)
+    {
+        if ($obj instanceof ADOdbLoadBalancerConnection) {
+            $this->connections[] = $obj;
+            end($this->connections);
+            $i = key($this->connections);
+
+            $this->total_connections[$obj->type]++;
+            $this->total_connections['all']++;
+
+            $this->total_connection_weights[$obj->type] += abs($obj->weight);
+            $this->total_connection_weights['all'] += abs($obj->weight);
+
+            if ($obj->type == 'write') {
+                $this->connections_write[] = $i;
+            } else {
+                $this->connections_readonly[] = $i;
+            }
+
+            return true;
+        }
+
+        throw new Exception('Connection object is not an instance of ADOdbLoadBalancerConnection');
+    }
+
+    /**
+     * Removes a database connection from the pool.
+     *
+     * @param  $i
+     * @return bool
+     */
+    public function removeConnection($i)
+    {
+        if (isset($this->connections[$i])) {
+               $obj = $this->connections[ $i ];
+
+               $this->total_connections[ $obj->type ]--;
+               $this->total_connections['all']--;
+
+               $this->total_connection_weights[ $obj->type ] -= abs($obj->weight);
+               $this->total_connection_weights['all'] -= abs($obj->weight);
+
+            if ($obj->type == 'write') {
+                unset($this->connections_write[array_search($i, $this->connections_write)]);
+                // Reindex array.
+                $this->connections_write = array_values($this->connections_write);
+            } else {
+                unset($this->connections_readonly[array_search($i, $this->connections_readonly)]);
+                // Reindex array.
+                $this->connections_readonly = array_values($this->connections_readonly);
+            }
+
+            // Remove any sticky connections as well.
+            if ($this->last_connection_id[$obj->type] == $i) {
+                $this->last_connection_id[$obj->type] = false;
+            }
+
+            unset($this->connections[$i]);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns a database connection of the specified type.
+     *
+     * Takes into account the connection weight for load balancing.
+     *
+     * @param  string $type Type of database connection, either: 'write' capable or 'readonly'
+     * @return bool|int|string
+     */
+    private function getConnectionByWeight($type)
+    {
+        if ($type == 'readonly') {
+            $total_weight = $this->total_connection_weights['all'];
+        } else {
+            $total_weight = $this->total_connection_weights['write'];
+        }
+
+        $i = false;
+        if (is_array($this->connections)) {
+            $n = 0;
+            $num = mt_rand(0, $total_weight);
+            foreach ($this->connections as $i => $connection_obj) {
+                if ($connection_obj->weight > 0 && ($type == 'readonly' || $connection_obj->type == 'write')) {
+                    $n += $connection_obj->weight;
+                    if ($n >= $num) {
+                        break;
+                    }
+                }
+            }
+        }
+
+        return $i;
+    }
+
+    /**
+     * Returns the proper database connection when taking into account sticky sessions and load balancing.
+     *
+     * @param  $type
+     * @return bool|int|mixed|string
+     */
+    public function getLoadBalancedConnection($type)
+    {
+        if ($this->total_connections == 0) {
+            $connection_id = 0;
+        } else {
+            if ($this->enable_sticky_sessions == true && $this->last_connection_id[$type] !== false) {
+                $connection_id = $this->last_connection_id[$type];
+            } else {
+                if ($type == 'write' && $this->total_connections['write'] == 1) {
+                    $connection_id = $this->connections_write[0];
+                } else {
+                    $connection_id = $this->getConnectionByWeight($type);
+                }
+            }
+        }
+
+        return $connection_id;
+    }
+
+    /**
+     * Returns the ADODB connection object by connection_id.
+     *
+     * Ensures that it's connected and the session variables are executed.
+     *
+     * @param  $connection_id
+     * @return bool|ADOConnection
+     * @throws Exception
+     */
+    private function _getConnection($connection_id)
+    {
+        if (isset($this->connections[$connection_id])) {
+            $connection_obj = $this->connections[$connection_id];
+            /** @var ADOConnection $adodb_obj */
+            $adodb_obj = $connection_obj->getADOdbObject();
+            if (is_object($adodb_obj) && $adodb_obj->_connectionID == false) {
+                try {
+                    if ($connection_obj->persistent_connection == true) {
+                        $adodb_obj->Pconnect(
+                            $connection_obj->host,
+                            $connection_obj->user,
+                            $connection_obj->password,
+                            $connection_obj->database
+                        );
+                    } else {
+                        $adodb_obj->Connect(
+                            $connection_obj->host,
+                            $connection_obj->user,
+                            $connection_obj->password,
+                            $connection_obj->database
+                        );
+                    }
+                } catch (Exception $e) {
+                    // Connection error, see if there are other connections to try still.
+                    throw $e; // No connections left, reThrow exception so application can catch it.
+                }
+
+                if (is_array($this->user_defined_session_init_sql)) {
+                    foreach ($this->user_defined_session_init_sql as $session_init_sql) {
+                        $adodb_obj->Execute($session_init_sql);
+                    }
+                }
+                $this->executeSessionVariables($adodb_obj);
+            }
+
+            return $adodb_obj;
+        } else {
+            throw new Exception('Unable to return Connection object...');
+        }
+    }
+
+    /**
+     * Returns the ADODB connection object by database type.
+     *
+     * Ensures that it's connected and the session variables are executed.
+     *
+     * @param  string $type
+     * @param  null   $pin_connection
+     * @return ADOConnection|bool
+     * @throws Exception
+     */
+    public function getConnection($type = 'write', $pin_connection = null)
+    {
+        while (($type == 'write' && $this->total_connections['write'] > 0)
+            || ($type == 'readonly' && $this->total_connections['all'] > 0)
+        ) {
+            if ($this->pinned_connection_id !== false) {
+                $connection_id = $this->pinned_connection_id;
+            } else {
+                $connection_id = $this->getLoadBalancedConnection($type);
+            }
+
+            if ($connection_id !== false) {
+                try {
+                    $adodb_obj = $this->_getConnection($connection_id);
+                    // $connection_obj = $this->connections[$connection_id];
+                    break;
+                } catch (Exception $e) {
+                    // Connection error, see if there are other connections to try still.
+                    $this->removeConnection($connection_id);
+                    if (   ($type == 'write' && $this->total_connections['write'] == 0)
+                        || ($type == 'readonly' && $this->total_connections['all'] == 0)
+                    ) {
+                        throw $e;
+                    }
+                }
+            } else {
+                throw new Exception('Connection ID is invalid!');
+            }
+        }
+
+        $this->last_connection_id[$type] = $connection_id;
+
+        if ($pin_connection === true) {
+            $this->pinned_connection_id = $connection_id;
+        } elseif ($pin_connection === false && $adodb_obj->transOff <= 1) {
+            // UnPin connection only if we are 1 level deep in a transaction.
+            $this->pinned_connection_id = false;
+
+            // When unpinning connection, reset last_connection_id so readonly
+            // queries don't get stuck on the write capable connection.
+            $this->last_connection_id['write'] = false;
+            $this->last_connection_id['readonly'] = false;
+        }
+
+        return $adodb_obj;
+    }
+
+    /**
+     * This is a hack to work around pass by reference error.
+     *
+     * Parameter 1 to ADOConnection::GetInsertSQL() expected to be a reference,
+     * value given in adodb-loadbalancer.inc.php on line 83
+     *
+     * @param  $arr
+     * @return array
+     */
+    private function makeValuesReferenced($arr)
+    {
+        $refs = array();
+
+        foreach ($arr as $key => $value) {
+            $refs[$key] = &$arr[$key];
+        }
+
+        return $refs;
+    }
+
+    /**
+     * Allow setting session variables that are maintained across connections.
+     *
+     * Its important that these are set using name/value, so it can determine
+     * if the same variable is set multiple times causing bloat/clutter when
+     * new connections are established. For example if the time_zone is set to
+     * many different ones through the course of a single connection, a new
+     * connection should only set it to the most recent value.
+     *
+     * @param  $name
+     * @param  $value
+     * @param  bool  $execute_immediately
+     * @return array|bool|mixed
+     * @throws Exception
+     */
+    public function setSessionVariable($name, $value, $execute_immediately = true)
+    {
+        $this->session_variables[$name] = $value;
+
+        if ($execute_immediately == true) {
+            return $this->executeSessionVariables();
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Executes the session variables on a given ADODB object.
+     *
+     * @param  ADOConnection|bool $adodb_obj
+     * @return array|bool|mixed
+     * @throws Exception
+     */
+    private function executeSessionVariables($adodb_obj = false)
+    {
+        if (is_array($this->session_variables)) {
+            $sql = '';
+            foreach ($this->session_variables as $name => $value) {
+                // $sql .= 'SET SESSION '. $name .' '. $value;
+                // MySQL uses: SET SESSION foo_bar='foo'
+                // PGSQL uses: SET SESSION foo_bar 'foo'
+                // So leave it up to the user to pass the proper value with '=' if needed.
+                // This may be a candidate to move into ADOdb proper.
+                $sql .= 'SET SESSION ' . $name . ' ' . $value;
+            }
+
+            if ($adodb_obj !== false) {
+                return $adodb_obj->Execute($sql);
+            } else {
+                return $this->ClusterExecute($sql);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Executes the same SQL QUERY on the entire cluster of connections.
+     * Would be used for things like SET SESSION TIME ZONE calls and such.
+     *
+     * @param  $sql
+     * @param  bool $inputarr
+     * @param  bool $return_all_results
+     * @param  bool $existing_connections_only
+     * @return array|bool|mixed
+     * @throws Exception
+     */
+    public function clusterExecute(
+        $sql,
+        $inputarr = false,
+        $return_all_results = false,
+        $existing_connections_only = true
+    ) {
+        if (is_array($this->connections) && count($this->connections) > 0) {
+            foreach ($this->connections as $key => $connection_obj) {
+                if ($existing_connections_only == false
+                    || ($existing_connections_only == true
+                        && $connection_obj->getADOdbObject()->_connectionID !== false
+                    )
+                ) {
+                    $adodb_obj = $this->_getConnection($key);
+                    if (is_object($adodb_obj)) {
+                        $result_arr[] = $adodb_obj->Execute($sql, $inputarr);
+                    }
+                }
+            }
+
+            if (isset($result_arr) && $return_all_results == true) {
+                return $result_arr;
+            } else {
+                // Loop through all results checking to see if they match, if they do return the first one
+                // otherwise return an array of all results.
+                if (isset($result_arr)) {
+                    foreach ($result_arr as $result) {
+                        if ($result == false) {
+                            return $result_arr;
+                        }
+                    }
+
+                    return $result_arr[0];
+                } else {
+                    // When using lazy connections, there are cases where
+                    // setSessionVariable() is called early on, but there are
+                    // no connections to execute the queries on yet.
+                    // This captures that case and forces a RETURN TRUE to occur.
+                    // As likely the queries will be executed as soon as a
+                    // connection is established.
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determines if a SQL query is read-only or not.
+     *
+     * @param  string $sql SQL Query to test.
+     * @return bool
+     */
+    public function isReadOnlyQuery($sql)
+    {
+        if (   stripos($sql, 'SELECT') === 0
+            && stripos($sql, 'FOR UPDATE') === false
+            && stripos($sql, ' INTO ') === false
+            && stripos($sql, 'LOCK IN') === false
+        ) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Use this instead of __call() as it significantly reduces the overhead of call_user_func_array().
+     *
+     * @param  $sql
+     * @param  bool $inputarr
+     * @return array|bool|mixed
+     * @throws Exception
+     */
+    public function execute($sql, $inputarr = false)
+    {
+        $type = 'write';
+        $pin_connection = null;
+
+        // Prevent leading spaces from causing isReadOnlyQuery/stripos from failing.
+        $sql = trim($sql);
+
+        // SELECT queries that can write and therefore must be run on a write capable connection.
+        // SELECT ... FOR UPDATE;
+        // SELECT ... INTO ...
+        // SELECT .. LOCK IN ... (MYSQL)
+        if ($this->isReadOnlyQuery($sql) == true) {
+            $type = 'readonly';
+        } elseif (stripos($sql, 'SET') === 0) {
+            // SET SQL statements should likely use setSessionVariable() instead,
+            // so state is properly maintained across connections, especially when they are lazily created.
+            return $this->ClusterExecute($sql, $inputarr);
+        }
+
+        $adodb_obj = $this->getConnection($type, $pin_connection);
+        if ($adodb_obj !== false) {
+            return $adodb_obj->Execute($sql, $inputarr);
+        }
+
+        return false;
+    }
+
+    /**
+     * Magic method to intercept method and callback to the proper ADODB object for write/readonly connections.
+     *
+     * @param  string $method ADODB method to call.
+     * @param  array  $args   Arguments to the ADODB method.
+     * @return bool|mixed
+     * @throws Exception
+     */
+    public function __call($method, $args)
+    {
+        $type = 'write';
+        $pin_connection = null;
+
+        // Intercept specific methods to determine if they are read-only or not.
+        $method = strtolower($method);
+        switch ($method) {
+            // case 'execute': // This is the direct overloaded function above instead.
+            case 'getone':
+            case 'getrow':
+            case 'getall':
+            case 'getcol':
+            case 'getassoc':
+            case 'selectlimit':
+                if ($this->isReadOnlyQuery(trim($args[0])) == true) {
+                    $type = 'readonly';
+                }
+                break;
+            case 'cachegetone':
+            case 'cachegetrow':
+            case 'cachegetall':
+            case 'cachegetcol':
+            case 'cachegetassoc':
+            case 'cacheexecute':
+            case 'cacheselect':
+            case 'pageexecute':
+            case 'cachepageexecute':
+                $type = 'readonly';
+                break;
+                // case 'ignoreerrors':
+                //     // When ignoreerrors is called, PIN to the connection until its called again.
+                //     if (!isset($args[0]) || (isset($args[0]) && $args[0] == FALSE)) {
+                //             $pin_connection = TRUE;
+                //     } else {
+                //             $pin_connection = FALSE;
+                //     }
+                //     break;
+
+                // Manual transactions
+            case 'begintrans':
+            case 'settransactionmode':
+                    $pin_connection = true;
+                break;
+            case 'rollbacktrans':
+            case 'committrans':
+                $pin_connection = false;
+                break;
+                // Smart transactions
+            case 'starttrans':
+                $pin_connection = true;
+                break;
+            case 'completetrans':
+            case 'failtrans':
+                // getConnection() will only unpin the transaction if we're exiting the last nested transaction
+                $pin_connection = false;
+                break;
+
+            // Functions that don't require any connection and therefore
+            // shouldn't force a connection be established before they run.
+            case 'qstr':
+            case 'escape':
+            case 'binddate':
+            case 'bindtimestamp':
+            case 'setfetchmode':
+                  $type = false; // No connection necessary.
+                break;
+
+            // Default to assuming write connection is required to be on the safe side.
+            default:
+                break;
+        }
+
+        if ($type === false) {
+            if (is_array($this->connections) && count($this->connections) > 0) {
+                foreach ($this->connections as $key => $connection_obj) {
+                    $adodb_obj = $connection_obj->getADOdbObject();
+                    return call_user_func_array(array($adodb_obj, $method), $this->makeValuesReferenced($args)); // Just makes the function call on the first object.
+                }
+            }
+        } else {
+               $adodb_obj = $this->getConnection($type, $pin_connection);
+            if (is_object($adodb_obj)) {
+                $result = call_user_func_array(array($adodb_obj, $method), $this->makeValuesReferenced($args));
+
+                return $result;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Magic method to proxy property getter calls back to the proper ADODB object currently in use.
+     *
+     * @param  $property
+     * @return mixed
+     * @throws Exception
+     */
+    public function __get($property)
+    {
+        if (is_array($this->connections) && count($this->connections) > 0) {
+            foreach ($this->connections as $key => $connection_obj) {
+                // Just returns the property from the first object.
+                return $connection_obj->getADOdbObject()->$property;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Magic method to proxy property setter calls back to the proper ADODB object currently in use.
+     *
+     * @param  $property
+     * @param  $value
+     * @return mixed
+     * @throws Exception
+     */
+    public function __set($property, $value)
+    {
+        // Special function to set object properties on all objects
+        // without initiating a connection to the database.
+        if (is_array($this->connections) && count($this->connections) > 0) {
+            foreach ($this->connections as $key => $connection_obj) {
+                $connection_obj->getADOdbObject()->$property = $value;
+            }
+
+               return true;
+        }
+
+        return false;
+    }
+
+    /**
+     *  Override the __clone() magic method.
+     */
+    private function __clone()
+    {
+    }
+}
+
+/**
+ * Class ADOdbLoadBalancerConnection
+ */
+class ADOdbLoadBalancerConnection
+{
+    /**
+     * @var bool    ADOdb drive name.
+     */
+    protected $driver = false;
+
+    /**
+     * @var bool    ADODB object.
+     */
+    protected $adodb_obj = false;
+
+    /**
+     * @var string    Type of connection, either 'write' capable or 'readonly'
+     */
+    public $type = 'write';
+
+    /**
+     * @var int        Weight of connection, lower receives less queries, higher receives more queries.
+     */
+    public $weight = 1;
+
+    /**
+     * @var bool    Determines if the connection persistent.
+     */
+    public $persistent_connection = false;
+
+    /**
+     * @var string    Database connection host
+     */
+    public $host = '';
+
+    /**
+     * @var string    Database connection user
+     */
+    public $user = '';
+
+    /**
+     * @var string    Database connection password
+     */
+    public $password = '';
+
+    /**
+     * @var string    Database connection database name
+     */
+    public $database = '';
+
+    /**
+     * ADOdbLoadBalancerConnection constructor to setup the ADODB object.
+     *
+     * @param $driver
+     * @param string $type
+     * @param int    $weight
+     * @param bool   $persistent_connection
+     * @param string $argHostname
+     * @param string $argUsername
+     * @param string $argPassword
+     * @param string $argDatabaseName
+     */
+    public function __construct(
+        $driver,
+        $type = 'write',
+        $weight = 1,
+        $persistent_connection = false,
+        $argHostname = '',
+        $argUsername = '',
+        $argPassword = '',
+        $argDatabaseName = ''
+    ) {
+        if ($type !== 'write' && $type !== 'readonly') {
+            return false;
+        }
+
+        $this->adodb_obj = ADONewConnection($driver);
+
+        $this->type = $type;
+        $this->weight = $weight;
+        $this->persistent_connection = $persistent_connection;
+
+        $this->host = $argHostname;
+        $this->user = $argUsername;
+        $this->password = $argPassword;
+        $this->database = $argDatabaseName;
+
+        return true;
+    }
+
+    /**
+     * Returns the ADODB object for this connection.
+     *
+     * @return bool
+     */
+    public function getADOdbObject()
+    {
+        return $this->adodb_obj;
+    }
+}
index 394ce08..f143e8c 100644 (file)
@@ -11,7 +11,7 @@ if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
 
 /*
 
-  @version   v5.20.16  12-Jan-2020
+  @version   v5.21.0  2021-02-27
   @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
   @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
   Released under both BSD license and Lesser GPL library license.
@@ -19,7 +19,7 @@ if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
   the BSD license will take precedence. See License.txt.
   Set tabs to 4 for best viewing.
 
-  Latest version is available at http://adodb.org/
+  Latest version is available at https://adodb.org/
 
 Usage:
 
@@ -28,11 +28,14 @@ $db->memCache = true; /// should we use memCache instead of caching in files
 $db->memCacheHost = array($ip1, $ip2, $ip3);
 $db->memCachePort = 11211; /// this is default memCache port
 $db->memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib)
+                               /// Note; compression is not supported w/the memcached library
 
 $db->Connect(...);
 $db->CacheExecute($sql);
 
-  Note the memcache class is shared by all connections, is created during the first call to Connect/PConnect.
+  Notes; The memcache class is shared by all connections, is created during the first call to Connect/PConnect.
+         We'll look for both the memcache library (https://pecl.php.net/package/memcache) and the  memcached
+         library (https://pecl.php.net/package/memcached). If both exist, the memcache library will be used.
 
   Class instance is stored in $ADODB_CACHE
 */
@@ -40,6 +43,11 @@ $db->CacheExecute($sql);
        class ADODB_Cache_MemCache {
                var $createdir = false; // create caching directory structure?
 
+               // $library will be populated with the proper library on connect
+               // and is used later when there are differences in specific calls
+               // between memcache and memcached
+               var $library = false;
+
                //-----------------------------
                // memcache specific variables
 
@@ -60,18 +68,23 @@ $db->CacheExecute($sql);
                // implement as lazy connection. The connection only occurs on CacheExecute call
                function connect(&$err)
                {
-                       if (!function_exists('memcache_pconnect')) {
-                               $err = 'Memcache module PECL extension not found!';
+                       // do we have memcache or memcached?
+                       if (class_exists('Memcache')) {
+                               $this->library='Memcache';
+                               $memcache = new MemCache;
+                       } elseif (class_exists('Memcached')) {
+                               $this->library='Memcached';
+                               $memcache = new MemCached;
+                       } else {
+                               $err = 'Neither the Memcache nor Memcached PECL extensions were found!';
                                return false;
                        }
 
-                       $memcache = new MemCache;
-
                        if (!is_array($this->hosts)) $this->hosts = array($this->hosts);
 
                        $failcnt = 0;
                        foreach($this->hosts as $host) {
-                               if (!@$memcache->addServer($host,$this->port,true)) {
+                               if (!@$memcache->addServer($host,$this->port)) {
                                        $failcnt += 1;
                                }
                        }
@@ -93,8 +106,25 @@ $db->CacheExecute($sql);
                        }
                        if (!$this->_memcache) return false;
 
-                       if (!$this->_memcache->set($filename, $contents, $this->compress ? MEMCACHE_COMPRESSED : 0, $secs2cache)) {
-                               if ($debug) ADOConnection::outp(" Failed to save data at the memcached server!<br>\n");
+                       $failed=false;
+                       switch ($this->library) {
+                               case 'Memcache':
+                                       if (!$this->_memcache->set($filename, $contents, $this->compress ? MEMCACHE_COMPRESSED : 0, $secs2cache)) {
+                                               $failed=true;
+                                       }
+                                       break;
+                               case 'Memcached':
+                                       if (!$this->_memcache->set($filename, $contents, $secs2cache)) {
+                                               $failed=true;
+                                       }
+                                       break;
+                               default:
+                                       $failed=true;
+                                       break;
+                       }
+
+                       if($failed) {
+                               if ($debug) ADOConnection::outp(" Failed to save data at the memcache server!<br>\n");
                                return false;
                        }
 
@@ -110,7 +140,7 @@ $db->CacheExecute($sql);
 
                        $rs = $this->_memcache->get($filename);
                        if (!$rs) {
-                               $err = 'Item with such key doesn\'t exists on the memcached server.';
+                               $err = 'Item with such key doesn\'t exist on the memcache server.';
                                return $false;
                        }
 
@@ -176,8 +206,8 @@ $db->CacheExecute($sql);
                        $del = $this->_memcache->delete($filename);
 
                        if ($debug)
-                               if (!$del) ADOConnection::outp("flushcache: $key entry doesn't exist on memcached server!<br>\n");
-                               else ADOConnection::outp("flushcache: $key entry flushed from memcached server!<br>\n");
+                               if (!$del) ADOConnection::outp("flushcache: $key entry doesn't exist on memcache server!<br>\n");
+                               else ADOConnection::outp("flushcache: $key entry flushed from memcache server!<br>\n");
 
                        return $del;
                }
index 5d0ca6d..ec6aba4 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /*
-       @version   v5.20.16  12-Jan-2020
+       @version   v5.21.0  2021-02-27
        @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
        @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
          Released under both BSD license and Lesser GPL library license.
@@ -275,7 +275,7 @@ class ADODB_Pager {
        }
 
        //------------------------------------------------------
-       // override this to control overall layout and formating
+       // override this to control overall layout and formatting
        function RenderLayout($header,$grid,$footer,$attributes='border=1 bgcolor=beige')
        {
                echo "<table ".$attributes."><tr><td>",
index d1037c0..e0ee74b 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * @version   v5.20.16  12-Jan-2020
+ * @version   v5.21.0  2021-02-27
  * @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
  * @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
  * Released under both BSD license and Lesser GPL library license.
@@ -336,7 +336,7 @@ class DB
                        $parsed['hostspec'] = urldecode($str);
                }
 
-               // Get dabase if any
+               // Get database if any
                // $dsn => database
                if (!empty($dsn)) {
                        $parsed['database'] = $dsn;
index 05a413b..6bce735 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /*
-@version   v5.20.16  12-Jan-2020
+@version   v5.21.0  2021-02-27
 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
 @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
   Released under both BSD license and Lesser GPL library license.
@@ -8,7 +8,7 @@
   the BSD license will take precedence. See License.txt.
   Set tabs to 4 for best viewing.
 
-  Latest version is available at http://adodb.org/
+  Latest version is available at https://adodb.org/
 
   Library for basic performance monitoring and tuning.
 
@@ -170,6 +170,9 @@ function adodb_log_sql(&$connx,$sql,$inputarr)
                                mysqli_free_result($conn->_queryID);
                        }
                        $ok = $conn->Execute($isql,$arr);
+                       if($conn instanceof ADODB_mysqli && $conn->_queryID){
+                               mysqli_free_result($conn->_queryID);
+                       }
                } else
                        $ok = true;
 
@@ -229,7 +232,7 @@ class adodb_perf {
        var $cliFormat = "%32s => %s \r\n";
        var $sql1 = 'sql1';  // used for casting sql1 to text for mssql
        var $explain = true;
-       var $helpurl = '<a href="http://adodb.org/dokuwiki/doku.php?id=v5:performance:logsql">LogSQL help</a>';
+       var $helpurl = '<a href="https://adodb.org/dokuwiki/doku.php?id=v5:performance:logsql">LogSQL help</a>';
        var $createTableSQL = false;
        var $maxLength = 2000;
 
@@ -263,12 +266,6 @@ processes 69293
                // Algorithm is taken from
                // http://social.technet.microsoft.com/Forums/en-US/winservergen/thread/414b0e1b-499c-411e-8a02-6a12e339c0f1/
                if (strncmp(PHP_OS,'WIN',3)==0) {
-                       if (PHP_VERSION == '5.0.0') return false;
-                       if (PHP_VERSION == '5.0.1') return false;
-                       if (PHP_VERSION == '5.0.2') return false;
-                       if (PHP_VERSION == '5.0.3') return false;
-                       if (PHP_VERSION == '4.3.10') return false; # see http://bugs.php.net/bug.php?id=31737
-
                        static $FAIL = false;
                        if ($FAIL) return false;
 
@@ -593,7 +590,7 @@ Committed_AS:   348732 kB
        }
 
        /*
-               Raw function returning array of poll paramters
+               Raw function returning array of poll parameters
        */
        function PollParameters()
        {
@@ -694,12 +691,6 @@ Committed_AS:   348732 kB
        }
        $this->conn->LogSQL($savelog);
 
-       // magic quotes
-
-       if (isset($_GET['sql']) && get_magic_quotes_gpc()) {
-               $_GET['sql'] = $_GET['sql'] = str_replace(array("\\'",'\"'),array("'",'"'),$_GET['sql']);
-       }
-
        if (!isset($_SESSION['ADODB_PERF_SQL'])) $nsql = $_SESSION['ADODB_PERF_SQL'] = 10;
        else  $nsql = $_SESSION['ADODB_PERF_SQL'];
 
@@ -724,7 +715,7 @@ Committed_AS:   348732 kB
 
        if  (empty($_GET['hidem']))
        echo "<table border=1 width=100% bgcolor=lightyellow><tr><td colspan=2>
-       <b><a href=http://adodb.org/dokuwiki/doku.php?id=v5:performance:performance_index>ADOdb</a> Performance Monitor</b> <font size=1>for $app</font></tr><tr><td>
+       <b><a href=https://adodb.org/dokuwiki/doku.php?id=v5:performance:performance_index>ADOdb</a> Performance Monitor</b> <font size=1>for $app</font></tr><tr><td>
        <a href=?do=stats><b>Performance Stats</b></a> &nbsp; <a href=?do=viewsql><b>View SQL</b></a>
         &nbsp; <a href=?do=tables><b>View Tables</b></a> &nbsp; <a href=?do=poll><b>Poll Stats</b></a>",
         $allowsql ? ' &nbsp; <a href=?do=dosql><b>Run SQL</b></a>' : '',
@@ -767,7 +758,6 @@ Committed_AS:   348732 kB
                        echo $this->Tables(); break;
                }
                global $ADODB_vers;
-               echo "<p><div align=center><font size=1>$ADODB_vers Sponsored by <a href=http://phplens.com/>phpLens</a></font></div>";
        }
 
        /*
@@ -956,7 +946,7 @@ Committed_AS:   348732 kB
 <?php
                if (!isset($_REQUEST['sql'])) return;
 
-               $sql = $this->undomq(trim($sql));
+               $sql = trim($sql);
                if (substr($sql,strlen($sql)-1) === ';') {
                        $print = true;
                        $sqla = $this->SplitSQL($sql);
@@ -1000,18 +990,6 @@ Committed_AS:   348732 kB
                return $arr;
        }
 
-       function undomq($m)
-       {
-       if (get_magic_quotes_gpc()) {
-               // undo the damage
-               $m = str_replace('\\\\','\\',$m);
-               $m = str_replace('\"','"',$m);
-               $m = str_replace('\\\'','\'',$m);
-       }
-       return $m;
-}
-
-
    /************************************************************************/
 
     /**
index 69f8149..eea5818 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 
 /*
-  @version   v5.20.16  12-Jan-2020
+  @version   v5.21.0  2021-02-27
   @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
   @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
   Released under both BSD license and Lesser GPL library license.
index af8db3e..ebab801 100644 (file)
@@ -2,9 +2,9 @@
 /*
 ADOdb Date Library, part of the ADOdb abstraction library
 
-Latest version is available at http://adodb.org/
+Latest version is available at https://adodb.org/
 
-@version   v5.20.16  12-Jan-2020
+@version   v5.21.0  2021-02-27
 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
 @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
 
@@ -38,7 +38,7 @@ of date()'s field formats. Mktime() will convert from local time to GMT,
 and date() will convert from GMT to local time, but daylight savings is
 not handled currently.
 
-This library is independant of the rest of ADOdb, and can be used
+This library is independent of the rest of ADOdb, and can be used
 as standalone code.
 
 PERFORMANCE
@@ -66,13 +66,6 @@ COPYRIGHT
 jackbbs, which includes adodb_mktime, adodb_get_gmt_diff, adodb_is_leap_year
 and originally found at http://www.php.net/manual/en/function.mktime.php
 
-=============================================================================
-
-BUG REPORTS
-
-These should be posted to the ADOdb forums at
-
-       http://phplens.com/lens/lensforum/topics.php?id=4
 
 =============================================================================
 
@@ -268,7 +261,7 @@ Changed adodb_get_gm_diff to use DateTimeZone().
 * Now adodb_mktime(0,0,0,24,1,2037) works correctly.
 
 - 15 July 2007 0.30
-Added PHP 5.2.0 compatability fixes.
+Added PHP 5.2.0 compatibility fixes.
  * gmtime behaviour for 1970 has changed. We use the actual date if it is between 1970 to 2038 to get the
  * timezone, otherwise we use the current year as the baseline to retrieve the timezone.
  * Also the timezone's in php 5.2.* support historical data better, eg. if timezone today was +8, but
@@ -348,7 +341,7 @@ January!!!), changed adodb_get_gmt_diff() to ignore daylight savings.
 
 - 9 Aug 2003 0.10
 Fixed bug with dates after 2038.
-See http://phplens.com/lens/lensforum/msgs.php?id=6980
+See PHPLens Issue No: 6980
 
 - 1 July 2003 0.09
 Added support for Q (Quarter).
@@ -404,8 +397,6 @@ First implementation.
 */
 define('ADODB_DATE_VERSION',0.35);
 
-$ADODB_DATETIME_CLASS = (PHP_VERSION >= 5.2);
-
 /*
        This code was originally for windows. But apparently this problem happens
        also with Linux, RH 7.3 and later!
@@ -532,8 +523,8 @@ function adodb_date_test()
        if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950<br>";
        if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990<br>";
 
-       // Test string formating
-       print "<p>Testing date formating</p>";
+       // Test string formatting
+       print "<p>Testing date formatting</p>";
 
        $fmt = '\d\a\t\e T Y-m-d H:i:s a A d D F g G h H i j l L m M n O \R\F\C2822 r s t U w y Y z Z 2003';
        $s1 = date($fmt,0);
@@ -737,13 +728,12 @@ function adodb_get_gmt_diff_ts($ts)
 */
 function adodb_get_gmt_diff($y,$m,$d)
 {
-static $TZ,$tzo;
-global $ADODB_DATETIME_CLASS;
+       static $TZ,$tzo;
 
        if (!defined('ADODB_TEST_DATES')) $y = false;
        else if ($y < 1970 || $y >= 2038) $y = false;
 
-       if ($ADODB_DATETIME_CLASS && $y !== false) {
+       if ($y !== false) {
                $dt = new DateTime();
                $dt->setISODate($y,$m,$d);
                if (empty($tzo)) {
@@ -1035,20 +1025,20 @@ global $_month_table_normal,$_month_table_leaf, $_adodb_last_date_call_failed;
                0 => $origd
        );
 }
-/*
-               if ($isphp5)
-                               $dates .= sprintf('%s%04d',($gmt<=0)?'+':'-',abs($gmt)/36);
-                       else
-                               $dates .= sprintf('%s%04d',($gmt<0)?'+':'-',abs($gmt)/36);
-                       break;*/
-function adodb_tz_offset($gmt,$isphp5)
+
+/**
+ * Compute timezone offset.
+ *
+ * @param int  $gmt     Time offset from GMT, in seconds
+ * @param bool $ignored Param leftover from removed PHP4-compatibility code
+ *                      kept to avoid altering function signature.
+ * @return string
+ */
+function adodb_tz_offset($gmt, $ignored=true)
 {
-       $zhrs = abs($gmt)/3600;
+       $zhrs = abs($gmt) / 3600;
        $hrs = floor($zhrs);
-       if ($isphp5)
-               return sprintf('%s%02d%02d',($gmt<=0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60);
-       else
-               return sprintf('%s%02d%02d',($gmt<0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60);
+       return sprintf('%s%02d%02d', ($gmt <= 0) ? '+' : '-', $hrs, ($zhrs - $hrs) * 60);
 }
 
 
@@ -1081,10 +1071,8 @@ function adodb_date2($fmt, $d=false, $is_gmt=false)
 */
 function adodb_date($fmt,$d=false,$is_gmt=false)
 {
-static $daylight;
-global $ADODB_DATETIME_CLASS;
-static $jan1_1971;
-
+       static $daylight;
+       static $jan1_1971;
 
        if (!isset($daylight)) {
                $daylight = function_exists('adodb_daylight_sv');
@@ -1093,7 +1081,15 @@ static $jan1_1971;
 
        if ($d === false) return ($is_gmt)? @gmdate($fmt): @date($fmt);
        if (!defined('ADODB_TEST_DATES')) {
-               if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
+
+               /*
+               * Format 'Q' is an ADOdb custom format, not supported in PHP
+               * so if there is a 'Q' in the format, we force it to use our
+               * function. There is a trivial overhead in this
+               */
+
+               if ((abs($d) <= 0x7FFFFFFF) && strpos($fmt,'Q') === false)
+               { // check if number in 32-bit signed range
 
                        if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= $jan1_1971) // if windows, must be +ve integer
                                return ($is_gmt)? @gmdate($fmt,$d): @date($fmt,$d);
@@ -1116,8 +1112,6 @@ static $jan1_1971;
        $max = strlen($fmt);
        $dates = '';
 
-       $isphp5 = PHP_VERSION >= 5;
-
        /*
                at this point, we have the following integer vars to manipulate:
                $year, $month, $day, $hour, $min, $secs
@@ -1128,12 +1122,9 @@ static $jan1_1971;
                        $dates .= date('e');
                        break;
                case 'T':
-                       if ($ADODB_DATETIME_CLASS) {
-                               $dt = new DateTime();
-                               $dt->SetDate($year,$month,$day);
-                               $dates .= $dt->Format('T');
-                       } else
-                               $dates .= date('T');
+                       $dt = new DateTime();
+                       $dt->SetDate($year,$month,$day);
+                       $dates .= $dt->Format('T');
                        break;
                // YEAR
                case 'L': $dates .= $arr['leap'] ? '1' : '0'; break;
@@ -1152,14 +1143,16 @@ static $jan1_1971;
 
                        $gmt = adodb_get_gmt_diff($year,$month,$day);
 
-                       $dates .= ' '.adodb_tz_offset($gmt,$isphp5);
+                       $dates .= ' '.adodb_tz_offset($gmt);
                        break;
 
                case 'Y': $dates .= $year; break;
                case 'y': $dates .= substr($year,strlen($year)-2,2); break;
                // MONTH
                case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break;
-               case 'Q': $dates .= ($month+3)>>2; break;
+               case 'Q':
+                       $dates .= ceil($month / 3);
+                       break;
                case 'n': $dates .= $month; break;
                case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break;
                case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break;
@@ -1167,6 +1160,9 @@ static $jan1_1971;
                case 't': $dates .= $arr['ndays']; break;
                case 'z': $dates .= $arr['yday']; break;
                case 'w': $dates .= adodb_dow($year,$month,$day); break;
+               case 'W':
+                       $dates .= sprintf('%02d',ceil( $arr['yday'] / 7) - 1);
+                       break;
                case 'l': $dates .= gmdate('l',$_day_power*(3+adodb_dow($year,$month,$day))); break;
                case 'D': $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))); break;
                case 'j': $dates .= $day; break;
@@ -1185,7 +1181,7 @@ static $jan1_1971;
                case 'O':
                        $gmt = ($is_gmt) ? 0 : adodb_get_gmt_diff($year,$month,$day);
 
-                       $dates .= adodb_tz_offset($gmt,$isphp5);
+                       $dates .= adodb_tz_offset($gmt);
                        break;
 
                case 'H':
@@ -1389,7 +1385,7 @@ global $ADODB_DATE_LOCALE;
                $sep = substr($tstr,2,1);
                $hasAM = strrpos($tstr,'M') !== false;
        */
-               # see http://phplens.com/lens/lensforum/msgs.php?id=14865 for reasoning, and changelog for version 0.24
+               # see PHPLens Issue No: 14865 for reasoning, and changelog for version 0.24
                $dstr = gmstrftime('%x',31366800); // 30 Dec 1970, 1 am
                $sep = substr($dstr,2,1);
                $tstr = strtoupper(gmstrftime('%X',31366800)); // 30 Dec 1970, 1 am
index b53d4e2..0961b4d 100644 (file)
@@ -1254,12 +1254,6 @@ class adoSchema {
        */
        var $objectPrefix = '';
 
-       /**
-       * @var long     Original Magic Quotes Runtime value
-       * @access private
-       */
-       var $mgq;
-
        /**
        * @var long     System debug
        * @access private
@@ -1303,12 +1297,6 @@ class adoSchema {
        * @param object $db ADOdb database connection object.
        */
        function __construct( $db ) {
-               // Initialize the environment
-               $this->mgq = get_magic_quotes_runtime();
-               if ($this->mgq !== false) {
-                       ini_set('magic_quotes_runtime', 0);
-               }
-
                $this->db = $db;
                $this->debug = $this->db->debug;
                $this->dict = NewDataDictionary( $this->db );
@@ -2195,11 +2183,7 @@ class adoSchema {
        * Call this method to clean up after an adoSchema object that is no longer in use.
        * @deprecated adoSchema now cleans up automatically.
        */
-       function Destroy() {
-               if ($this->mgq !== false) {
-                       ini_set('magic_quotes_runtime', $this->mgq );
-               }
-       }
+       function Destroy() {}
 }
 
 /**
index 4d1faad..9fb8272 100644 (file)
@@ -145,7 +145,7 @@ class dbObject {
        *
        * @access private
        */
-       function _tag_open( &$parser, $tag, $attributes ) {
+       function _tag_open( $parser, $tag, $attributes ) {
 
        }
 
@@ -154,7 +154,7 @@ class dbObject {
        *
        * @access private
        */
-       function _tag_cdata( &$parser, $cdata ) {
+       function _tag_cdata( $parser, $cdata ) {
 
        }
 
@@ -163,7 +163,7 @@ class dbObject {
        *
        * @access private
        */
-       function _tag_close( &$parser, $tag ) {
+       function _tag_close( $parser, $tag ) {
 
        }
 
@@ -204,7 +204,7 @@ class dbObject {
        * @param string $field Field.
        * @return string Field ID.
        */
-       function FieldID( $field ) {
+       function fieldID( $field ) {
                return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );
        }
 }
@@ -283,7 +283,7 @@ class dbTable extends dbObject {
        *
        * @access private
        */
-       function _tag_open( &$parser, $tag, $attributes ) {
+       function _tag_open( $parser, $tag, $attributes ) {
                $this->currentElement = strtoupper( $tag );
 
                switch( $this->currentElement ) {
@@ -345,8 +345,16 @@ class dbTable extends dbObject {
        *
        * @access private
        */
-       function _tag_cdata( &$parser, $cdata ) {
+       function _tag_cdata( $parser, $cdata ) {
                switch( $this->currentElement ) {
+                       // Table or field comment
+                       case 'DESCR':
+                               if( isset( $this->current_field ) ) {
+                                       $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );
+                               } else {
+                                       $this->addTableComment( $cdata );
+                               }
+                               break;
                        // Table/field constraint
                        case 'CONSTRAINT':
                                if( isset( $this->current_field ) ) {
@@ -373,7 +381,7 @@ class dbTable extends dbObject {
        *
        * @access private
        */
-       function _tag_close( &$parser, $tag ) {
+       function _tag_close( $parser, $tag ) {
                $this->currentElement = '';
 
                switch( strtoupper( $tag ) ) {
@@ -449,7 +457,7 @@ class dbTable extends dbObject {
        * @return array Field specifier array
        */
        function addField( $name, $type, $size = NULL, $opts = NULL ) {
-               $field_id = $this->FieldID( $name );
+               $field_id = $this->fieldID( $name );
 
                // Set the field index so we know where we are
                $this->current_field = $field_id;
@@ -506,11 +514,15 @@ class dbTable extends dbObject {
        */
        function addTableOpt( $opt ) {
                if(isset($this->currentPlatform)) {
-                       $this->opts[$this->parent->db->databaseType] = $opt;
+                       $this->opts[$this->parent->db->dataProvider] = $opt;
                }
                return $this->opts;
        }
 
+       function addTableComment( $opt ) {
+               $this->opts['comment'] = $opt;
+               return $this->opts;
+       }
 
        /**
        * Generates the SQL that will create the table in the database
@@ -522,9 +534,9 @@ class dbTable extends dbObject {
                $sql = array();
 
                // drop any existing indexes
-               if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {
+               if( is_array( $legacy_indexes = $xmls->dict->metaIndexes( $this->name ) ) ) {
                        foreach( $legacy_indexes as $index => $index_details ) {
-                               $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );
+                               $sql[] = $xmls->dict->dropIndexSQL( $index, $this->name );
                        }
                }
 
@@ -534,10 +546,10 @@ class dbTable extends dbObject {
                }
 
                // if table exists
-               if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {
+               if( is_array( $legacy_fields = $xmls->dict->metaColumns( $this->name ) ) ) {
                        // drop table
                        if( $this->drop_table ) {
-                               $sql[] = $xmls->dict->DropTableSQL( $this->name );
+                               $sql[] = $xmls->dict->dropTableSQL( $this->name );
 
                                return $sql;
                        }
@@ -545,7 +557,7 @@ class dbTable extends dbObject {
                        // drop any existing fields not in schema
                        foreach( $legacy_fields as $field_id => $field ) {
                                if( !isset( $this->fields[$field_id] ) ) {
-                                       $sql[] = $xmls->dict->DropColumnSQL( $this->name, $field->name );
+                                       $sql[] = $xmls->dict->dropColumnSQL( $this->name, $field->name );
                                }
                        }
                // if table doesn't exist
@@ -591,21 +603,21 @@ class dbTable extends dbObject {
 
                if( empty( $legacy_fields ) ) {
                        // Create the new table
-                       $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
-                       logMsg( end( $sql ), 'Generated CreateTableSQL' );
+                       $sql[] = $xmls->dict->createTableSQL( $this->name, $fldarray, $this->opts );
+                       logMsg( end( $sql ), 'Generated createTableSQL' );
                } else {
                        // Upgrade an existing table
                        logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );
                        switch( $xmls->upgrade ) {
                                // Use ChangeTableSQL
                                case 'ALTER':
-                                       logMsg( 'Generated ChangeTableSQL (ALTERing table)' );
-                                       $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );
+                                       logMsg( 'Generated changeTableSQL (ALTERing table)' );
+                                       $sql[] = $xmls->dict->changeTableSQL( $this->name, $fldarray, $this->opts );
                                        break;
                                case 'REPLACE':
                                        logMsg( 'Doing upgrade REPLACE (testing)' );
-                                       $sql[] = $xmls->dict->DropTableSQL( $this->name );
-                                       $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
+                                       $sql[] = $xmls->dict->dropTableSQL( $this->name );
+                                       $sql[] = $xmls->dict->createTableSQL( $this->name, $fldarray, $this->opts );
                                        break;
                                // ignore table
                                default:
@@ -698,7 +710,7 @@ class dbIndex extends dbObject {
        *
        * @access private
        */
-       function _tag_open( &$parser, $tag, $attributes ) {
+       function _tag_open( $parser, $tag, $attributes ) {
                $this->currentElement = strtoupper( $tag );
 
                switch( $this->currentElement ) {
@@ -725,7 +737,7 @@ class dbIndex extends dbObject {
        *
        * @access private
        */
-       function _tag_cdata( &$parser, $cdata ) {
+       function _tag_cdata( $parser, $cdata ) {
                switch( $this->currentElement ) {
                        // Index field name
                        case 'COL':
@@ -741,7 +753,7 @@ class dbIndex extends dbObject {
        *
        * @access private
        */
-       function _tag_close( &$parser, $tag ) {
+       function _tag_close( $parser, $tag ) {
                $this->currentElement = '';
 
                switch( strtoupper( $tag ) ) {
@@ -758,7 +770,7 @@ class dbIndex extends dbObject {
        * @return string Field list
        */
        function addField( $name ) {
-               $this->columns[$this->FieldID( $name )] = $name;
+               $this->columns[$this->fieldID( $name )] = $name;
 
                // Return the field list
                return $this->columns;
@@ -795,7 +807,7 @@ class dbIndex extends dbObject {
                        }
                }
 
-               return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );
+               return $xmls->dict->createIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );
        }
 
        /**
@@ -841,7 +853,7 @@ class dbData extends dbObject {
        *
        * @access private
        */
-       function _tag_open( &$parser, $tag, $attributes ) {
+       function _tag_open( $parser, $tag, $attributes ) {
                $this->currentElement = strtoupper( $tag );
 
                switch( $this->currentElement ) {
@@ -863,7 +875,7 @@ class dbData extends dbObject {
        *
        * @access private
        */
-       function _tag_cdata( &$parser, $cdata ) {
+       function _tag_cdata( $parser, $cdata ) {
                switch( $this->currentElement ) {
                        // Index field name
                        case 'F':
@@ -879,7 +891,7 @@ class dbData extends dbObject {
        *
        * @access private
        */
-       function _tag_close( &$parser, $tag ) {
+       function _tag_close( $parser, $tag ) {
                $this->currentElement = '';
 
                switch( strtoupper( $tag ) ) {
@@ -903,7 +915,7 @@ class dbData extends dbObject {
 
                // Set the field index so we know where we are
                if( isset( $attributes['NAME'] ) ) {
-                       $this->current_field = $this->FieldID( $attributes['NAME'] );
+                       $this->current_field = $this->fieldID( $attributes['NAME'] );
                } else {
                        $this->current_field = count( $this->data[$this->row] );
                }
@@ -935,12 +947,12 @@ class dbData extends dbObject {
        * @return array Array containing index creation SQL
        */
        function create( &$xmls ) {
-               $table = $xmls->dict->TableName($this->parent->name);
+               $table = $xmls->dict->tableName($this->parent->name);
                $table_field_count = count($this->parent->fields);
-               $tables = $xmls->db->MetaTables();
+               $tables = $xmls->db->metaTables();
                $sql = array();
 
-               $ukeys = $xmls->db->MetaPrimaryKeys( $table );
+               $ukeys = $xmls->db->metaPrimaryKeys( $table );