first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,410 @@
<?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/>.
/**
* Class cli_helper
*
* @package tool_uploaduser
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_uploaduser;
defined('MOODLE_INTERNAL') || die();
use tool_uploaduser\local\cli_progress_tracker;
require_once($CFG->dirroot.'/user/profile/lib.php');
require_once($CFG->dirroot.'/user/lib.php');
require_once($CFG->dirroot.'/group/lib.php');
require_once($CFG->dirroot.'/cohort/lib.php');
require_once($CFG->libdir.'/csvlib.class.php');
require_once($CFG->dirroot.'/'.$CFG->admin.'/tool/uploaduser/locallib.php');
require_once($CFG->dirroot.'/'.$CFG->admin.'/tool/uploaduser/user_form.php');
require_once($CFG->libdir . '/clilib.php');
/**
* Helper method for CLI script to upload users (also has special wrappers for cli* functions for phpunit testing)
*
* @package tool_uploaduser
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cli_helper {
/** @var string */
protected $operation;
/** @var array */
protected $clioptions;
/** @var array */
protected $unrecognized;
/** @var string */
protected $progresstrackerclass;
/** @var process */
protected $process;
/**
* cli_helper constructor.
*
* @param string|null $progresstrackerclass
*/
public function __construct(?string $progresstrackerclass = null) {
$this->progresstrackerclass = $progresstrackerclass ?? cli_progress_tracker::class;
$optionsdefinitions = $this->options_definitions();
$longoptions = [];
$shortmapping = [];
foreach ($optionsdefinitions as $key => $option) {
$longoptions[$key] = $option['default'];
if (!empty($option['alias'])) {
$shortmapping[$option['alias']] = $key;
}
}
list($this->clioptions, $this->unrecognized) = cli_get_params(
$longoptions,
$shortmapping
);
}
/**
* Options used in this CLI script
*
* @return array
*/
protected function options_definitions(): array {
$options = [
'help' => [
'hasvalue' => false,
'description' => get_string('clihelp', 'tool_uploaduser'),
'default' => 0,
'alias' => 'h',
],
'file' => [
'hasvalue' => 'PATH',
'description' => get_string('clifile', 'tool_uploaduser'),
'default' => null,
'validation' => function($file) {
if (!$file) {
$this->cli_error(get_string('climissingargument', 'tool_uploaduser', 'file'));
}
if ($file && (!file_exists($file) || !is_readable($file))) {
$this->cli_error(get_string('clifilenotreadable', 'tool_uploaduser', $file));
}
}
],
];
$form = new \admin_uploaduser_form1();
[$elements, $defaults] = $form->get_form_for_cli();
$options += $this->prepare_form_elements_for_cli($elements, $defaults);
// Specify pseudo-column 'type1' to force the form to populate the legacy role mapping selector
// but only if user is allowed to assign roles in courses (otherwise form validation will fail).
$columns = uu_allowed_roles() ? ['type1'] : [];
$form = new \admin_uploaduser_form2(null, ['columns' => $columns, 'data' => []]);
[$elements, $defaults] = $form->get_form_for_cli();
$options += $this->prepare_form_elements_for_cli($elements, $defaults);
return $options;
}
/**
* Print help for export
*/
public function print_help(): void {
$this->cli_writeln(get_string('clititle', 'tool_uploaduser'));
$this->cli_writeln('');
$this->print_help_options($this->options_definitions());
$this->cli_writeln('');
$this->cli_writeln('Example:');
$this->cli_writeln('$sudo -u www-data /usr/bin/php admin/tool/uploaduser/cli/uploaduser.php --file=PATH');
}
/**
* Get CLI option
*
* @param string $key
* @return mixed|null
*/
public function get_cli_option(string $key) {
return $this->clioptions[$key] ?? null;
}
/**
* Write a text to the given stream
*
* @param string $text text to be written
*/
protected function cli_write($text): void {
if (PHPUNIT_TEST) {
echo $text;
} else {
cli_write($text);
}
}
/**
* Write error notification
* @param string $text
* @return void
*/
protected function cli_problem($text): void {
if (PHPUNIT_TEST) {
echo $text;
} else {
cli_problem($text);
}
}
/**
* Write a text followed by an end of line symbol to the given stream
*
* @param string $text text to be written
*/
protected function cli_writeln($text): void {
$this->cli_write($text . PHP_EOL);
}
/**
* Write to standard error output and exit with the given code
*
* @param string $text
* @param int $errorcode
* @return void (does not return)
*/
protected function cli_error($text, $errorcode = 1): void {
$this->cli_problem($text);
$this->die($errorcode);
}
/**
* Wrapper for "die()" method so we can unittest it
*
* @param mixed $errorcode
* @throws \moodle_exception
*/
protected function die($errorcode): void {
if (!PHPUNIT_TEST) {
die($errorcode);
} else {
throw new \moodle_exception('CLI script finished with error code '.$errorcode);
}
}
/**
* Display as CLI table
*
* @param array $column1
* @param array $column2
* @param int $indent
* @return string
*/
protected function convert_to_table(array $column1, array $column2, int $indent = 0): string {
$maxlengthleft = 0;
$left = [];
$column1 = array_values($column1);
$column2 = array_values($column2);
foreach ($column1 as $i => $l) {
$left[$i] = str_repeat(' ', $indent) . $l;
if (strlen('' . $column2[$i])) {
$maxlengthleft = max($maxlengthleft, strlen($l) + $indent);
}
}
$maxlengthright = 80 - $maxlengthleft - 1;
$output = '';
foreach ($column2 as $i => $r) {
if (!strlen('' . $r)) {
$output .= $left[$i] . "\n";
continue;
}
$right = wordwrap($r, $maxlengthright, "\n");
$output .= str_pad($left[$i], $maxlengthleft) . ' ' .
str_replace("\n", PHP_EOL . str_repeat(' ', $maxlengthleft + 1), $right) . PHP_EOL;
}
return $output;
}
/**
* Display available CLI options as a table
*
* @param array $options
*/
protected function print_help_options(array $options): void {
$left = [];
$right = [];
foreach ($options as $key => $option) {
if ($option['hasvalue'] !== false) {
$l = "--$key={$option['hasvalue']}";
} else if (!empty($option['alias'])) {
$l = "-{$option['alias']}, --$key";
} else {
$l = "--$key";
}
$left[] = $l;
$right[] = $option['description'];
}
$this->cli_write('Options:' . PHP_EOL . $this->convert_to_table($left, $right));
}
/**
* Process the upload
*/
public function process(): void {
// First, validate all arguments.
$definitions = $this->options_definitions();
foreach ($this->clioptions as $key => $value) {
if ($validator = $definitions[$key]['validation'] ?? null) {
$validator($value);
}
}
// Read the CSV file.
$iid = \csv_import_reader::get_new_iid('uploaduser');
$cir = new \csv_import_reader($iid, 'uploaduser');
$cir->load_csv_content(file_get_contents($this->get_cli_option('file')),
$this->get_cli_option('encoding'), $this->get_cli_option('delimiter_name'));
$csvloaderror = $cir->get_error();
if (!is_null($csvloaderror)) {
$this->cli_error(get_string('csvloaderror', 'error', $csvloaderror), 1);
}
// Start upload user process.
$this->process = new \tool_uploaduser\process($cir, $this->progresstrackerclass);
$filecolumns = $this->process->get_file_columns();
$form = $this->mock_form(['columns' => $filecolumns, 'data' => ['iid' => $iid, 'previewrows' => 1]], $this->clioptions);
if (!$form->is_validated()) {
$errors = $form->get_validation_errors();
$this->cli_error(get_string('clivalidationerror', 'tool_uploaduser') . PHP_EOL .
$this->convert_to_table(array_keys($errors), array_values($errors), 2));
}
$this->process->set_form_data($form->get_data());
$this->process->process();
}
/**
* Mock form submission
*
* @param array $customdata
* @param array $submitteddata
* @return \admin_uploaduser_form2
*/
protected function mock_form(array $customdata, array $submitteddata): \admin_uploaduser_form2 {
global $USER;
$submitteddata['description'] = ['text' => $submitteddata['description'], 'format' => FORMAT_HTML];
// Now mock the form submission.
$submitteddata['_qf__admin_uploaduser_form2'] = 1;
$oldignoresesskey = $USER->ignoresesskey ?? null;
$USER->ignoresesskey = true;
$form = new \admin_uploaduser_form2(null, $customdata, 'post', '', [], true, $submitteddata);
$USER->ignoresesskey = $oldignoresesskey;
$form->set_data($submitteddata);
return $form;
}
/**
* Prepare form elements for CLI
*
* @param \HTML_QuickForm_element[] $elements
* @param array $defaults
* @return array
*/
protected function prepare_form_elements_for_cli(array $elements, array $defaults): array {
$options = [];
foreach ($elements as $element) {
if ($element instanceof \HTML_QuickForm_submit || $element instanceof \HTML_QuickForm_static) {
continue;
}
$type = $element->getType();
if ($type === 'html' || $type === 'hidden' || $type === 'header') {
continue;
}
$name = $element->getName();
if ($name === null || preg_match('/^mform_isexpanded_/', $name)
|| preg_match('/^_qf__/', $name)) {
continue;
}
$label = $element->getLabel();
if (!strlen($label) && method_exists($element, 'getText')) {
$label = $element->getText();
}
$default = $defaults[$element->getName()] ?? null;
$postfix = '';
$possiblevalues = null;
if ($element instanceof \HTML_QuickForm_select) {
$selectoptions = $element->_options;
$possiblevalues = [];
foreach ($selectoptions as $option) {
$possiblevalues[] = '' . $option['attr']['value'];
}
if (count($selectoptions) < 10) {
$postfix .= ':';
foreach ($selectoptions as $option) {
$postfix .= "\n ".$option['attr']['value']." - ".$option['text'];
}
}
if (!array_key_exists($name, $defaults)) {
$firstoption = reset($selectoptions);
$default = $firstoption['attr']['value'];
}
// The menu profile field type allows for an empty default value, handle that here.
if (preg_match('/^profile_field_/', $name) && $default === '') {
$possiblevalues[] = $default;
}
}
if ($element instanceof \HTML_QuickForm_checkbox) {
$postfix = ":\n 0|1";
$possiblevalues = ['0', '1'];
}
if ($default !== null & $default !== '') {
$postfix .= "\n ".get_string('clidefault', 'tool_uploaduser')." ".$default;
}
$options[$name] = [
'hasvalue' => 'VALUE',
'description' => $label.$postfix,
'default' => $default,
];
if ($possiblevalues !== null) {
$options[$name]['validation'] = function($v) use ($possiblevalues, $name) {
if (!in_array('' . $v, $possiblevalues)) {
$this->cli_error(get_string('clierrorargument', 'tool_uploaduser',
(object)['name' => $name, 'values' => join(', ', $possiblevalues)]));
}
};
}
}
return $options;
}
/**
* Get process statistics.
*
* @return array
*/
public function get_stats(): array {
return $this->process->get_stats();
}
}
@@ -0,0 +1,43 @@
<?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/>.
/**
* Class cli_progress_tracker
*
* @package tool_uploaduser
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_uploaduser\local;
/**
* Tracks the progress of the user upload and outputs it in CLI script (writes to STDOUT)
*
* @package tool_uploaduser
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cli_progress_tracker extends text_progress_tracker {
/**
* Output one line (followed by newline)
* @param string $line
*/
protected function output_line(string $line): void {
cli_writeln($line);
}
}
@@ -0,0 +1,79 @@
<?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/>.
/**
* File containing the field_value_validators class.
*
* @package tool_uploaduser
* @copyright 2019 Mathew May
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_uploaduser\local;
defined('MOODLE_INTERNAL') || die();
/**
* Field validator class.
*
* @package tool_uploaduser
* @copyright 2019 Mathew May
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class field_value_validators {
/**
* List of valid and compatible themes.
*
* @return array
*/
protected static $themescache;
/**
* Validates the value provided for the theme field.
*
* @param string $value The value for the theme field.
* @return array Contains the validation status and message.
*/
public static function validate_theme($value) {
global $CFG;
$status = 'normal';
$message = '';
// Validate if user themes are allowed.
if (!$CFG->allowuserthemes) {
$status = 'warning';
$message = get_string('userthemesnotallowed', 'tool_uploaduser');
} else {
// Cache list of themes if not yet set.
if (!isset(self::$themescache)) {
self::$themescache = get_list_of_themes();
}
// Check if we have a valid theme.
if (empty($value)) {
$status = 'warning';
$message = get_string('notheme', 'tool_uploaduser');
} else if (!isset(self::$themescache[$value])) {
$status = 'warning';
$message = get_string('invalidtheme', 'tool_uploaduser', s($value));
}
}
return [$status, $message];
}
}
@@ -0,0 +1,124 @@
<?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/>.
/**
* Class text_progress_tracker
*
* @package tool_uploaduser
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_uploaduser\local;
/**
* Tracks the progress of the user upload and echos it in a text format
*
* @package tool_uploaduser
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class text_progress_tracker extends \uu_progress_tracker {
/**
* Print table header.
* @return void
*/
public function start() {
$this->_row = null;
}
/**
* Output one line (followed by newline)
* @param string $line
*/
protected function output_line(string $line): void {
echo $line . PHP_EOL;
}
/**
* Flush previous line and start a new one.
* @return void
*/
public function flush() {
if (empty($this->_row) or empty($this->_row['line']['normal'])) {
// Nothing to print - each line has to have at least number.
$this->_row = array();
foreach ($this->columns as $col) {
$this->_row[$col] = ['normal' => '', 'info' => '', 'warning' => '', 'error' => ''];
}
return;
}
$this->output_line(get_string('linex', 'tool_uploaduser', $this->_row['line']['normal']));
$prefix = [
'normal' => '',
'info' => '',
'warning' => get_string('warningprefix', 'tool_uploaduser') . ' ',
'error' => get_string('errorprefix', 'tool_uploaduser') . ' ',
];
foreach ($this->_row['status'] as $type => $content) {
if (strlen($content)) {
$this->output_line(' '.$prefix[$type].$content);
}
}
foreach ($this->_row as $key => $field) {
foreach ($field as $type => $content) {
if ($key !== 'status' && $type !== 'normal' && strlen($content)) {
$this->output_line(' ' . $prefix[$type] . $this->headers[$key] . ': ' .
str_replace("\n", "\n".str_repeat(" ", strlen($prefix[$type] . $this->headers[$key]) + 4), $content));
}
}
}
foreach ($this->columns as $col) {
$this->_row[$col] = ['normal' => '', 'info' => '', 'warning' => '', 'error' => ''];
}
}
/**
* Add tracking info
* @param string $col name of column
* @param string $msg message
* @param string $level 'normal', 'warning' or 'error'
* @param bool $merge true means add as new line, false means override all previous text of the same type
* @return void
*/
public function track($col, $msg, $level = 'normal', $merge = true) {
if (empty($this->_row)) {
$this->flush();
}
if (!in_array($col, $this->columns)) {
return;
}
if ($merge) {
if ($this->_row[$col][$level] != '') {
$this->_row[$col][$level] .= "\n";
}
$this->_row[$col][$level] .= $msg;
} else {
$this->_row[$col][$level] = $msg;
}
}
/**
* Print the table end
* @return void
*/
public function close() {
$this->flush();
$this->output_line(str_repeat('-', 79));
}
}
+161
View File
@@ -0,0 +1,161 @@
<?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/>.
/**
* Class preview
*
* @package tool_uploaduser
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_uploaduser;
defined('MOODLE_INTERNAL') || die();
use tool_uploaduser\local\field_value_validators;
require_once($CFG->libdir.'/csvlib.class.php');
require_once($CFG->dirroot.'/'.$CFG->admin.'/tool/uploaduser/locallib.php');
/**
* Display the preview of a CSV file
*
* @package tool_uploaduser
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class preview extends \html_table {
/** @var \csv_import_reader */
protected $cir;
/** @var array */
protected $filecolumns;
/** @var int */
protected $previewrows;
/** @var bool */
protected $noerror = true; // Keep status of any error.
/**
* preview constructor.
*
* @param \csv_import_reader $cir
* @param array $filecolumns
* @param int $previewrows
* @throws \coding_exception
*/
public function __construct(\csv_import_reader $cir, array $filecolumns, int $previewrows) {
parent::__construct();
$this->cir = $cir;
$this->filecolumns = $filecolumns;
$this->previewrows = $previewrows;
$this->id = "uupreview";
$this->attributes['class'] = 'generaltable';
$this->tablealign = 'center';
$this->summary = get_string('uploaduserspreview', 'tool_uploaduser');
$this->head = array();
$this->data = $this->read_data();
$this->head[] = get_string('uucsvline', 'tool_uploaduser');
foreach ($filecolumns as $column) {
$this->head[] = $column;
}
$this->head[] = get_string('status');
}
/**
* Read data
*
* @return array
* @throws \coding_exception
* @throws \dml_exception
* @throws \moodle_exception
*/
protected function read_data() {
global $DB, $CFG;
// Track whether values for profile fields defined as unique have already been used.
$profilefieldvalues = [];
$data = array();
$this->cir->init();
$linenum = 1; // Column header is first line.
while ($linenum <= $this->previewrows and $fields = $this->cir->next()) {
$linenum++;
$rowcols = array();
$rowcols['line'] = $linenum;
foreach ($fields as $key => $field) {
$rowcols[$this->filecolumns[$key]] = s(trim($field));
}
$rowcols['status'] = array();
if (isset($rowcols['username'])) {
$stdusername = \core_user::clean_field($rowcols['username'], 'username');
if ($rowcols['username'] !== $stdusername) {
$rowcols['status'][] = get_string('invalidusernameupload');
}
if ($userid = $DB->get_field('user', 'id',
['username' => $stdusername, 'mnethostid' => $CFG->mnet_localhost_id])) {
$rowcols['username'] = \html_writer::link(
new \moodle_url('/user/profile.php', ['id' => $userid]), $rowcols['username']);
}
} else {
$rowcols['status'][] = get_string('missingusername');
}
if (isset($rowcols['email'])) {
if (!validate_email($rowcols['email'])) {
$rowcols['status'][] = get_string('invalidemail');
}
$select = $DB->sql_like('email', ':email', false, true, false, '|');
$params = array('email' => $DB->sql_like_escape($rowcols['email'], '|'));
if ($DB->record_exists_select('user', $select , $params)) {
$rowcols['status'][] = get_string('useremailduplicate', 'error');
}
}
if (isset($rowcols['theme'])) {
list($status, $message) = field_value_validators::validate_theme($rowcols['theme']);
if ($status !== 'normal' && !empty($message)) {
$rowcols['status'][] = $message;
}
}
// Check if rowcols have custom profile field with correct data and update error state.
$this->noerror = uu_check_custom_profile_data($rowcols, $profilefieldvalues) && $this->noerror;
$rowcols['status'] = implode('<br />', $rowcols['status']);
$data[] = $rowcols;
}
if ($fields = $this->cir->next()) {
$data[] = array_fill(0, count($fields) + 2, '...');
}
$this->cir->close();
return $data;
}
/**
* Getter for noerror
*
* @return bool
*/
public function get_no_error() {
return $this->noerror;
}
}
@@ -0,0 +1,46 @@
<?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/>.
/**
* Privacy Subsystem implementation for tool_uploaduser.
*
* @package tool_uploaduser
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_uploaduser\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for tool_uploaduser implementing null_provider.
*
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
File diff suppressed because it is too large Load Diff