Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add auto-provisioning for matomo #52

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

SimonasAdams
Copy link

This commit contains the ability to auto provision sites based on either a value in the config.php file or via the instance form on the front end.

To auto provision on install or upgrade add a setting watool_matomo into config.php as an array, e.g:

$CFG->watool_matomo = [
        'instance1' => [        
                'apitoken' => 'abcdef123456',
                'siteurl' => 'https://example1.com'
        ],
        'instance2' => [
                'apitoken' => 'abcdef123456',
                'siteurl' => 'https://example2.com'
        ],
];

Where the apitoken is the auth_token of the user that is allowed to do API requests on the Matomo server, and the 'siteurl' is the URL of the Matomo server instance.

During install if the above setting is present it will loop through the array and attempt to create a site on the remote Matomo instance per array item. If successful, it create new records locally and stores the siteid, the apitoken, and the siteurl per instance created.

When manually adding a new instance via the front end, an auto provision attempt will be made if the form submission contains both the siteurl and the apitoken. If successful, the siteid is retrieved and stored against the local record. If the form is submitted with a siteid, there will be not attempt to register with the remote server.

Additionally, at the point of submisision the current CFG->wwwroot is stored against that instance locally. On page loads that check if the tag should be output, a check is made to see if the current CFG->wwwroot matches that stored against the local record. If it does not match, an attempt is made to update the remotely registered instance with the new url. If successful, this new url is then stored locally. This is to allow the remote to track if and when the url of the server changes.

classes/injector.php Outdated Show resolved Hide resolved
@@ -144,4 +146,13 @@ public function trackurl($urlencode = false, $leadingslash = false): string {
return $trackurl;
}

