first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-06-08 17:09:23 -04:00
commit df3a033196
17887 changed files with 8637778 additions and 0 deletions
+313
View File
@@ -0,0 +1,313 @@
<?php
/**
* @file tools/buildSwagger.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class buildSwagger
*
* @ingroup tools
*
* @brief CLI tool to compile a complete swagger.json file for hosting API
* documentation.
*/
use APP\core\Services;
use PKP\decision\DecisionType;
use PKP\file\FileManager;
define('APP_ROOT', dirname(__FILE__, 4));
require(APP_ROOT . '/tools/bootstrap.php');
class buildSwagger extends \PKP\cliTool\CommandLineTool
{
public $outputFile;
public $parameters;
/**
* Constructor.
*
* @param array $argv command-line arguments (see usage)
*/
public function __construct($argv = [])
{
parent::__construct($argv);
$this->outputFile = array_shift($this->argv);
}
/**
* Print command usage information.
*/
public function usage()
{
echo "Command-line tool to compile swagger.json API definitions\n"
. "Usage:\n"
. "\t{$this->scriptName} [outputFile]: Compile swagger file and save to [outputFile]\n"
. "\t{$this->scriptName} usage: Display usage information this tool\n";
}
/**
* Parse and execute the import/export task.
*/
public function execute()
{
if (empty($this->outputFile)) {
$this->usage();
exit;
} elseif ((file_exists($this->outputFile) && !is_writable($this->outputFile)) ||
(!is_writeable(dirname($this->outputFile)))) {
echo "You do not have permission to write to this file.\n";
exit;
} else {
$source = file_get_contents(APP_ROOT . '/docs/dev/swagger-source.json');
$decisions = file_get_contents(APP_ROOT . '/docs/dev/swagger-source-decision-examples.json');
if (!$source || !$decisions) {
echo 'Unable to find source files at ' . APP_ROOT . '/docs/dev/';
exit;
}
$locales = ['en', 'fr_CA'];
$apiSchema = json_decode($source);
foreach ($apiSchema->definitions as $definitionName => $definition) {
// We assume a definition that is not a string does not need to be compiled
// from the schema files. It has already been defined.
if (!is_string($definition)) {
continue;
}
$editDefinition = $summaryDefinition = $readDefinition = ['type' => 'object', 'properties' => []];
$entitySchema = Services::get('schema')->get($definition, true);
foreach ($entitySchema->properties as $propName => $propSchema) {
$editPropSchema = clone $propSchema;
$readPropSchema = clone $propSchema;
$summaryPropSchema = clone $propSchema;
// Special handling to catch readOnly, writeOnly and apiSummary props in objects
if (!empty($propSchema->{'$ref'})) {
if (empty($propSchema->readOnly) || empty($propSchema->writeDisabledInApi)) {
$editPropSchema->properties = $propSchema;
}
if (empty($propSchema->writeOnly)) {
$readPropSchema->properties = $propSchema;
}
if (!empty($propSchema->apiSummary)) {
$summaryPropSchema->properties = $propSchema;
}
} elseif ($propSchema->type === 'object') {
$subPropsEdit = $subPropsRead = $subPropsSummary = [];
foreach ($propSchema->properties as $subPropName => $subPropSchema) {
if (empty($subPropSchema->readOnly) && empty($propSchema->writeDisabledInApi)) {
$subPropsEdit[$subPropName] = $subPropSchema;
}
if (empty($subPropSchema->writeOnly)) {
$subPropsRead[$subPropName] = $subPropSchema;
}
if (!empty($subPropSchema->apiSummary)) {
$subPropsSummary[$subPropName] = $subPropSchema;
}
}
if (!empty($propSchema->multilingual)) {
$subPropsSchemaEdit = $subPropsSchemaRead = $subPropsSchemaSummary = [
'type' => 'object',
'properties' => [],
];
foreach ($locales as $localeKey) {
$subPropsSchemaEdit[$localeKey]['properties'] = $subPropsEdit;
$subPropsSchemaRead[$localeKey]['properties'] = $subPropsRead;
$subPropsSchemaSummary[$localeKey]['properties'] = $subPropsSummary;
}
} else {
$subPropsSchemaEdit = $subPropsEdit;
$subPropsSchemaRead = $subPropsRead;
$subPropsSchemaSummary = $subPropsSummary;
}
if (empty($propSchema->readOnly) && empty($propSchema->writeDisabledInApi)) {
$editPropSchema->properties = $subPropsSchemaEdit;
}
if (empty($propSchema->writeOnly)) {
$readPropSchema->properties = $subPropsSchemaRead;
}
if (!empty($propSchema->apiSummary)) {
$summaryPropSchema->properties = $subPropsSchemaSummary;
}
// All non-object props
} else {
if (!empty($propSchema->multilingual)) {
if ($propSchema->type === 'array') {
$subProperties = [];
foreach ($locales as $localeKey) {
$subProperties[$localeKey] = $propSchema->items;
}
if (empty($propSchema->readOnly) && empty($propSchema->writeDisabledInApi)) {
$editPropSchema->properties = $subProperties;
}
if (empty($propSchema->writeOnly)) {
$readPropSchema->properties = $subProperties;
}
if (!empty($propSchema->apiSummary)) {
$summaryPropSchema->properties = $subProperties;
}
} else {
if (empty($propSchema->readOnly) && empty($propSchema->writeDisabledInApi)) {
$editPropSchema = ['$ref' => '#/definitions/LocaleObject'];
}
if (empty($propSchema->writeOnly)) {
$readPropSchema = ['$ref' => '#/definitions/LocaleObject'];
}
if (!empty($propSchema->apiSummary)) {
$summaryPropSchema = ['$ref' => '#/definitions/LocaleObject'];
}
}
}
}
if (empty($propSchema->readOnly) && empty($propSchema->writeDisabledInApi)) {
$editDefinition['properties'][$propName] = $editPropSchema;
}
if (empty($propSchema->writeOnly)) {
$readDefinition['properties'][$propName] = $readPropSchema;
}
if (!empty($propSchema->apiSummary)) {
$summaryDefinition['properties'][$propName] = $summaryPropSchema;
}
}
if (!empty($editDefinition['properties'])) {
$definitionEditableName = $definitionName . 'Editable';
ksort($editDefinition['properties']);
$apiSchema->definitions->{$definitionEditableName} = $this->setEnum($editDefinition);
}
if (!empty($readDefinition['properties'])) {
ksort($readDefinition['properties']);
$apiSchema->definitions->{$definitionName} = $this->setEnum($readDefinition);
}
if (!empty($summaryDefinition['properties'])) {
$definitionSummaryName = $definitionName . 'Summary';
ksort($summaryDefinition['properties']);
$apiSchema->definitions->{$definitionSummaryName} = $this->setEnum($summaryDefinition);
}
}
$this->addDecisionExamples($apiSchema, json_decode($decisions));
file_put_contents($this->outputFile, json_encode($apiSchema, JSON_PRETTY_PRINT));
echo "Done\n";
}
}
/**
* Convert the `in:` validation rules to swagger's
* `enum` specification
*/
protected function setEnum(array $definition): array
{
foreach ($definition['properties'] as $propName => $schema) {
if (isset($schema->validation) && is_array($schema->validation)) {
foreach ($schema->validation as $rule) {
if (substr($rule, 0, 3) === 'in:') {
$enum = explode(',', substr($rule, 3));
if ($schema->type === 'integer') {
$enum = array_map('intval', $enum);
}
$definition['properties'][$propName]->enum = $enum;
}
}
}
}
return $definition;
}
/**
* Add the example request bodies for each decision
*/
protected function addDecisionExamples(stdClass $schema, stdClass $decisions): void
{
$examples = [];
foreach ($decisions as $class => $decision) {
/** @var DecisionType $object */
$object = new $class();
$value = [
'decision' => $object->getDecision(),
];
if ($this->isDecisionInReview($object)) {
$value['reviewRound'] = 123;
$value['round'] = 1;
}
if (!empty($decision->actions)) {
$value['actions'] = array_map(
function (stdClass $action) {
if ($action->type === 'form') {
return array_merge((array) $action->data, ['id' => $action->id]);
} elseif ($action->type === 'email') {
return [
'attachments' => [
[
'name' => 'example-upload.pdf',
'temporaryFileId' => 1,
'documentType' => FileManager::DOCUMENT_TYPE_PDF
],
[
'name' => 'example-submission-file.pdf',
'submissionFileId' => 1,
'documentType' => FileManager::DOCUMENT_TYPE_PDF
],
[
'name' => 'example-library-file.pdf',
'libraryFileId' => 1,
'documentType' => FileManager::DOCUMENT_TYPE_PDF
]
],
'bcc' => 'example@pkp.sfu.ca',
'cc' => 'example@pkp.sfu.ca',
'id' => $action->id,
'locale' => 'en',
'recipients' => $action->canChangeRecipients
? [1,2]
: [],
'subject' => 'Example email subject',
'body' => '<p>Example email body.</p>',
];
}
throw new Exception('Unrecognized decision action type. Can not compile example request body for decision ' . $class);
},
$decision->actions ?? []
);
}
$examples[$class] = [
'summary' => $object->getLabel(),
'value' => $value,
];
}
$schema
->paths
->{'/submissions/{submissionId}/decisions'}
->post
->requestBody
->content
->{'application/json'}
->examples = $examples;
}
/**
* Is the decision type in a review stage?
*/
protected function isDecisionInReview(DecisionType $decision): bool
{
return in_array($decision->getStageId(), [WORKFLOW_STAGE_ID_INTERNAL_REVIEW, WORKFLOW_STAGE_ID_EXTERNAL_REVIEW]);
}
}
$tool = new buildSwagger($argv ?? []);
$tool->execute();
+231
View File
@@ -0,0 +1,231 @@
#!/bin/bash
# @file tools/buildjs.sh
#
# Copyright (c) 2014-2021 Simon Fraser University
# Copyright (c) 2010-2021 John Willinsky
# Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
#
# Script to check and minimize JavaScript for distribution.
#
# Requirements:
# - Requires Python/Closure Linter and Java/Closure Compiler, see
# <http://code.google.com/closure>. Install this using npm.
# Please see the Closure Linter documentation for installation instructions
# of that tool.
#
# - Requires jslint4java, see <http://code.google.com/p/jslint4java/>. Expects the
# jslint4java.jar (must be renamed!) in the same path as the Closure compiler,
# i.e. in TOOL_PATH as configured below.
#
# - This tool expects to be run from the application's main directory.
#
# Usage: lib/pkp/tools/buildjs.sh [-n]
# ...where -n can be optionally specified to prevent caching.
#
### OS specific configuration ###
# Define a tab to be used inside of sed commands (sed on OSX does not recognize \t)
TAB=$'\t'
# Determine what flag to use for extended regular expressions
if [ `uname` == 'Darwin' ]; then
EXTENDED_REGEX_FLAG='E'
else
EXTENDED_REGEX_FLAG='r'
fi
### Configuration ###
TOOL_PATH=~/bin
CLOSURE_COMPILER_JAR=./node_modules/google-closure-compiler-java/compiler.jar
JS_OUTPUT='js/pkp.min.js'
CLOSURE_EXTERNS='
--externs lib/pkp/tools/closure-externs.js
--externs lib/pkp/tools/closure-externs-check-only.js
--externs lib/pkp/tools/jquery-externs.js'
### Command Line Options ###
OPTIND=1
DO_CACHE=1
while getopts "n" opt; do
case "$opt" in
n) DO_CACHE=0 # No caching
;;
esac
done
shift $((OPTIND-1))
### Start Processing ###
echo >&2
echo "Starting PKP JavaScript builder." >&2
echo "Copyright (c) 2014-2021 Simon Fraser University" >&2
echo "Copyright (c) 2010-2021 John Willinsky" >&2
### Checking Requirements ###
MISSING_REQUIREMENT=''
if [ -z `which gjslint` ]; then
echo >&2
echo "Google Closure Linter not found in PATH. Please go" >&2
echo "to <https://developers.google.com/closure/utilities/docs/linter_howto>" >&2
echo "and make sure that you correctly install the tool before you run" >&2
echo "buildjs.sh." >&2
MISSING_REQUIREMENT='gjslint'
fi
if [ ! -e "$TOOL_PATH/jslint4java.jar" ]; then
echo >&2
echo "JSLint4Java must be installed in the '$TOOL_PATH'" >&2
echo "directory. Please download the tool from" >&2
echo "<http://code.google.com/p/jslint4java/>," >&2
echo "rename it to jslint4java.jar and try again." >&2
MISSING_REQUIREMENT='jslint4java'
fi
if [ ! -e "$CLOSURE_COMPILER_JAR" ]; then
echo >&2
echo "Google Closure Compiler not found in '$CLOSURE_COMPILER_JAR'" >&2
echo "Please run 'npm npm install --save google-closure-compiler' and try again." >&2
MISSING_REQUIREMENT='closure'
fi
if [ -n "$MISSING_REQUIREMENT" ]; then
echo >&2
echo "Exiting!" >&2
exit 1
fi
echo >&2
# A list with all files to be compiled and minified. Expects
# a complete list of script files in registry/minifiedScripts.txt.
COMPILE_FILES=$(sed -n '/^[^#]/p' registry/minifiedScripts.txt)
# FIXME: For now we only check classes as the other
# files contain too many errors to be fixed right now.
LINT_FILES=`echo "$COMPILE_FILES" | egrep -v '^lib/pkp/js/(lib|functions)'`
# Create a working directory in the cache
WORKDIR=`mktemp -dt tmp.XXXXXXXXXX` || { echo "The working directory could not be created\!"; exit 1; }
# Show a list of the files we are going to lint.
echo "Lint..." >&2
echo "Lint..." >"$WORKDIR/.compile-warnings.out"
for JS_FILE in $LINT_FILES; do
echo -n "...$JS_FILE" >&2
echo "...$JS_FILE"
# Prepare file for gjslint and compiler check:
# - transforms whitespace to comply with Google style guide
# - wraps @extends type in curly braces to comply with Google style guide.
# - works around http://code.google.com/p/closure-compiler/issues/detail?id=61 by removing the jQuery closure.
mkdir -p `dirname "$WORKDIR/$JS_FILE"`
sed \
-e "s/^${TAB}//" \
-e "s/${TAB}/ /g" \
-e 's/^(function(\$) {//' \
-e 's/^}(jQuery));//' \
-e 's/@extends \(.*\)$/@extends {\1}/' \
"$JS_FILE" > "$WORKDIR/$JS_FILE"
# Only lint file if it has been changed since last compilation.
if [ ! \( -e "$JS_OUTPUT" \) -o \( "$JS_FILE" -nt "$JS_OUTPUT" \) -o \( "$DO_CACHE" -eq 0 \) ]; then
#############################
### Google Closure Linter ###
#############################
# Run gjslint on the file.
gjslint --strict --nosummary --custom_jsdoc_tags=defgroup,ingroup,file,brief "$WORKDIR/$JS_FILE" | grep '^Line' | sed "s/^/${TAB}/"
##################################
### Douglas Crockford's JSLint ###
##################################
# Run JSLint on the file:
# - Allow for loops without "hasOwnProperty()" check because we operate in an environment
# where additions to the Object prototype are not allowed (same as jQuery).
# - Do not alert on whitespace checking which we prefer to be checked by gjslint.
# This is necessary to remove inconsistency between gjslint's and
# jslint's whitespace rules.
# - We allow dangling underscores (_) to mark private properties and let the
# Closure compiler enforce it.
# - We allow the ++ and == syntax
# - We allow "continue"
# - Multiple var statements in one function are allowed to reduce variable span.
# - We allow code without the 'use strict' pragma as we need the callee property
# for our class framework implementation.
java -jar "$TOOL_PATH/jslint4java.jar" --white --forin --nomen --plusplus --continue \
--eqeq --sloppy --browser --predef pkp,jQuery,alert,tinyMCE,confirm,plupload \
--regexp "$JS_FILE" | sed "s/^/${TAB}/"
echo "...processed!" >&2
else
echo "...skipped!" >&2
fi
done >>"$WORKDIR/.compile-warnings.out"
echo >&2
###############################
### Google Closure Compiler ###
###############################
# Transform lint file list into Closure input parameter list.
LINT_FILES=`echo "$LINT_FILES" | sed "s%^%$WORKDIR/%" | tr '\n' ' ' | sed -$EXTENDED_REGEX_FLAG 's/ $//;s/(^| )/ --js /g'`
# Run Closure - first pass to check with transformed files.
echo >> "$WORKDIR/.compile-warnings.out"
echo "Compile (Check)..." >> "$WORKDIR/.compile-warnings.out"
echo "Compile (Check)..." >&2
java -jar ${CLOSURE_COMPILER_JAR} --language_in=ECMASCRIPT5 --jscomp_warning visibility --warning_level DEFAULT \
$CLOSURE_EXTERNS $LINT_FILES --js_output_file /dev/null 2>&1 \
| sed "s/^/${TAB}/" >>"$WORKDIR/.compile-warnings.out"
# Only minify when there were no warnings.
if [ -n "`cat $WORKDIR/.compile-warnings.out | grep '^ ' | grep -v 'Picked up _JAVA_OPTIONS'`" ]; then
# Issue warnings. If interactive, use "less".
case "$-" in
*i*) less "$WORKDIR/.compile-warnings.out" ;;
*) cat "$WORKDIR/.compile-warnings.out" ;;
esac
echo >&2
echo "Found Errors! Not minified."
echo "Exiting!"
# Remove the temporary directory.
rm -r "$WORKDIR"
exit -1
fi
# Show the list of files we are going to compile:
echo >&2
echo "Compile (Minify)..." >&2
echo "$COMPILE_FILES" | sed 's/^/.../' >&2
# Transform file list into Closure input parameter list.
COMPILE_FILES=`echo "$COMPILE_FILES" | tr '\n' ' ' | sed -$EXTENDED_REGEX_FLAG 's/ $//;s/(^| )/ --js /g'`
# Run Closure - second pass to minify
java -jar ${CLOSURE_COMPILER_JAR} --language_in=ECMASCRIPT5 --jscomp_off checkTypes --warning_level DEFAULT $COMPILE_FILES \
$CLOSURE_EXTERNS --js_output_file "$JS_OUTPUT" 2>&1
echo >&2
echo "Please don't forget to set enable_minified=On in your config.inc.php." >&2
echo >&2
echo "Done!" >&2
# Remove the temporary directory.
rm -r "$WORKDIR"
exit 0
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
# @file tools/checkHelp.sh
#
# Copyright (c) 2014-2021 Simon Fraser University
# Copyright (c) 2010-2021 John Willinsky
# Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
#
# Script to check help file mappings from code to Markdown.
#
# Usage: lib/pkp/tools/checkHelp.sh
#
# Look for help filenames referred to in templates and check that they all exist (in English)
ERRORS=0
for filename in `find . -name \*.tpl -exec sed -n -e "s/.*{help[^}]file=\"\([^\"#]\+\)[#\"].*/\1/p" "{}" ";"`; do
if [ ! -f docs/manual/en/$filename.md ]; then
echo "Help file \"$filename.md\" referred to in template does not exist!"
ERRORS=1
fi
done
if [ $ERRORS -ne 0 ]; then
exit -1
fi
# Generate a quick report of the differences between the files listed in templates and the available files.
find . -name \*.tpl -exec sed -n -e "s/.*{help[^}]file=\"\([^\"]\+\)\".*/\1/p" "{}" ";" | sort | uniq > /tmp/template-help-references.txt
cat docs/manual/en/SUMMARY.md | sed -n -e "s/.*(\([^)]\+\))/\1/p" | sort | uniq > /tmp/help-files.txt
echo "Unreferenced help files:"
diff /tmp/template-help-references.txt /tmp/help-files.txt | grep -e "^>"
# Successful completion
exit 0
@@ -0,0 +1,56 @@
/**
* closure-externs-check-only.js
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2010-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* Import symbols into the closure compiler that are not defined
* within the files compiled during the strict check phase of the build
* script. (We only include classes for strict checking, not legacy
* function.)
*
* @externs
*/
// FIXME: Replace the reference to the ajaxAction() function
// with an object/event oriented approach, see #6339.
/**
* @param {string} actOnId the ID of an element to be changed.
* @param {string} callingElement selector of the element that triggers the ajax call
* @param {string} url the url to be called, defaults to the form action in case of
* action type 'post'.
* @param {Object=} data (post action type only) the data to be posted, defaults to
* the form data.
* @param {string=} eventName the name of the event that triggers the action, default 'click'.
* @param {string=} form the selector of a form element.
*/
function ajaxAction(actOnId, callingElement, url, data, eventName, form) {}
/**
* @param {string} jsonStr The string to parse.
* @param {(function(string, *) : *)=} opt_reviver
* @return {*} The JSON object.
* @throws {Error}
* @nosideeffects
*/
JSONType.prototype.parse = function(jsonStr, opt_reviver) {};
/**
* @param {*} jsonObj Input object.
* @param {(Array.<string>|(function(string, *) : *)|null)=} opt_replacer
* @param {(number|string)=} opt_space
* @return {string} JSON string which represents jsonObj.
* @throws {Error}
* @nosideeffects
*/
JSONType.prototype.stringify = function(jsonObj, opt_replacer, opt_space) {};
/**
* @type {!JSONType}
* @suppress {duplicate}
*/
var JSON;
+319
View File
@@ -0,0 +1,319 @@
/**
* closure-externs.js
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2010-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* Import symbols into the closure compiler that are not defined
* within the compiled files.
*
* See https://github.com/google/closure-compiler/tree/master/externs
* for pre-extracted extern files, e.g. for jQuery.
*
* @externs
*/
jQueryObject.prototype.browser = { msie: false };
/**
* @param {Object} arg1
*/
jQueryObject.prototype.autocomplete = function(arg1) {};
/**
* @param {string=} param1
* @param {string|number=} param2
* @param {string=} param3
*/
jQueryObject.prototype.button = function(param1, param2, param3) {};
/**
* @param {Object=} options
*/
jQueryObject.prototype.validate = function(options) {};
jQueryObject.prototype.valid = function() {};
/**
* @param {Function} param1
*/
jQueryObject.prototype.sortElements = function(param1) {};
/**
* @param {Object|string=} param1
*/
jQueryObject.prototype.sortable = function(param1) {};
/**
* @param {Object=} options
*/
jQueryObject.prototype.jLabel = function(options) {};
/**
* @param {Object=} options
*/
jQueryObject.prototype.selectBox = function(options) {};
jQueryObject.prototype.equalizeElementHeights = function() {};
/**
* @param {Object=} options
*/
jQueryObject.prototype.slider = function(options) {};
/**
* @param {string|Object=} param1
* @param {string|number|Object=} param2
* @param {string|number|Object=} param3
*/
jQueryObject.prototype.tabs = function(param1, param2, param3) {};
/**
* @param {string|Object} param1
* @param {string|Object=} param2
*/
jQueryObject.prototype.datepicker = function(param1, param2) {};
/**
* @param {string|Object} param1
* @param {string|Object|boolean|number=} param2
* @param {string|boolean=} param3
*/
jQueryObject.prototype.accordion = function(param1, param2, param3) {};
/**
* Handler plug-in.
* @param {string} handlerName The handler to be instantiated
* and attached to the target HTML element(s).
* @param {Object=} options Parameters to be passed on
* to the handler.
* @return {jQueryObject} Selected HTML elements for chaining.
*/
jQueryObject.prototype.pkpHandler = function(handlerName, options) {};
/**
* Re-implementation of jQuery's html() method
* with a remote source.
* @param {string} url the AJAX endpoint from which to
* retrieve the HTML to be inserted.
* @param {Object=} callback function to be called on ajax success.
* @return {jQueryObject} Selected HTML elements for chaining.
*/
jQueryObject.prototype.pkpAjaxHtml = function(url, callback) {};
/**
* @param {string|Object=} param1
* @param {string=} param2
* @param {string|Object=} param3
*/
jQueryObject.prototype.dialog = function(param1, param2, param3) {};
/**
* @constructor
* @param {Object=} options
* @param {jQueryObject=} form
*/
jQuery.validator = function(options, form) {};
jQuery.validator.prototype.checkForm = function() {};
jQuery.validator.prototype.defaultShowErrors = function() {};
jQuery.validator.prototype.settings = {};
/**
* @param {string} param1
* @param {string|boolean|Object=} param2
*/
jQueryObject.prototype.prop = function(param1, param2) {};
jQueryObject.prototype.panel = null;
jQueryObject.prototype.newTab = null;
jQueryObject.prototype.newTab.index = function() {};
jQueryObject.prototype.newTab.find = function() {};
jQueryObject.prototype.newPanel = null;
jQueryObject.prototype.ajaxSettings = null;
jQueryObject.prototype.jqXHR = null;
/**
* @constructor
* @private
*/
function tinyMCEObject() {}
tinyMCEObject.prototype.PluginManager = {};
/**
* @param {string} param1
* @param {string} param2
* @return {tinyMCEObject}
*/
tinyMCEObject.prototype.PluginManager.load = function(param1, param2) {};
tinyMCEObject.prototype.EditorManager = {};
tinyMCEObject.prototype.EditorManager.triggerSave = function() {};
/**
* @param {string} param1
* @param {Object} param2
* @return {tinyMCEObject}
*/
tinyMCEObject.prototype.EditorManager.createEditor = function(param1, param2) {};
/**
* @param {string} param1
* @return {tinyMCEObject}
*/
tinyMCEObject.prototype.EditorManager.get = function(param1) {};
/**
* @param {Object} param1
*/
tinyMCEObject.prototype.init = function(param1) {};
/**
* @param {string} param1
* @return {tinyMCEObject}
*/
tinyMCEObject.prototype.get = function(param1) {};
tinyMCEObject.prototype.target = {dom: {}, editorContainer: {}};
/**
* @param {string} param1
*/
tinyMCEObject.prototype.target.dom.get = function(param1) {};
tinyMCEObject.prototype.target.getContent = function() {};
tinyMCEObject.prototype.getContent = function() {};
/**
* @param {string} param1
*/
tinyMCEObject.prototype.setContent = function(param1) {};
tinyMCEObject.prototype.render = function() {};
/**
* @param {string} param1
* @param {Object} param2
*/
tinyMCEObject.prototype.on = function(param1, param2) {};
tinyMCEObject.prototype.off = function() {};
tinyMCEObject.prototype.editor = { dom: {}, id: '' };
tinyMCEObject.prototype.dom = {};
tinyMCEObject.prototype.editor.dom.getRoot = function() {};
/**
* @type {string} c
*/
tinyMCEObject.prototype.id = '';
tinyMCEObject.prototype.getWin = function() {};
tinyMCEObject.prototype.getBody = function() {};
tinyMCEObject.prototype.getContainer = function() {};
tinyMCEObject.prototype.onSetContent = function() {};
/**
* @param {Object} param1
*/
tinyMCEObject.prototype.onSetContent.add = function(param1) {};
/**
* @param {Object} param1
*/
tinyMCEObject.prototype.onSetContent.remove = function(param1) {};
/**
* @type {tinyMCEObject}
*/
var tinyMCE;
/**
* @param {string} f
*/
jQueryObject.prototype.plupload = function(f) {};
var plupload = {};
/**
* @param {Object} options
* @constructor
*/
plupload.Uploader = function (options) {};
plupload.Uploader.prototype.id = null;
plupload.Uploader.prototype.init = function() {};
plupload.Uploader.prototype.refresh = function() {};
/**
* @param {string|number} p
*/
plupload.Uploader.prototype.percent = function(p) {};
/**
* @param {string} f
*/
plupload.Uploader.prototype.removeFile = function(f) {};
/**
* @param {!string} eventName
* @param {Function} f
*/
plupload.Uploader.prototype.bind = function(eventName, f) {};
$.pkp.app = {
baseUrl: '',
tinyMceContentCSS: '',
};
$.pkp.cons = {
WORKFLOW_STAGE_ID_SUBMISSION: 0,
WORKFLOW_STAGE_ID_INTERNAL_REVIEW: 0,
WORKFLOW_STAGE_ID_EXTERNAL_REVIEW: 0,
WORKFLOW_STAGE_ID_EDITING: 0,
WORKFLOW_STAGE_ID_PRODUCTION: 0,
REALLY_BIG_NUMBER: 0,
ORDER_CATEGORY_GRID_CATEGORIES_ONLY: 0,
ORDER_CATEGORY_GRID_CATEGORIES_AND_ROWS: 0,
LISTBUILDER_SOURCE_TYPE_SELECT: 0,
LISTBUILDER_OPTGROUP_LABEL: 0,
ORDER_CATEGORY_GRID_CATEGORIES_ROWS_ONLY: 0,
UPLOAD_MAX_FILESIZE: 0,
INSERT_TAG_VARIABLE_TYPE_PLAIN_TEXT: 0
};
/**
* @type {Object}
*/
var _ = {
isNull: function(object) {},
each: function(array, callback) {},
reject: function(array, callback) {}
};
/**
* @type {Object}
*/
var pkp = {
currentUser: {
id: 0,
csrfToken: '',
roles: []
},
eventBus: {
$emit: function(name, data) {},
$on: function(name, callback) {},
$off: function(name, callback) {}
},
registry: {
_instances: []
}
};
+80
View File
@@ -0,0 +1,80 @@
<?php
/**
* @file tools/constants.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class constants
*
* @ingroup tools
*
* @brief Get the value of application constants.
*/
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
class constants extends \PKP\cliTool\CommandLineTool
{
public $value;
/**
* Constructor
*/
public function __construct($argv = [])
{
parent::__construct($argv);
if (isset($argv[1]) && in_array($argv[1], ['--help', '-h'])) {
$this->usage();
exit;
}
if (isset($argv[1])) {
$this->value = $argv[1];
}
}
/**
* Print command usage information.
*/
public function usage()
{
echo "Get the value of application constants.\n"
. "\n"
. "Usage: {$this->scriptName} [value]\n"
. "[value] Get the name of constants that match this value.\n"
. "\n"
. "This will only match application-wide constants available after the initial bootstrap.\n";
}
/**
* Generate test metrics
*/
public function execute()
{
$all = get_defined_constants(true);
$app = $all['user'];
if ($this->value) {
$constants = [];
foreach ($app as $const => $value) {
if ($this->value == $value) {
$constants[] = $const;
}
}
if (empty($constants)) {
echo 'No constants were found with that value. This tool only matches constants loaded in the application bootstrap process and may miss other constants.';
} else {
print_r($constants);
}
} else {
print_r($app);
}
}
}
$tool = new constants($argv ?? []);
$tool->execute();
File diff suppressed because it is too large Load Diff
+114
View File
@@ -0,0 +1,114 @@
<?php
/**
* @file tools/convertUsageStatsLogFile.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ConvertUsageStatsLogFile
*
* @ingroup tools
*
* @brief CLI tool to convert an old usage stats log file (used in releases < 3.4) into the new format.
*
*/
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
use APP\statistics\StatisticsHelper;
use PKP\cliTool\ConvertLogFileTool;
use PKP\task\FileLoader;
class ConvertUsageStatsLogFile extends ConvertLogFileTool
{
/**
* Weather the URL parameters are used instead of CGI PATH_INFO.
* This is the former variable 'disable_path_info' in the config.inc.php
*
* This needs to be set to true if the URLs in the old log file contain the paramteres as URL query string.
*/
public const PATH_INFO_DISABLED = false;
/**
* Regular expression that is used for parsing the old log file entries that should be converted to the new format.
*
* The default regex can parse the usageStats plugin's log files.
*/
public const PARSEREGEX = '/^(?P<ip>\S+) \S+ \S+ "(?P<date>.*?)" (?P<url>\S+) (?P<returnCode>\S+) "(?P<userAgent>.*?)"/';
/**
* PHP format of the time in the log file.
* S. https://www.php.net/manual/en/datetime.format.php
*
* This default format can parse the date in the usageStats plugin's log files.
*/
public const PHP_DATETIME_FORMAT = 'Y-m-d H:i:s';
/**
* Name of the log file that should be converted into the new format.
*/
public string $fileName;
/**
* Constructor.
*
* @param array $argv command-line arguments (see usage)
*/
public function __construct(array $argv = [])
{
parent::__construct($argv);
if (count($this->argv) != 1) {
$this->usage();
exit(8);
}
$this->fileName = array_shift($this->argv);
}
/**
* Print command usage information.
*/
public function usage(): void
{
$archivePath = $this->getLogFileDir();
echo "\nConvert an old usage stats log file.\nThe old usage stats log file needs to be in the folder {$archivePath}.\n\n"
. " Usage: php {$this->scriptName} [fileName]\n\n";
}
public function getLogFileDir(): string
{
return StatisticsHelper::getUsageStatsDirPath() . '/' . FileLoader::FILE_LOADER_PATH_ARCHIVE;
}
public function getParseRegex(): string
{
return self::PARSEREGEX;
}
public function getPhpDateTimeFormat(): string
{
return self::PHP_DATETIME_FORMAT;
}
public function isPathInfoDisabled(): bool
{
return self::PATH_INFO_DISABLED;
}
public function isApacheAccessLogFile(): bool
{
return false;
}
/**
* Convert the file.
*/
public function execute(): void
{
$this->convert($this->fileName);
}
}
$tool = new ConvertUsageStatsLogFile($argv ?? []);
$tool->execute();
+207
View File
@@ -0,0 +1,207 @@
<?php
declare(strict_types=1);
/**
* @file tools/events.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class commandEvents
*
* @ingroup tools
*
* @brief CLI tool to list all events registered on the system
*/
namespace PKP\tools\event;
use Illuminate\Console\Concerns\InteractsWithIO;
use Illuminate\Console\OutputStyle;
use PKP\cliTool\CommandLineTool;
use PKP\core\EventServiceProvider;
use PKP\core\PKPContainer;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\StreamOutput;
use Throwable;
define('APP_ROOT', dirname(__FILE__, 4));
require_once APP_ROOT . '/tools/bootstrap.php';
class commandEvents extends CommandLineTool
{
use InteractsWithIO;
protected const AVAILABLE_OPTIONS = [
'cache' => 'Create an Events cached version',
'clear' => 'Clear the Events cached version',
'list' => 'List all events on the system',
'usage' => 'Display the command usage'
];
/**
* @var null|string Which option will be call?
*/
protected $option = null;
/**
* Constructor
*/
public function __construct($argv = [])
{
parent::__construct($argv);
array_shift($argv);
$this->option = array_shift($argv);
if (!$this->option) {
throw new CommandNotFoundException(
'Option could not be empty! Check the usage method.',
array_keys(self::AVAILABLE_OPTIONS)
);
}
$output = new OutputStyle(
new StringInput(''),
new StreamOutput(fopen('php://stdout', 'w'))
);
$this->setOutput($output);
}
/**
* Print command usage information.
*/
public function usage()
{
$this->line('<comment>Usage:</comment>');
$this->line('command [arguments]' . PHP_EOL);
$this->line('<comment>Available commands for the "events" namespace:</comment>');
$width = $this->getColumnWidth(array_keys(self::AVAILABLE_OPTIONS));
foreach (self::AVAILABLE_OPTIONS as $commandName => $description) {
$spacingWidth = $width - Helper::width($commandName);
$this->line(
sprintf(
' <info>%s</info>%s%s',
$commandName,
str_repeat(' ', $spacingWidth),
$description
)
);
}
}
/**
* Retrieve the columnWidth based on the commands text size
*/
protected function getColumnWidth(array $commands): int
{
$widths = [];
foreach ($commands as $command) {
$widths[] = Helper::width($command);
}
return $widths ? max($widths) + 2 : 0;
}
/**
* List all events registered
*/
protected function list(): void
{
$eventServiceProvider = app()
->makeWith(
EventServiceProvider::class,
['app' => PKPContainer::getInstance()]
);
$events = [];
$rawEvents = $eventServiceProvider->getEvents();
foreach ($rawEvents as $event => $listeners) {
$events[] = [$event, implode(', ', $listeners)];
}
$this->table(['Event', 'Listeners'], $events);
}
/**
* Clean the Event cached file
*/
protected function clear(): void
{
EventServiceProvider::clearCache();
$this->getOutput()->success('Cache cleared!');
}
/**
* Create an Event cached file
*/
protected function cache(): void
{
EventServiceProvider::clearCache();
$eventServiceProvider = app()
->makeWith(
EventServiceProvider::class,
['app' => PKPContainer::getInstance()]
);
// Rebuild the cache
$eventServiceProvider->getEvents();
$this->getOutput()->success('Cache rebuilt!');
}
/**
* Parse and execute list event command
*/
public function execute()
{
if (!isset(self::AVAILABLE_OPTIONS[$this->option])) {
throw new CommandNotFoundException(
sprintf('Option "%s" does not exist.', $this->option),
array_keys(self::AVAILABLE_OPTIONS)
);
}
$this->{$this->option}();
}
}
try {
$tool = new commandEvents($argv ?? []);
$tool->execute();
} catch (Throwable $e) {
if ($e instanceof CommandNotFoundException) {
$output = new OutputStyle(
new StringInput(''),
new StreamOutput(fopen('php://stdout', 'w'))
);
$alternatives = $e->getAlternatives();
$message = count($alternatives) > 1 ? 'Did you mean one of those?' : 'Did you mean this?';
$message .= PHP_EOL . implode(PHP_EOL, $alternatives);
$output->block(
[$e->getMessage(), $message],
null,
'fg=white;bg=red',
' ',
true
);
exit;
}
throw $e;
}
+123
View File
@@ -0,0 +1,123 @@
<?php
/**
* @file tools/generateTestMGeoetrics.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class generateTestGeoMetrics
*
* @ingroup tools
*
* @brief Generate example Geo metric data.
*/
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
use APP\facades\Repo;
use APP\submission\Submission;
use Illuminate\Support\Facades\DB;
use Sokil\IsoCodes\IsoCodesFactory;
class generateTestGeoMetrics extends \PKP\cliTool\CommandLineTool
{
public $contextId;
public $dateStart;
public $dateEnd;
/**
* Constructor
*/
public function __construct($argv = [])
{
parent::__construct($argv);
if (sizeof($this->argv) < 3) {
$this->usage();
exit(1);
}
$this->contextId = (int) $argv[1];
$this->dateStart = $argv[2];
$this->dateEnd = $argv[3];
}
/**
* Print command usage information.
*/
public function usage()
{
echo "Generate fake usage data in the DB table metrics_submission_geo_monthly.\n"
. "Usage: {$this->scriptName} [contextId] [dateStart] [dateEnd]\n"
. "contextId The context to add metrics for.\n"
. "dateStart Add monthly metrics after this date. YYYY-MM-DD\n"
. "dateEnd Add monthly metrics before this date. YYYY-MM-DD\n";
}
/**
* Generate test metrics
*/
public function execute()
{
$isoCodes = app(IsoCodesFactory::class);
$countries = $isoCodes->getCountries()->toArray();
$subDivisions = $isoCodes->getSubdivisions();
$submissionIds = $this->getPublishedSubmissionIds();
$currentDate = new DateTime($this->dateStart);
$endDate = new DateTime($this->dateEnd);
$endDateTimeStamp = $endDate->getTimestamp();
$count = 0;
while ($currentDate->getTimestamp() < $endDateTimeStamp) {
foreach ($submissionIds as $submissionId) {
$randomCountryIndex = array_rand($countries);
$randomCountry = $countries[$randomCountryIndex];
$countryRegions = $subDivisions->getAllByCountryCode($randomCountry->getAlpha2());
$randomRegion = '';
if (!empty($countryRegions)) {
$randomSubDivisionIndex = array_rand($countryRegions);
$randomSubDivision = $countryRegions[$randomSubDivisionIndex];
$regionIsoCodeArray = explode('-', $randomSubDivision->getCode());
$randomRegion = $regionIsoCodeArray[1];
}
$randomMetric = random_int(1, 10);
DB::table('metrics_submission_geo_monthly')->insert([
'context_id' => $this->contextId,
'submission_id' => $submissionId,
'country' => $randomCountry->getAlpha2(),
'region' => $randomRegion,
'month' => $currentDate->format('Ym'),
'metric' => $randomMetric,
'metric_unique' => random_int(1, $randomMetric)
]);
$count++;
}
$currentDate->add(new DateInterval('P1M'));
}
echo $count . ' records added for ' . count($submissionIds) . " submissions.\n";
}
/**
* Get an array of all published submission IDs in the database
*/
public function getPublishedSubmissionIds()
{
return Repo::submission()
->getCollector()
->filterByContextIds([$this->contextId])
->filterByStatus([Submission::STATUS_PUBLISHED])
->getIds();
}
}
$tool = new generateTestGeoMetrics($argv ?? []);
$tool->execute();
+103
View File
@@ -0,0 +1,103 @@
<?php
/**
* @file tools/generateTestMetrics.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class generateTestMetrics
*
* @ingroup tools
*
* @brief Generate example metric data.
*/
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
use APP\core\Application;
use APP\facades\Repo;
use APP\submission\Submission;
use Illuminate\Support\Facades\DB;
class generateTestMetrics extends \PKP\cliTool\CommandLineTool
{
public $contextId;
public $dateStart;
public $dateEnd;
/**
* Constructor
*/
public function __construct($argv = [])
{
parent::__construct($argv);
if (sizeof($this->argv) < 3) {
$this->usage();
exit(1);
}
$this->contextId = (int) $argv[1];
$this->dateStart = $argv[2];
$this->dateEnd = $argv[3];
}
/**
* Print command usage information.
*/
public function usage()
{
echo "Generate fake usage data in the metrics table.\n"
. "Usage: {$this->scriptName} [contextId] [dateStart] [dateEnd]\n"
. "contextId The context to add metrics for.\n"
. "dateStart Add metrics after this date. YYYY-MM-DD\n"
. "dateEnd Add metrics after this date. YYYY-MM-DD\n";
}
/**
* Generate test metrics
*/
public function execute()
{
$submissionIds = $this->getPublishedSubmissionIds();
$currentDate = new DateTime($this->dateStart);
$endDate = new DateTime($this->dateEnd);
$endDateTimeStamp = $endDate->getTimestamp();
$count = 0;
while ($currentDate->getTimestamp() < $endDateTimeStamp) {
foreach ($submissionIds as $submissionId) {
DB::table('metrics_submission')->insert([
'load_id' => 'test_events_' . $currentDate->format('Ymd'),
'context_id' => $this->contextId,
'submission_id' => $submissionId,
'assoc_type' => Application::ASSOC_TYPE_SUBMISSION,
'date' => $currentDate->format('Y-m-d'),
'metric' => random_int(1, 10),
]);
$count++;
}
$currentDate->add(new DateInterval('P1D'));
}
echo $count . ' records added for ' . count($submissionIds) . " submissions.\n";
}
/**
* Get an array of all published submission IDs in the database
*/
public function getPublishedSubmissionIds()
{
return Repo::submission()
->getCollector()
->filterByContextIds([$this->contextId])
->filterByStatus([Submission::STATUS_PUBLISHED])
->getIds();
}
}
$tool = new generateTestMetrics($argv ?? []);
$tool->execute();
+133
View File
@@ -0,0 +1,133 @@
<?php
/**
* @file tools/getHooks.php
*
* Copyright (c) 2014-2020 Simon Fraser University
* Copyright (c) 2003-2020 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class getHooks
*
* @ingroup tools
*
* @brief CLI tool to compile documentation on hooks in markdown
*/
define('APP_ROOT', dirname(dirname(dirname(dirname(__FILE__)))));
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
class getHooks extends \PKP\cliTool\CommandLineTool
{
/** @var array Hooks */
public array $hooks = [];
/** @var array Directories to exclude from indexing. (.gitignore dirs will be added to this list) */
public array $excludePaths = [
'./.git',
'./cache',
'./cypress',
'./docs',
'./locale',
'./lib/pkp/.git',
'./lib/pkp/cypress',
'./lib/pkp/lib/vendor',
'./lib/pkp/locale',
'./lib/ui-library',
'./lib/pkp/tools/getHooks.php'
];
/**
* Parse and execute the import/export task.
*/
public function execute()
{
$this->loadIgnoreDirs(APP_ROOT . '/.gitignore');
$this->loadIgnoreDirs(APP_ROOT . '/lib/pkp/.gitignore', './lib/pkp/');
$this->processDir('./', function ($fileName) {
if (substr($fileName, -4) === '.php') {
$file = file_get_contents($fileName);
preg_match_all('/Hook\:\:call\(\s*\'([\d\D]*?)\'/', $file, $matches);
if (count($matches) > 1) {
foreach ($matches[1] as $hook) {
$this->hooks[] = $hook;
}
}
} elseif (substr($fileName, -4) !== '.tpl') {
$file = file_get_contents($fileName);
preg_match_all('/call_hook[\s]*name\=\"([\d\D]*?)\"/', $file, $matches);
if (count($matches) > 1) {
foreach ($matches[1] as $hook) {
$this->hooks[] = $hook;
}
}
}
});
sort($this->hooks);
echo join(',', $this->hooks);
}
/**
* Recursive function to find hook docblocks in a directory
*/
public function processDir(string $dir, callable $function)
{
foreach (new DirectoryIterator($dir) as $fileInfo) {
$isExcluded = false;
foreach ($this->excludePaths as $excludePath) {
if (strpos($fileInfo->getPathname(), $excludePath) === 0) {
$isExcluded = true;
break;
}
}
if ($isExcluded) {
continue;
}
if (!$fileInfo->isDot()) {
if ($fileInfo->isDir()) {
$this->processDir($fileInfo->getPathname(), $function);
} else {
call_user_func($function, $dir . '/' . $fileInfo->getFilename());
}
}
}
}
/**
* Load a .gitignore file and add to the excluded to directories
*
* @param string $path Path and filename for gitignore file
* @param string $prefix A prefix to give to each of the paths in the gitignore file
*/
public function loadIgnoreDirs(string $path, $prefix = '')
{
$gitIgnore = file_get_contents($path);
$gitIgnorePaths = explode("\n", $gitIgnore);
foreach ($gitIgnorePaths as $gitIgnorePath) {
if (!strlen(trim($gitIgnorePath))) {
continue;
} elseif (substr($gitIgnorePath, 0, 1) === '#') {
continue;
} elseif (substr($gitIgnorePath, 0, 1) === '/') {
$gitIgnorePath = '.' . $gitIgnorePath;
} elseif (strpos($gitIgnorePath, '.') === 0) {
if (strpos($gitIgnorePath, '/') !== 1) {
$gitIgnorePath = '';
}
} elseif (substr($gitIgnorePath, 0, 2) !== './') {
$gitIgnorePath = './' . $gitIgnorePath;
}
if ($gitIgnorePath) {
$this->excludePaths[] = $prefix . $gitIgnorePath;
}
}
}
}
$tool = new getHooks($argv ?? []);
$tool->execute();
+79
View File
@@ -0,0 +1,79 @@
<?php
/**
* @file tools/installEmailTemplate.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class installEmailTemplate
*
* @ingroup tools
*
* @brief CLI tool to install email templates from PO files into the database.
*/
use PKP\cliTool\CommandLineTool;
use PKP\facades\Repo;
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
class installEmailTemplates extends CommandLineTool
{
/** @var string The email key of the email template to install. */
public $_emailKey;
/** @var string The list of locales in which to install the template. */
public $_locales;
/**
* Constructor.
*
* @param array $argv command-line arguments
*/
public function __construct($argv = [])
{
parent::__construct($argv);
$this->_emailKey = array_shift($this->argv);
$this->_locales = array_shift($this->argv);
if ($this->_emailKey === null) {
$this->usage();
exit;
}
}
/**
* Print command usage information.
*/
public function usage()
{
echo "Command-line tool for installing email templates.\n"
. "Usage:\n"
. "\t{$this->scriptName} emailKey aa_BB[,cc_DD,...] [path/to/emails.po]\n"
. "\t\temailKey: The email key of the email to install, e.g. ANNOUNCEMENT\n"
. "\t\taa_BB[,cc_DD,...]: The optional comma-separated list of locales to install. If none provided will be determined by site's installed locales\n";
}
/**
* Execute upgrade task
*/
public function execute()
{
// Load the necessary locale data
$locales = explode(',', $this->_locales ?? '');
// Install to the database
Repo::emailTemplate()->dao->installEmailTemplates(
Repo::emailTemplate()->dao->getMainEmailTemplatesFilename(),
$locales,
$this->_emailKey
);
}
}
$tool = new installEmailTemplates($argv ?? []);
$tool->execute();
+111
View File
@@ -0,0 +1,111 @@
<?php
/**
* @file tools/installPluginVersionTool.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class InstallPluginVersionTool
*
* @ingroup tools
*
* @brief CLI tool for installing a plugin version descriptor.
*/
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
use APP\core\Application;
use APP\install\Upgrade;
use PKP\db\DAORegistry;
use PKP\plugins\PluginRegistry;
use PKP\site\VersionCheck;
class InstallPluginVersionTool extends \PKP\cliTool\CommandLineTool
{
/** @var string Path to descriptor file to install */
private $_descriptor;
/**
* Constructor.
*
* @param array $argv command-line arguments
*/
public function __construct($argv = [])
{
parent::__construct($argv);
if (!isset($this->argv[0]) || !file_exists($this->argv[0])) {
$this->usage();
exit(1);
}
$this->_descriptor = $this->argv[0];
}
/**
* Print command usage information.
*/
public function usage()
{
echo "Install plugin version tool\n"
. "Usage: {$this->scriptName} path/to/version.xml\n";
}
/**
* Execute the specified command.
*/
public function execute()
{
$versionInfo = VersionCheck::parseVersionXML($this->_descriptor);
$pluginVersion = $versionInfo['version'];
$productType = $pluginVersion->getProductType();
if (!preg_match('/^plugins\.(.+)$/', $productType, $matches) || !in_array($matches[1], Application::get()->getPluginCategories())) {
error_log("Invalid type \"{$productType}\".");
return false;
}
/** @var VersionDAO */
$versionDao = DAORegistry::getDAO('VersionDAO');
$versionDao->insertVersion($pluginVersion, true);
$pluginPath = dirname($this->_descriptor);
if (file_exists($wrapperName = "{$pluginPath}/index.php")) {
// Old-style (non-FQCN) plugin class name
$plugin = include("{$pluginPath}/index.php");
} else {
// Expect a wrapper-less plugin in a namespace.
$fqcn = '\\APP\\' . strtr($pluginVersion->getProductType(), '.', '\\') . '\\' . $pluginVersion->getProduct() . '\\' . $pluginVersion->getProductClassName();
$plugin = new $fqcn();
}
if ($plugin && is_object($plugin)) {
PluginRegistry::register($matches[1], $plugin, $pluginPath);
}
$plugin = PluginRegistry::getPlugin($matches[1], $plugin->getName());
$installer = new Upgrade([]);
$result = true;
$param = [&$installer, &$result];
if ($plugin->getInstallMigration()) {
$plugin->updateSchema('Installer::postInstall', $param);
}
if ($plugin->getInstallSitePluginSettingsFile()) {
$plugin->installSiteSettings('Installer::postInstall', $param);
}
if ($plugin->getInstallEmailTemplatesFile()) {
$plugin->installEmailTemplates('Installer::postInstall', $param);
}
if ($plugin->getInstallEmailTemplateDataFile()) {
$plugin->installEmailTemplateData('Installer::postInstall', $param);
}
$plugin->installFilters('Installer::postInstall', $param);
return $result;
}
}
Application::upgrade();
$tool = new InstallPluginVersionTool($argv ?? []);
$tool->execute();
+720
View File
@@ -0,0 +1,720 @@
<?php
declare(strict_types=1);
/**
* @file tools/jobs.php
*
* Copyright (c) 2014-2022 Simon Fraser University
* Copyright (c) 2003-2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class commandJobs
*
* @ingroup tools
*
* @brief CLI tool to list, iterate and purge queued jobs on database
*/
namespace PKP\tools;
use APP\core\Application;
use APP\facades\Repo;
use Carbon\Carbon;
use Illuminate\Console\Concerns\InteractsWithIO;
use Illuminate\Console\OutputStyle;
use Illuminate\Contracts\Queue\Job;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Queue\Events\JobFailed;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
use PKP\cliTool\CommandLineTool;
use PKP\config\Config;
use PKP\job\models\Job as PKPJobModel;
use PKP\jobs\testJobs\TestJobFailure;
use PKP\jobs\testJobs\TestJobSuccess;
use PKP\queue\WorkerConfiguration;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Exception\InvalidArgumentException as CommandInvalidArgumentException;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Helper\TableCell;
use Symfony\Component\Console\Helper\TableCellStyle;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\StreamOutput;
use Throwable;
define('APP_ROOT', dirname(__FILE__, 4));
require_once APP_ROOT . '/tools/bootstrap.php';
class commandInterface
{
use InteractsWithIO;
public function __construct()
{
$output = new OutputStyle(
new StringInput(''),
new StreamOutput(fopen('php://stdout', 'w'))
);
$this->setOutput($output);
}
public function errorBlock(array $messages = [], ?string $title = null): void
{
$this->getOutput()->block(
$messages,
$title,
'fg=white;bg=red',
' ',
true
);
}
}
class commandJobs extends CommandLineTool
{
protected const AVAILABLE_OPTIONS = [
'list' => 'admin.cli.tool.jobs.available.options.list.description',
'purge' => 'admin.cli.tool.jobs.available.options.purge.description',
'test' => 'admin.cli.tool.jobs.available.options.test.description',
'total' => 'admin.cli.tool.jobs.available.options.total.description',
'help' => 'admin.cli.tool.jobs.available.options.help.description',
'run' => 'admin.cli.tool.jobs.available.options.run.description',
'work' => 'admin.cli.tool.jobs.available.options.work.description',
'failed' => 'admin.cli.tool.jobs.available.options.failed.description',
'restart' => 'admin.cli.tool.jobs.available.options.restart.description',
'usage' => 'admin.cli.tool.jobs.available.options.usage.description',
];
protected const CURRENT_PAGE = 'current';
protected const PREVIOUS_PAGE = 'previous';
protected const NEXT_PAGE = 'next';
/**
* @var null|string Which option will be call?
*/
protected $option = null;
/**
* @var null|array Parameters and arguments from CLI
*/
protected $parameterList = null;
/**
* CLI interface, this object should extends InteractsWithIO
*/
protected $commandInterface = null;
/**
* Constructor
*/
public function __construct($argv = [])
{
parent::__construct($argv);
array_shift($argv);
$this->setParameterList($argv);
if (!isset($this->getParameterList()[0])) {
throw new CommandNotFoundException(
__('admin.cli.tool.jobs.empty.option'),
array_keys(self::AVAILABLE_OPTIONS)
);
}
$this->option = $this->getParameterList()[0];
$this->setCommandInterface(new commandInterface());
}
public function setCommandInterface(commandInterface $commandInterface): self
{
$this->commandInterface = $commandInterface;
return $this;
}
public function getCommandInterface(): commandInterface
{
return $this->commandInterface;
}
/**
* Save the parameter list passed on CLI
*
* @param array $items Array with parameters and arguments passed on CLI
*
*/
public function setParameterList(array $items): self
{
$parameters = [];
foreach ($items as $param) {
if (strpos($param, '=')) {
[$key, $value] = explode('=', ltrim($param, '-'));
$parameters[$key] = $value;
continue;
}
$parameters[] = $param;
}
$this->parameterList = $parameters;
return $this;
}
/**
* Get the parameter list passed on CLI
*
*/
public function getParameterList(): ?array
{
return $this->parameterList;
}
/**
* Get the value of a specific parameter
*
* @param mixed $default
*
*/
protected function getParameterValue(string $parameter, mixed $default = null): mixed
{
if (!isset($this->getParameterList()[$parameter])) {
return $default;
}
return $this->getParameterList()[$parameter];
}
/**
* Print command usage information.
*/
public function usage()
{
$this->getCommandInterface()->line('<comment>' . __('admin.cli.tool.usage.title') . '</comment>');
$this->getCommandInterface()->line(__('admin.cli.tool.usage.parameters') . PHP_EOL);
$this->getCommandInterface()->line('<comment>' . __('admin.cli.tool.available.commands', ['namespace' => 'jobs']) . '</comment>');
$this->printUsage(self::AVAILABLE_OPTIONS);
}
/**
* Alias for usage command
*/
public function help(): void
{
$this->usage();
}
/**
* Retrieve the columnWidth based on the commands text size
*/
protected function getColumnWidth(array $commands): int
{
$widths = [];
foreach ($commands as $command) {
$widths[] = Helper::width($command);
}
return $widths ? max($widths) + 2 : 0;
}
/**
* Failed jobs list/redispatch/remove
*/
protected function failed(): void
{
$parameterList = $this->getParameterList();
if (in_array('--redispatch', $parameterList) || ($jobIds = $this->getParameterValue('redispatch'))) {
$jobsCount = Repo::failedJob()->redispatchToQueue(
$this->getParameterValue('queue'),
collect(explode(',', $jobIds ?? ''))
->filter()
->map(fn ($item) => (int)$item)
->toArray()
);
$this->getCommandInterface()->getOutput()->success(__('admin.cli.tool.jobs.failed.redispatch.successful', ['jobsCount' => $jobsCount]));
return;
}
if (in_array('--clear', $parameterList) || ($jobIds = $this->getParameterValue('clear'))) {
$jobsCount = Repo::failedJob()->deleteJobs(
$this->getParameterValue('queue'),
collect(explode(',', $jobIds ?? ''))
->filter()
->map(fn ($item) => (int)$item)
->toArray()
);
$this->getCommandInterface()->getOutput()->success(__('admin.cli.tool.jobs.failed.clear.successful', ['jobsCount' => $jobsCount]));
return;
}
array_push($this->parameterList, '--failed');
$this->list();
}
/**
* Signal the queue worker to quit gracefully
*/
protected function restart(): void
{
$cache = app()->get("cache.store"); /** @var \Illuminate\Contracts\Cache\Repository $cache */
$cache->forever('illuminate:queue:restart', Carbon::now()->getTimestamp());
$this
->getCommandInterface()
->getOutput()
->info(__('admin.cli.tool.jobs.available.options.restart.confirm'));
}
/**
* List all queued jobs
*/
protected function list(): void
{
$perPage = $this->getParameterValue('perPage', '10');
$page = $this->getParameterValue('page', '1');
$parameterList = $this->getparameterList();
$repository = in_array('--failed', $parameterList) ? Repo::failedJob() : Repo::job();
$data = $repository
->setOutputFormat($repository::OUTPUT_CLI)
->perPage((int) $perPage)
->setPage((int) $page)
->showJobs();
$this->total();
$this->getCommandInterface()->table(
$this->getListTableFormat(),
$data
->map(fn(JsonResource $job) => $job->toArray(app('request')))
->toArray()
);
$pagination = [
'pagination' => [
self::CURRENT_PAGE => $data->currentPage(),
self::PREVIOUS_PAGE => ($data->currentPage() - 1) > 0 ? $data->currentPage() - 1 : 1,
self::NEXT_PAGE => $data->currentPage(),
],
];
if ($data->hasMorePages()) {
$pagination['pagination'][self::NEXT_PAGE] = $data->currentPage() + 1;
}
$this->getCommandInterface()
->table(
[
[
new TableCell(
__('admin.cli.tool.jobs.pagination'),
[
'colspan' => 3,
'style' => new TableCellStyle(['align' => 'center'])
]
)
],
[
__('admin.cli.tool.jobs.pagination.current'),
__('admin.cli.tool.jobs.pagination.previous'),
__('admin.cli.tool.jobs.pagination.next'),
]
],
$pagination
);
}
/**
* Get table format for list view
*/
protected function getListTableFormat(): array
{
$listForFailedJobs = in_array('--failed', $this->getParameterList());
return [
[
new TableCell(
$listForFailedJobs
? __('admin.cli.tool.jobs.queued.jobs.failed.title')
: __('admin.cli.tool.jobs.queued.jobs.title'),
[
'colspan' => $listForFailedJobs ? 6 : 7,
'style' => new TableCellStyle(['align' => 'center'])
]
)
],
array_merge([
__('admin.cli.tool.jobs.queued.jobs.fields.id'),
__('admin.cli.tool.jobs.queued.jobs.fields.queue'),
__('admin.cli.tool.jobs.queued.jobs.fields.job.display.name'),
], $listForFailedJobs ? [
__('admin.cli.tool.jobs.queued.jobs.fields.connection'),
__('admin.cli.tool.jobs.queued.jobs.fields.failed.at'),
__('admin.cli.tool.jobs.queued.jobs.fields.exception'),
] : [
__('admin.cli.tool.jobs.queued.jobs.fields.attempts'),
__('admin.cli.tool.jobs.queued.jobs.fields.reserved.at'),
__('admin.cli.tool.jobs.queued.jobs.fields.available.at'),
__('admin.cli.tool.jobs.queued.jobs.fields.created.at')
])
];
}
/**
* Run daemon worker process to continue handle jobs
*/
protected function work(): void
{
$parameterList = $this->getParameterList();
if (in_array('--help', $parameterList)) {
$this->workerOptionsHelp();
return;
}
if (Application::isUnderMaintenance()) {
$this->getCommandInterface()->getOutput()->error(__('admin.cli.tool.jobs.maintenance.message'));
return;
}
if (Config::getVar('general', 'sandbox', false)) {
$this->getCommandInterface()->getOutput()->error(__('admin.cli.tool.jobs.sandbox.message'));
error_log(__('admin.cli.tool.jobs.sandbox.message'));
return;
}
$connection = $parameterList['connection'] ?? Config::getVar('queues', 'default_connection', 'database');
$queue = $parameterList['queue'] ?? Config::getVar('queues', 'default_queue', 'queue');
if (in_array('--test', $parameterList)) {
$queue = PKPJobModel::TESTING_QUEUE;
}
$this->listenForEvents();
app('pkpJobQueue')->runJobsViaDaemon(
$connection,
$queue,
$this->gatherWorkerOptions($parameterList)
);
}
/**
* Dispatch jobs into the queue
*/
protected function run(): void
{
if (Application::isUnderMaintenance()) {
$this->getCommandInterface()->getOutput()->error(__('admin.cli.tool.jobs.maintenance.message'));
return;
}
if (Config::getVar('general', 'sandbox', false)) {
$this->getCommandInterface()->getOutput()->error(__('admin.cli.tool.jobs.sandbox.message'));
error_log(__('admin.cli.tool.jobs.sandbox.message'));
return;
}
$parameterList = $this->getParameterList();
$queue = $parameterList['queue'] ?? Config::getVar('queues', 'default_queue', 'queue');
if (in_array('--test', $parameterList)) {
$queue = PKPJobModel::TESTING_QUEUE;
}
$jobQueue = app('pkpJobQueue');
if ($queue && is_string($queue)) {
$jobQueue = $jobQueue->forQueue($queue);
}
$jobBuilder = $jobQueue->getJobModelBuilder();
if (($jobCount = $jobBuilder->count()) <= 0) {
$this->getCommandInterface()->getOutput()->info(
__(
'admin.cli.tool.jobs.available.options.run.empty.description',
['queueName' => $queue,]
)
);
return;
}
$this->listenForEvents();
while ($jobBuilder->count()) {
$jobQueue->runJobInQueue();
if (in_array('--once', $parameterList)) {
$jobCount = 1;
break;
}
}
$this->getCommandInterface()->getOutput()->success(
__(
'admin.cli.tool.jobs.available.options.run.completed.description',
['jobCount' => $jobCount, 'queueName' => $queue,]
)
);
}
/**
* Purge queued jobs
*/
protected function purge(): void
{
if (!isset($this->getParameterList()['queue']) && !isset($this->getParameterList()[1])) {
throw new CommandInvalidArgumentException(__('admin.cli.tool.jobs.purge.without.id'));
}
$parameterList = $this->getParameterList();
if (in_array('--all', $parameterList) || ($queue = $this->getParameterValue('queue'))) {
if (!Repo::job()->deleteJobs($queue ?? null)) {
$this->getCommandInterface()->getOutput()->warning(__('admin.cli.tool.jobs.purge.impossible.to.purge.empty'));
return;
}
$this->getCommandInterface()->getOutput()->success(__('admin.cli.tool.jobs.purge.successful.all'));
return;
}
$deleted = Repo::job()->delete((int) $this->getParameterList()[1]);
if (!$deleted) {
throw new CommandInvalidArgumentException(__('admin.cli.tool.jobs.purge.invalid.id'));
}
$this->getCommandInterface()->getOutput()->success(__('admin.cli.tool.jobs.purge.successful'));
}
/**
* Create a test queued job
*/
protected function test(): void
{
$queue = PKPJobModel::TESTING_QUEUE;
$runnableJob = $this->getParameterList()['only'] ?? null;
if ($runnableJob && !in_array($runnableJob, ['failed', 'success'])) {
throw new CommandInvalidArgumentException(__('admin.cli.tool.jobs.test.invalid.option'));
}
if (!$runnableJob || $runnableJob === 'failed') {
dispatch(new TestJobFailure());
$this->getCommandInterface()->getOutput()->success(__('admin.cli.tool.jobs.test.job.failed.dispatch.message', ['queueName' => $queue]));
}
if (!$runnableJob || $runnableJob === 'success') {
dispatch(new TestJobSuccess());
$this->getCommandInterface()->getOutput()->success(__('admin.cli.tool.jobs.test.job.success.dispatch.message', ['queueName' => $queue]));
}
}
/**
* Gather worker daemon options
*
*/
protected function gatherWorkerOptions(array $parameters = []): array
{
$workerConfig = new WorkerConfiguration();
return [
'name' => $this->getParameterValue('name', $workerConfig->getName()),
'backoff' => $this->getParameterValue('backoff', $workerConfig->getBackoff()),
'memory' => $this->getParameterValue('memory', $workerConfig->getMemory()),
'timeout' => $this->getParameterValue('timeout', $workerConfig->getTimeout()),
'sleep' => $this->getParameterValue('sleep', $workerConfig->getSleep()),
'maxTries' => $this->getParameterValue('tries', $workerConfig->getMaxTries()),
'force' => $this->getParameterValue('force', in_array('force', $parameters) ? true : $workerConfig->getForce()),
'stopWhenEmpty' => $this->getParameterValue('stop-when-empty', in_array('stop-when-empty', $parameters) ? true : $workerConfig->getStopWhenEmpty()),
'maxJobs' => $this->getParameterValue('max-jobs', $workerConfig->getMaxJobs()),
'maxTime' => $this->getParameterValue('max-time', $workerConfig->getMaxTime()),
'rest' => $this->getParameterValue('rest', $workerConfig->getRest()),
];
}
/**
* Listen for the queue events in order to update the console output.
*
*/
protected function listenForEvents(): void
{
$events = app()['events'];
$events->listen(JobProcessing::class, function ($event) {
$this->writeOutput($event->job, 'starting');
});
$events->listen(JobProcessed::class, function ($event) {
$this->writeOutput($event->job, 'success');
});
$events->listen(JobFailed::class, function ($event) {
$this->writeOutput($event->job, 'failed');
});
}
/**
* Write the status output for the queue worker.
*
* @param string $status
*
*/
protected function writeOutput(Job $job, $status): void
{
match ($status) {
'starting' => $this->writeStatus($job, 'Processing', 'comment'),
'success' => $this->writeStatus($job, 'Processed', 'info'),
'failed' => $this->writeStatus($job, 'Failed', 'error'),
};
}
/**
* Format the status output for the queue worker.
*
* @param string $status
* @param string $type
*/
protected function writeStatus(Job $job, $status, $type): void
{
$this->getCommandInterface()->getOutput()->writeln(sprintf(
"<{$type}>[%s][%s] %s</{$type}> %s",
Carbon::now()->format('Y-m-d H:i:s'),
$job->getJobId(),
str_pad("{$status}:", 11),
$job->resolveName()
));
}
/**
* Print work command options information.
*/
protected function workerOptionsHelp(): void
{
$this->getCommandInterface()->line('<comment>' . __('admin.cli.tool.jobs.work.options.title') . '</comment>');
$this->getCommandInterface()->line(__('admin.cli.tool.jobs.work.options.usage') . PHP_EOL);
$this->getCommandInterface()->line('<comment>' . __('admin.cli.tool.jobs.work.options.description') . '</comment>');
$workerConfig = new WorkerConfiguration();
$options = [
'--connection[=CONNECTION]' => __('admin.cli.tool.jobs.work.option.connection.description', ['default' => Config::getVar('queue', 'default_connection', 'database')]),
'--queue[=QUEUE]' => __('admin.cli.tool.jobs.work.option.queue.description', ['default' => Config::getVar('queue', 'default_queue', 'queue')]),
'--name[=NAME]' => __('admin.cli.tool.jobs.work.option.name.description', ['default' => $workerConfig->getName()]),
'--backoff[=BACKOFF]' => __('admin.cli.tool.jobs.work.option.backoff.description', ['default' => $workerConfig->getBackoff()]),
'--memory[=MEMORY]' => __('admin.cli.tool.jobs.work.option.memory.description', ['default' => $workerConfig->getMemory()]),
'--timeout[=TIMEOUT]' => __('admin.cli.tool.jobs.work.option.timeout.description', ['default' => $workerConfig->getTimeout()]),
'--sleep[=SLEEP]' => __('admin.cli.tool.jobs.work.option.sleep.description', ['default' => $workerConfig->getSleep()]),
'--tries[=TRIES]' => __('admin.cli.tool.jobs.work.option.tries.description', ['default' => $workerConfig->getMaxTries()]),
'--force' => __('admin.cli.tool.jobs.work.option.force.description', ['default' => $workerConfig->getForce() ? 'true' : 'false']),
'--stop-when-empty' => __('admin.cli.tool.jobs.work.option.stopWhenEmpty.description', ['default' => $workerConfig->getStopWhenEmpty() ? 'true' : 'false']),
'--max-jobs[=MAX-JOBS]' => __('admin.cli.tool.jobs.work.option.maxJobs.description', ['default' => $workerConfig->getMaxJobs()]),
'--max-time[=MAX-TIME]' => __('admin.cli.tool.jobs.work.option.maxTime.description', ['default' => $workerConfig->getMaxTime()]),
'--rest[=REST]' => __('admin.cli.tool.jobs.work.option.rest.description', ['default' => $workerConfig->getRest()]),
'--test' => __('admin.cli.tool.jobs.work.option.test.description'),
];
$this->printUsage($options, false);
}
/**
* Print given options in a pretty way.
*/
protected function printUsage(array $options, bool $shouldTranslate = true): void
{
$width = $this->getColumnWidth(array_keys($options));
foreach ($options as $commandName => $description) {
$spacingWidth = $width - Helper::width($commandName);
$this->getCommandInterface()->line(
sprintf(
' <info>%s</info>%s%s',
$commandName,
str_repeat(' ', $spacingWidth),
$shouldTranslate ? __($description) : $description
)
);
}
}
/**
* Display the queued/failed jobs quantity
*/
protected function total(): void
{
$parameterList = $this->getParameterList();
$total = in_array('--failed', $parameterList)
? Repo::failedJob()->total()
: Repo::job()->total();
$outputInterface = $this->getCommandInterface()->getOutput();
if (in_array('--failed', $parameterList)) {
$method = $total > 0 ? 'error' : 'success';
$outputInterface->{$method}(__('admin.cli.tool.jobs.total.failed.jobs', ['total' => $total]));
return;
}
$outputInterface->warning(__('admin.cli.tool.jobs.total.jobs', ['total' => $total]));
}
/**
* Parse and execute the command
*/
public function execute()
{
if (!isset(self::AVAILABLE_OPTIONS[$this->option])) {
throw new CommandNotFoundException(
__('admin.cli.tool.jobs.option.doesnt.exists', ['option' => $this->option]),
array_keys(self::AVAILABLE_OPTIONS)
);
}
$this->{$this->option}();
}
}
try {
$tool = new commandJobs($argv ?? []);
$tool->execute();
} catch (Throwable $e) {
$output = new commandInterface();
if ($e instanceof CommandInvalidArgumentException) {
$output->errorBlock([$e->getMessage()]);
return;
}
if ($e instanceof CommandNotFoundException) {
$alternatives = $e->getAlternatives();
$message = __('admin.cli.tool.jobs.mean.those') . PHP_EOL . implode(PHP_EOL, $alternatives);
$output->errorBlock([$e->getMessage(), $message]);
return;
}
throw $e;
}
+1329
View File
File diff suppressed because it is too large Load Diff
+89
View File
@@ -0,0 +1,89 @@
<?php
/**
* @file tools/migration.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class migrationTool
*
* @ingroup tools
*/
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
class migrationTool extends \PKP\cliTool\CommandLineTool
{
/** @var string Name (fully qualified) of migration class */
protected $class;
/** @var string "up" or "down" */
protected $direction;
/**
* Constructor
*/
public function __construct($argv = [])
{
parent::__construct($argv);
array_shift($argv); // Shift the tool name off the top
$this->class = array_shift($argv);
$this->direction = array_shift($argv);
// The source file/directory must be specified and exist.
if (empty($this->class)) {
$this->usage();
exit(2);
}
// The migration direction.
if (!in_array($this->direction, ['up', 'down'])) {
$this->usage();
exit(3);
}
}
/**
* Print command usage information.
*/
public function usage()
{
echo "Run a migration.\n\n"
. "Usage: {$this->scriptName} \\fully\\qualified\\migration\\Name [up|down]\n\n";
}
/**
* Log install message to stdout.
*
* @param string $message
*/
public function log($message)
{
printf("[%s]\n", $message);
}
/**
* Execute the specified migration.
*/
public function execute()
{
$upgrade = new \APP\install\Upgrade([]);
$upgrade->setLogger($this);
$migration = new $this->class($upgrade, []);
try {
$direction = $this->direction;
$migration->$direction();
} catch (Exception $e) {
echo 'ERROR: ' . $e->getMessage() . "\n\n";
exit(2);
}
}
}
$tool = new migrationTool($argv ?? []);
$tool->execute();
+172
View File
@@ -0,0 +1,172 @@
<?php
/**
* @file tools/moveLocaleKeysToLib.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class MoveLocaleKeysToLib
*
* @ingroup tools
*
* @brief Move a locale key from an application's locale files to the pkp-lib locale files.
*/
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
class MoveLocaleKeysToLib extends \PKP\cliTool\CommandLineTool
{
/** @var string The string to match in a msgid */
public $msgidMatch = '';
/** @var string The application file to search for keys */
public $sourceFile = '';
/** @var string The pkp-lib file to move the keys to */
public $targetFile = '';
/** @var bool Whether to move locale keys from lib/pkp to the app */
public $reverse = false;
/**
* Constructor
*/
public function __construct($argv = [])
{
parent::__construct($argv);
// discard first argument: script name
array_shift($argv);
if (sizeof($this->argv) < 3) {
$this->usage();
exit(1);
}
if ($argv[0] === '-r') {
$this->reverse = true;
array_shift($argv);
}
$this->msgidMatch = array_shift($argv);
$this->sourceFile = array_shift($argv);
$this->targetFile = array_shift($argv);
}
/**
* Print command usage information.
*/
public function usage()
{
echo "\nMove matching locale keys from one file to another.\n\n"
. "All matching locale keys will be moved from the source file to the target file. This will\n"
. "effect all locales.\n\n"
. " Usage: php {$this->scriptName} (options) [match] [sourceFile] [targetFile]\n\n"
. " (options) Optional flags:\n"
. " -r Move locale keys from lib/pkp into the app.\n"
. " [match] The string to match in the locale key's msgid, Supports partial\n"
. " matches from start of msgid. `example.key.` will match `msgid \"example.key.anything\"`.\n\n"
. " [sourceFile] The file to look for keys to move, such as `emails.po`.\n\n"
. " [targetFile] The file to move keys to, such as `emails.po`. Usually the same as `sourceFile`.\n\n";
}
/**
* Remove the requested locale key
*/
public function execute()
{
$localeDirs = scandir('locale');
if (!$localeDirs) {
$this->output('Locale directories could not be found. Run this from the root directory of the application.');
exit;
}
$localeDirs = array_filter($localeDirs, function ($localeDir) {
return $localeDir !== '.' && $localeDir !== '..';
});
$fromDir = $this->reverse
? 'lib/pkp/locale/'
: 'locale/';
$toDir = $this->reverse
? 'locale/'
: 'lib/pkp/locale/';
foreach (array_values($localeDirs) as $localeDir) {
$localeSourceFile = $fromDir . $localeDir . '/' . $this->sourceFile;
$localeTargetFile = $toDir . $localeDir . '/' . $this->targetFile;
if (!file_exists($localeSourceFile)) {
$this->output('No file exists at ' . $localeSourceFile . ' to move locale keys from. Skipping this locale.');
continue;
}
// Create a new file if no file exists at the target and add the weblate header
if (!file_exists($localeTargetFile)) {
if (!file_exists(dirname($localeTargetFile))) {
mkdir(dirname($localeTargetFile));
}
$lines = explode("\n", file_get_contents($localeSourceFile));
$headerLines = [];
$endOfHeader = '"X-Generator';
foreach ($lines as $line) {
$headerLines[] = $line;
if (substr($line, 0, strlen($endOfHeader)) == $endOfHeader) {
break;
}
}
$headerLines[] = "\n";
file_put_contents($localeTargetFile, join("\n", $headerLines));
$this->output('New file created at ' . $localeTargetFile . '.');
}
$changedSourceLines = [];
$newTargetLines = [];
$isMovingLine = false;
$lines = explode("\n", file_get_contents($localeSourceFile));
foreach ($lines as $i => $line) {
if ($line === "msgid \"{$this->msgidMatch}\"") {
$isMovingLine = true;
} elseif (trim($line) === '#, fuzzy' || substr($line, 0, 5) === 'msgid') {
$isMovingLine = false;
}
if ($isMovingLine) {
// Check for fuzzy flag and make sure it's moved over
if ($lines[$i - 1] === '#, fuzzy') {
$newTargetLines[] = $lines[$i - 1];
array_pop($changedSourceLines);
}
$newTargetLines[] = $line;
} else {
$changedSourceLines[] = $line;
}
}
if (count($newTargetLines)) {
file_put_contents($localeTargetFile, "\n" . join("\n", $newTargetLines), FILE_APPEND);
$this->output(count($newTargetLines) . ' lines added to ' . $localeTargetFile . '.');
}
$linesToRemove = count($lines) - count($changedSourceLines);
if (count($lines) !== count($changedSourceLines)) {
file_put_contents($localeSourceFile, join("\n", $changedSourceLines));
$this->output($linesToRemove . ' lines removed from ' . $localeSourceFile . '.');
}
if (!count($newTargetLines) && !$linesToRemove) {
$this->output('No changes made to ' . $localeSourceFile . '.');
}
}
}
protected function output(string $string)
{
echo "\n" . $string;
}
}
$tool = new MoveLocaleKeysToLib($argv ?? []);
$tool->execute();
+120
View File
@@ -0,0 +1,120 @@
<?php
/**
* @file tools/parseCitations.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class CitationsParsingTool
*
* @ingroup tools
*
* @brief CLI tool to parse existing citations
*/
use APP\core\Application;
use APP\facades\Repo;
use PKP\db\DAORegistry;
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
class CitationsParsingTool extends \PKP\cliTool\CommandLineTool
{
public $parameters;
/**
* Constructor.
*
* @param array $argv command-line arguments
*/
public function __construct($argv = [])
{
parent::__construct($argv);
if (!sizeof($this->argv)) {
$this->usage();
exit(1);
}
$this->parameters = $this->argv;
}
/**
* Print command usage information.
*/
public function usage()
{
echo "Parse and save submission(s) citations.\n"
. "Usage:\n"
. "{$this->scriptName} all\n"
. "{$this->scriptName} context context_id [...]\n"
. "{$this->scriptName} submission submission_id [...]\n";
}
/**
* Parse citations
*/
public function execute()
{
$citationDao = DAORegistry::getDAO('CitationDAO');
$contextDao = Application::getContextDAO();
switch (array_shift($this->parameters)) {
case 'all':
$contexts = $contextDao->getAll();
while ($context = $contexts->next()) {
$submissions = Repo::submission()->getCollector()->filterByContextIds([$context->getId()])->getMany();
foreach ($submissions as $submission) {
$this->_parseSubmission($submission);
}
}
break;
case 'context':
foreach ($this->parameters as $contextId) {
$context = $contextDao->getById($contextId);
if (!isset($context)) {
printf("Error: Skipping {$contextId}. Unknown context.\n");
continue;
}
$submissions = Repo::submission()->getCollector()->filterByContextIds([$context->getId()])->getMany();
foreach ($submissions as $submission) {
$this->_parseSubmission($submission);
}
}
break;
case 'submission':
foreach ($this->parameters as $submissionId) {
$submission = Repo::submission()->get($submissionId);
if (!isset($submission)) {
printf("Error: Skipping {$submissionId}. Unknown submission.\n");
continue;
}
$this->_parseSubmission($submission);
}
break;
default:
$this->usage();
break;
}
}
/**
* Parse the citations of one submission
*
* @param Submission $submission
*/
private function _parseSubmission($submission)
{
/** @var CitationDAO */
$citationDao = DAORegistry::getDAO('CitationDAO');
foreach ($submission->getData('publications') as $publication) {
if (!empty($publication->getData('citationsRaw'))) {
$citationDao->importCitations($publication->getId(), $publication->getData('citationsRaw'));
}
}
}
}
$tool = new CitationsParsingTool($argv ?? []);
$tool->execute();
+181
View File
@@ -0,0 +1,181 @@
<?php
/**
* @file tools/plugins.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PluginsTool
*
* @ingroup tools
*
* @brief CLI tool to get information about installed/available plugins
*/
use APP\core\Application;
use PKP\db\DAORegistry;
use PKP\plugins\PluginGalleryDAO;
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
class PluginsTool extends \PKP\cliTool\CommandLineTool
{
/**
* Constructor.
*
* @param array $argv command-line arguments
*/
public function __construct($argv = [])
{
parent::__construct($argv);
if (!isset($this->argv[0]) || !$this->validateArgs()) {
$this->usage();
exit(1);
}
}
/**
* Validate arguments
*/
public function validateArgs()
{
switch ($this->argv[0]) {
case 'list':
if (count($this->argv) > 2) {
return false;
}
return true;
case 'info':
if (count($this->argv) != 2) {
return false;
}
if (count(explode('/', $this->argv[1])) != 2) {
echo "\n\033[0;31m✘ The plugin path `" . $this->argv[1] . "` is not valid. It should be in the following format: generic/pln\033[0m\n\n";
return false;
}
return true;
default:
return false;
}
}
/**
* Print command usage information.
*/
public function usage()
{
echo "Plugin Gallery tool\n"
. "Usage: {$this->scriptName} action [arguments]\n"
. " Actions:\n"
. "\tlist [search]: show latest compatible plugin(s). Optional \"search\" text against plugin class\n"
. "\tinfo path: show detail for plugin identified by \"path\", such as generic/pln\n";
}
/**
* Execute the specified command.
*/
public function execute()
{
$result = false;
/** @var PluginGalleryDAO $pluginGalleryDao */
$pluginGalleryDao = DAORegistry::getDAO('PluginGalleryDAO');
switch ($this->argv[0]) {
case 'list':
$plugins = $pluginGalleryDao->getNewestCompatible(
Application::get(),
null,
count($this->argv) > 1 ? $this->argv[1] : null
);
$this->listPlugins($plugins);
$result = true;
break;
case 'info':
$opts = explode('/', $this->argv[1]);
$plugin = $this->selectPlugin($opts[0], $opts[1]);
if ($plugin) {
foreach ($plugin->getAllData() as $key => $data) {
if (is_array($data)) {
echo $key . ': ' . str_replace("\n", '\n', $plugin->getLocalizedData($key)) . "\n";
} else {
echo $key . ': ' . str_replace("\n", '\n', $data) . "\n";
}
}
$result = true;
}
if (!$result) {
error_log('"' . $opts[1] . '" not found in "' . $opts[0] . '"');
$result = true;
}
break;
}
if (!$result) {
$this->usage();
exit(1);
}
return $result;
}
/**
* Select a specific plugin
*
* @param string $category a plugin category
* @param string $name a plugin name
*
* @return GalleryPlugin|null
*/
public function selectPlugin($category, $name)
{
/** @var PluginGalleryDAO $pluginGalleryDao */
$pluginGalleryDao = DAORegistry::getDAO('PluginGalleryDAO');
$plugins = $pluginGalleryDao->getNewestCompatible(
Application::get(),
$category,
$name
);
foreach ($plugins as $plugin) {
if ($plugin->getData('product') === $name) {
return $plugin;
}
}
return;
}
/**
* Print the plugins as a list
*
* @param GalleryPlugin[] $plugins array of plugins
*/
public function listPlugins($plugins)
{
foreach ($plugins as $plugin) {
$statusKey = '';
switch ($plugin->getCurrentStatus()) {
case PLUGIN_GALLERY_STATE_NEWER:
$statusKey = 'manager.plugins.installedVersionNewer';
break;
case PLUGIN_GALLERY_STATE_UPGRADABLE:
$statusKey = 'manager.plugins.installedVersionOlder';
break;
case PLUGIN_GALLERY_STATE_CURRENT:
$statusKey = 'manager.plugins.installedVersionNewest';
break;
case PLUGIN_GALLERY_STATE_AVAILABLE:
$statusKey = 'manager.plugins.noInstalledVersion';
break;
case PLUGIN_GALLERY_STATE_INCOMPATIBLE:
$statusKey = 'manager.plugins.noCompatibleVersion';
break;
}
$keyOut = explode('.', $statusKey);
$keyOut = array_pop($keyOut);
echo implode('/', ['plugins', $plugin->getData('category'), $plugin->getData('product')]) . ' ' . $plugin->getData('releasePackage') . ' ' . $keyOut . "\n";
}
}
}
$tool = new PluginsTool($argv ?? []);
$tool->execute();
+26
View File
@@ -0,0 +1,26 @@
#!/bin/bash
# @file tools/pre-commit.sh
#
# Copyright (c) 2014-2021 Simon Fraser University
# Copyright (c) 2010-2021 John Willinsky
# Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
#
# A pre-commit hook to run php-cs-fixer on committed changes
#
# Run php-cs-fixer on all committed changes
git diff --name-only --diff-filter=d --cached | xargs ./lib/vendor/bin/php-cs-fixer fix --allow-risky=yes --path-mode=intersection --config=.php-cs-fixer.php -q
# Run php-cs-fixer again with --dry-run to throw an error if any files could not be automatically formatted
git diff --name-only --diff-filter=d --cached | xargs ./lib/vendor/bin/php-cs-fixer fix --allow-risky=yes --dry-run --path-mode=intersection --config=.php-cs-fixer.php
if [ $? -eq 0 ]
then
echo -e "\n\e[32m✔ Files formatted successfully.\e[0m\n"
git diff --name-only --diff-filter=d --cached | xargs git add -u
else
echo -e "\n\e[31m✘ Commit aborted. Files could not be formatted.\e[0m\n"
exit 1
fi
+105
View File
@@ -0,0 +1,105 @@
<?php
/**
* @file tools/removeLocaleKey.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class RemoveLocaleKey
*
* @ingroup tools
*
* @brief Remove a locale key from all locale files.
*/
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
class RemoveLocaleKey extends \PKP\cliTool\CommandLineTool
{
/** @var string Locale key to be removed */
public $localeKey = '';
/** @var array Which files to remove the locale key from */
public $dirs = ['locale', 'lib/pkp/locale'];
/**
* Constructor
*/
public function __construct($argv = [])
{
parent::__construct($argv);
if (!sizeof($this->argv)) {
$this->usage();
exit(1);
}
array_shift($argv);
$this->localeKey = array_shift($argv);
if (!empty($argv)) {
$this->dirs = $argv;
}
}
/**
* Print command usage information.
*/
public function usage()
{
echo "\nRemove a locale key from all locale files.\n\n"
. " Usage: php {$this->scriptName} [localeKey] ([path] [path])\n\n"
. " Remove locale keys from app:\n php {$this->scriptName} locale.key locale\n\n"
. " Remove locale keys from pkp-lib:\n php {$this->scriptName} locale.key lib/pkp/locale\n\n"
. " If no path is specified it will remove the locale\n key from files in both directories.\n\n";
}
/**
* Remove the requested locale key
*/
public function execute()
{
$localeKeyLine = 'msgid "' . $this->localeKey . '"';
$rootDir = dirname(__FILE__, 4);
foreach ($this->dirs as $dir) {
$locales = scandir($rootDir . '/' . $dir);
foreach ($locales as $locale) {
if ($locale === '.' || $locale === '..') {
continue;
}
$localeDir = join('/', [$rootDir, $dir, $locale]);
$files = scandir($localeDir);
foreach ($files as $file) {
if ($file === '.' || $file === '..' || substr($file, -2) !== 'po') {
continue;
}
$content = file_get_contents($localeDir . '/' . $file);
$lines = explode("\n", $content);
$newLines = [];
$removing = false;
foreach ($lines as $line) {
if ($localeKeyLine === substr($line, 0, strlen($localeKeyLine))) {
$removing = true;
} elseif ($removing && 'msgid' === substr($line, 0, strlen('msgid'))) {
$removing = false;
}
if (!$removing) {
$newLines[] = $line;
}
}
if (count($lines) !== count($newLines)) {
file_put_contents($localeDir . '/' . $file, join("\n", $newLines));
echo(count($lines) - count($newLines)) . " lines removed from {$localeDir}/{$file}.\n";
}
}
}
}
}
}
$tool = new RemoveLocaleKey($argv ?? []);
$tool->execute();
@@ -0,0 +1,135 @@
<?php
/**
* @file tools/replaceVariableInLocaleKey.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ReplaceVariableInLocaleKey
*
* @ingroup tools
*
* @brief Replace a {$variable} in a specific locale key across all locales
*/
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
class ReplaceVariableInLocaleKey extends \PKP\cliTool\CommandLineTool
{
/** @var string The string to match in a msgid */
public $msgidMatch = '';
/** @var string The {$variable} to search for */
public $oldVariable = '';
/** @var string The {$variable} to replace */
public $newVariable = '';
/**
* Constructor
*/
public function __construct($argv = [])
{
parent::__construct($argv);
// discard first argument: script name
array_shift($argv);
if (sizeof($this->argv) < 3) {
$this->usage();
exit(1);
}
$this->msgidMatch = array_shift($argv);
$this->oldVariable = '{$' . array_shift($argv) . '}';
$this->newVariable = '{$' . array_shift($argv) . '}';
}
public function usage()
{
echo "\nReplace a variable in a locale key.\n\n"
. "A variable like {\$example} can be replaced will be moved from the source file to the target file. This will\n"
. "effect all locales.\n\n"
. " Usage: php {$this->scriptName} [match] [oldVariable] [newVariable]\n\n"
. " [match] The msgid to modify to match in each locale file.\n\n"
. " [oldVariable] The variable to replace, without the `{\$` and `}`, such as: old\n\n"
. " [newVariable] The new variable value, without the `{\$` and `}`, such as: new\n\n"
. " Example: php lib/pkp/tools/replaceVariableInLocaleKey.php emails.submissionAck.body signature contextSignature\n\n";
}
public function execute()
{
$localeDirs = scandir('locale');
if (!$localeDirs) {
$this->output('Locale directories could not be found. Run this from the root directory of the application.');
exit;
}
$localeDirs = array_filter($localeDirs, function ($localeDir) {
return $localeDir !== '.' && $localeDir !== '..';
});
$searchDirs = [
'locale/',
'lib/pkp/locale/'
];
foreach ($searchDirs as $searchDir) {
foreach (array_values($localeDirs) as $localeDir) {
$dir = $searchDir . $localeDir;
if (!file_exists($dir)) {
$this->output('No directory exists at ' . $dir . ' to modify. Skipping this locale.');
continue;
}
$localeFiles = array_filter(scandir($dir), function ($localeDir) {
return $localeDir !== '.' && $localeDir !== '..';
});
foreach (array_values($localeFiles) as $localeFile) {
$countChanges = 0;
$isInMsgid = false;
$file = $dir . '/' . $localeFile;
if (is_dir($file)) {
$this->output('Skipping directory ' . $file);
continue;
}
$lines = explode("\n", file_get_contents($file));
foreach ($lines as $i => $line) {
if ($line === "msgid \"{$this->msgidMatch}\"") {
$isInMsgid = true;
} elseif (trim($line) === '#, fuzzy' || substr($line, 0, 5) === 'msgid') {
$isInMsgid = false;
}
if (!$isInMsgid) {
continue;
}
if (str_contains($line, $this->oldVariable)) {
$lines[$i] = str_replace($this->oldVariable, $this->newVariable, $line);
$countChanges++;
}
}
if ($countChanges) {
file_put_contents($file, join("\n", $lines));
$this->output('Replaced ' . $countChanges . ' lines in ' . $file . '.');
}
}
}
}
}
protected function output(string $string)
{
echo "\n" . $string;
}
}
$tool = new ReplaceVariableInLocaleKey($argv ?? []);
$tool->execute();
@@ -0,0 +1,72 @@
<?php
/**
* @file tools/reprocessUsageStatsMonth.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class reprocessUsageStatsMonth
*
* @ingroup tools
*
* @brief CLI tool to reprocess the usage stats log files for a month.
*/
use APP\core\Services;
use APP\tasks\UsageStatsLoader;
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
class ReprocessUsageStatsMonth extends \PKP\cliTool\CommandLineTool
{
/** Month that should be reprocessed and stats aggregated by. In the form [YYYYMM] */
public string $month;
/**
* Constructor.
*
* @param array $argv command-line arguments (see usage)
*/
public function __construct(array $argv = [])
{
parent::__construct($argv);
if (count($this->argv) != 1) {
$this->usage();
exit(1);
}
$this->month = array_shift($this->argv);
if (!preg_match('/[0-9]{6}/', $this->month)) {
$this->usage();
exit(1);
}
}
/**
* Print command usage information.
*/
public function usage(): void
{
echo "\nReprocess the usage stats log files for a month.\n\n"
. " Usage: php {$this->scriptName} [YYYYMM]\n\n";
}
/**
* Reprocess usage stats log file for the given month.
*/
public function execute(): void
{
// Remove the month from the monthly DB tables
$counterService = Services::get('sushiStats');
$geoService = Services::get('geoStats');
$counterService->deleteMonthlyMetrics($this->month);
$geoService->deleteMonthlyMetrics($this->month);
// Check if all log files from that month are in usageEventLogs folder???
$usageStatsLoader = new UsageStatsLoader([$this->month]);
$usageStatsLoader->execute();
}
}
$tool = new ReprocessUsageStatsMonth($argv ?? []);
$tool->execute();
+93
View File
@@ -0,0 +1,93 @@
#!/bin/bash
#
# USAGE:
# runAllTests.sh [options]
# -C Include class tests in lib/pkp.
# -P Include plugin tests in lib/pkp.
# -c Include class tests in application.
# -p Include plugin tests in application.
# -d Display debug output from phpunit.
# If no options are specified, then all tests will be executed.
#
# Some tests will certain require environment variables in order to cnfigure
# the environment. In particular...
# BASEURL="http://localhost/omp": Full URL to base URL, excluding index.php
# DBHOST=localhost: Hostname of database server
# DBNAME=yyy: Database name
# DBUSERNAME=xxx: Username for database connections
# DBPASSWORD=zzz: Database password
# FILESDIR=files: Pathname to use for storing server-side submission files
# DBTYPE=MySQL: Name of database driver (MySQL or PostgreSQL)
#
set -e # Fail on first error
# We recommend using Travis (https://travis-ci.org/) for continuous-integration
# based testing. Review the Travis configuration file (.travis.yml) as a
# reference for running the test locally, should you choose to do so.
### Command Line Options ###
# Run all types of tests by default, unless one or more is specified
DO_ALL=1
# Various types of tests
DO_PKP_CLASSES=0
DO_PKP_PLUGINS=0
DO_APP_CLASSES=0
DO_APP_PLUGINS=0
DO_COVERAGE=0
DEBUG=""
# Parse arguments
while getopts "CPcpdR" opt; do
case "$opt" in
C) DO_ALL=0
DO_PKP_CLASSES=1
;;
P) DO_ALL=0
DO_PKP_PLUGINS=1
;;
c) DO_ALL=0
DO_APP_CLASSES=1
;;
p) DO_ALL=0
DO_APP_PLUGINS=1
;;
d) DEBUG="--debug"
;;
R) DO_COVERAGE=1
;;
esac
done
PHPUNIT='php lib/pkp/lib/vendor/phpunit/phpunit/phpunit --configuration lib/pkp/tests/phpunit.xml --testdox --no-interaction'
# Where to look for tests
TEST_SUITES='--testsuite '
if [ \( "$DO_ALL" -eq 1 \) -o \( "$DO_PKP_CLASSES" -eq 1 \) ]; then
TEST_SUITES="${TEST_SUITES}LibraryClasses,"
fi
if [ \( "$DO_ALL" -eq 1 \) -o \( "$DO_PKP_PLUGINS" -eq 1 \) ]; then
TEST_SUITES="${TEST_SUITES}LibraryPlugins,"
fi
if [ \( "$DO_ALL" -eq 1 \) -o \( "$DO_APP_CLASSES" -eq 1 \) ]; then
TEST_SUITES="${TEST_SUITES}ApplicationClasses,"
fi
if [ \( "$DO_ALL" -eq 1 \) -o \( "$DO_APP_PLUGINS" -eq 1 \) ]; then
TEST_SUITES="${TEST_SUITES}ApplicationPlugins,"
fi
if [ "$DO_COVERAGE" -eq 1 ]; then
export XDEBUG_MODE=coverage
fi
$PHPUNIT $DEBUG -v ${TEST_SUITES%%,}
if [ "$DO_COVERAGE" -eq 1 ]; then
cat lib/pkp/tests/results/coverage.txt
fi
+50
View File
@@ -0,0 +1,50 @@
<?php
/**
* @file tools/setVersionTool.php
*
* Copyright (c) 2013-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SetVersionTool
*
* @ingroup tools
*
* @brief CLI tool to set a version number for each publication.
*/
use APP\core\Application;
use APP\core\Services;
use APP\facades\Repo;
require(dirname(__FILE__, 4) . '/tools/bootstrap.php');
class SetVersionTool extends \PKP\cliTool\CommandLineTool
{
/**
* Set the version numbers
*/
public function execute()
{
$request = Application::get()->getRequest();
$contextIds = Services::get('context')->getIds();
foreach ($contextIds as $contextId) {
$submissions = Repo::submission()
->getCollector()
->filterByContextIds([$contextId])
->getIds();
foreach ($submissions as $submission) {
$version = 1;
foreach ($submission->getData('publications') as $publication) {
Repo::publication()->edit($publication, ['version' => $version]);
$version++;
}
}
}
}
}
$tool = new SetVersionTool($argv ?? []);
$tool->execute();