/**
* Override in child to implement.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please flesh this out more with how this should work generically, eg if someone wanted to come and add support for GA later on

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should be more descriptive now.

tool/matomo/lib.php Outdated Show resolved Hide resolved
tool/matomo/lib.php Outdated Show resolved Hide resolved
@@ -27,6 +27,9 @@

use moodleform;
use tool_webanalytics\plugin_manager;
use tool_webanalytics\record;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are those leftovers?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct and now removed.

classes/injector.php Outdated Show resolved Hide resolved
- Set the global config settings 'apitoken' and 'apiurl' to enable auto provisioning. These can also be set in config.php e.g:
- `$CFG->forced_plugin_settings['watool_matomo']['apiurl'] = 'https://matomo.org';`
- `$CFG->forced_plugin_settings['watool_matomo']['apitoken'] = 'xxxx';`
- Auto provisioning attempts are made if the current site url has changed since any of the instances were stored.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please flesh this out a bit more, this might only need comments but might be some missing code too. I want to understand what is happened if we roll this auto provisioning out toa pre production side, eg p-client.catalyst-au.net and they get a matomo instance, and then a few months later they go live and the dns changes to moodle.client.com

In this scenario I can see a method called update_site but it isn't actually called from anywhere?

I would expect this to continue using the same matomo id, ie the one instance will seamlessly show stats from before and after the dns change. This should be the same as if the client later changes their domain for marketing reasons, the stats should continue to be seamless.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was previously just creating a new instance on the matomo server when the DNS changed but is now updating the url over the API if the DNS changes, essentially keeping the site id the same. Have fleshed this out in the docs.

Note that when manually adding an instance config on the front end it will also try to create/update over the API if the API config is in the form.

I have also documented these methods in unit tests for the watool_matomo plugin too.

@@ -0,0 +1,22 @@
# Matomo (formerly Piwik)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've also realized this is a new readme file. Even though this is specific to the tool_matomo I think this should be a section under the main readme purely to make it easier to find when casually browsing github so you don't have to go dig. We could just cross link the two readmes but this chunk of help is small enough that we can just embed it directly.

In theory if we added this same support to GA and other tools they could all be inline in the main readme

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have moved this to the parent plugin README

Copy link
Contributor

@bwalkerl bwalkerl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mentioning the issues I've noticed. We'll work on getting these sorted.

Comment on lines +7 to +17
<TABLE NAME="watool_matomo_api_error_log" COMMENT="Matomo Track log errors">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="endpoint" TYPE="text" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="The name of the endpoint function called during the sync attempt"/>
<FIELD NAME="error" TYPE="text" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="The string given as the error"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
</TABLE>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. This table is missing an upgrade step
  2. Table name is needlessly long and could cause issues

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree we probably just don't need the table

$request['method'] = 'SitesManager.addSite';
$request['siteName'] = !empty($sitename) ? $sitename : $SITE->fullname;
$request['timezone'] = !empty($timezone) ? $timezone : core_date::get_server_timezone();
$request['currency'] = !empty($currency) ? $currency : 'GBP';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fallback currency should be more global

if (!empty($url['scheme'])) {
return $this->config->siteurl;
}
return "https://{$url['path']}";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look like it would form a proper url

Comment on lines -103 to +111
'id' => 'id',
'username' => 'username',
'id' => 'id',
'username' => 'username',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indenting changed to being incorrect

* Otherwise, create a new instance over the API.
*
* @param $client
* @return int siteid if successful, 0 if not.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would recommend clarifying what is returned for already registered sites. This appears to be siteid, so would be "if successful or already registered"

Comment on lines +29 to +51
$settings->add(
new admin_setting_heading(
'watool_matomo_autoprovision',
get_string('autoprovision_heading', 'watool_matomo'),
''
)
);
$settings->add(
new admin_setting_configtext(
'watool_matomo_siteurl',
get_string('apiurl', 'watool_matomo'),
get_string('apiurl_help', 'watool_matomo'),
$CFG->forced_plugin_settings['watool_matomo']['url'] ?? '',
PARAM_URL
)
);
$settings->add(
new admin_setting_configpasswordunmask(
'watool_matomo_token',
get_string('apitoken', 'watool_matomo'),
get_string('apitoken_desc', 'watool_matomo'),
$CFG->forced_plugin_settings['watool_matomo']['token'] ?? '',
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indenting

Comment on lines +29 to +39
$category = new admin_category(
'tool_webanalytics',
new lang_string('pluginname', 'tool_webanalytics'),
false
);
$ADMIN->add('tools', $category);

$externalpage = new admin_externalpage(
'tool_webanalytics_manage',
get_string('pluginname', 'tool_webanalytics'),
new moodle_url('/admin/tool/webanalytics/manage.php')
'tool_webanalytics_manage',
get_string('pluginname', 'tool_webanalytics'),
new moodle_url('/admin/tool/webanalytics/manage.php')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indenting

Comment on lines +28 to +30
use curl;
use Exception;
use moodle_url;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curl and moodle_url aren't used

Comment on lines +29 to +30
use tool_webanalytics\tool\tool_interface;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed?

$settings = [];
$settings['siteid'] = isset($data->siteid) ? $data->siteid : '';
$settings['siteid'] = isset($data->siteid) ? $data->siteid : '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a bit repetitive to me, can this be a $defaults array and then cast the stdClass to an array and merge them and skip all the isset bits?

Copy link
Contributor

@bwalkerl bwalkerl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a couple of extra issues after more testing

foreach ($records as $record) {
$settings = $record->get_property('settings');
$name = $record->get_property('name');
if ((!empty($settings['wwwroot']) && $settings['wwwroot'] === $CFG->wwwroot) || $name === 'auto-provisioned:FAILED') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If $settings['wwwroot'] doesn't exist (which will happen for existing records since this is a new setting) auto provision is allowed for sites that may already have the proper URL set. Right now this will lead to creating a duplicate record due to other mentioned issues.

$request['siteName'] = !empty($sitename) ? $sitename : $SITE->fullname;
$request['timezone'] = !empty($timezone) ? $timezone : core_date::get_server_timezone();
$request['currency'] = !empty($currency) ? $currency : 'GBP';
$request = array_merge($request, $this->build_urls_for_request($urls));
Copy link
Contributor

@bwalkerl bwalkerl Nov 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the build_urls_for_request function isn't provided with the existing urls (none are passed here), it will override all existing sites with just the updated site. This is problematic for registrations that are connected to multiple site urls. This is used in both registrations and auto provisioning.

Comment on lines +272 to +294
$allrecords = $rm->get_all();
$autoprovisioned = array_filter($allrecords, function($record) {
return preg_match("/^auto-provisioned:/", $record->get_property('name'));
});

if ($autoprovisioned = reset($autoprovisioned)) {
$apsettings = $autoprovisioned->get_property('settings');
$data = $autoprovisioned->export();
} else {
$data = new stdClass();
$data->name = 'auto-provisioned:' . uniqid();
}

$hasdnschanged = !empty($apsettings['wwwroot']) && $apsettings['wwwroot'] !== $CFG->wwwroot;

try {
// If the DNS has changed, let's try to update an existing site.
if ($hasdnschanged && $siteid = $client->get_siteid_from_url($apsettings['wwwroot'])) {
$client->update_site($siteid, '', [$CFG->wwwroot]);
} else {
// No site provisioned yet, create a new one.
$siteid = $client->add_site();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hardcoded to 'auto-provisioned', but if there's no match it goes straight to adding a site, which will create duplicates for sites that have a manual link or have changed the name. Needs some extra checks.

Comment on lines +41 to +50
$CFG->forced_plugin_settings['watool_matomo']['url'] ?? '',
PARAM_URL
)
);
$settings->add(
new admin_setting_configpasswordunmask(
'watool_matomo_token',
get_string('apitoken', 'watool_matomo'),
get_string('apitoken_desc', 'watool_matomo'),
$CFG->forced_plugin_settings['watool_matomo']['token'] ?? '',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The defaults don't match the names of the settings that are used elsewhere or in the readme, and the token setting name doesn't match either. It may also be better to not use these as the default so the token can be kept hidden?

Comment on lines +41 to +42
$this->config = $config;
$this->settings = $settings;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Properties for these haven't been declared.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants