[ Mini Kiebo ]
Server: Windows NT DESKTOP-5B8S0D4 6.2 build 9200 (Windows 8 Professional Edition) i586
Path:
D:
/
Backup
/
05122024
/
htdocs
/
jurnal-kesmas
/
lib
/
pkp
/
plugins
/
generic
/
usageEvent
/
[
Home
]
File: PKPUsageEventPlugin.php
<?php /** * @file plugins/generic/usageEvent/PKPUsageEventPlugin.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 PKPUsageEventPlugin * * @ingroup plugins_generic_usageEvent * * @brief Base class for usage event plugin. Provide usage events to * other statistics plugins. */ namespace PKP\plugins\generic\usageEvent; use APP\core\Application; use APP\core\PageRouter; use APP\template\TemplateManager; use PKP\config\Config; use PKP\context\Context; use PKP\core\Core; use PKP\core\DataObject; use PKP\core\PKPRequest; use PKP\db\DAORegistry; use PKP\plugins\GenericPlugin; use PKP\plugins\Hook; use PKP\plugins\PluginRegistry; use PKP\security\Role; use PKP\security\RoleDAO; use PKP\template\PKPTemplateManager; // User classification types. define('USAGE_EVENT_PLUGIN_CLASSIFICATION_BOT', 'bot'); define('USAGE_EVENT_PLUGIN_CLASSIFICATION_ADMIN', 'administrative'); abstract class PKPUsageEventPlugin extends GenericPlugin { // // Implement methods from PKPPlugin. // /** * @copydoc Plugin::register() * * @param null|mixed $mainContextId */ public function register($category, $path, $mainContextId = null) { $success = parent::register($category, $path, $mainContextId); if ($success) { $eventHooks = $this->getEventHooks(); foreach ($eventHooks as $hook) { Hook::add($hook, [$this, 'getUsageEvent']); } } return $success; } /** * @copydoc LazyLoadPlugin::getName() */ public function getName() { return 'usageeventplugin'; } /** * @copydoc Plugin::getInstallSitePluginSettingsFile() */ public function getInstallSitePluginSettingsFile() { return PKP_LIB_PATH . "/{$this->getPluginPath()}/settings.xml"; } /** * @copydoc Plugin::getDisplayName() */ public function getDisplayName() { return __('plugins.generic.usageEvent.displayName'); } /** * @copydoc Plugin::getDescription() */ public function getDescription() { return __('plugins.generic.usageEvent.description'); } /** * @copydoc LazyLoadPlugin::getEnabled() * * @param null|mixed $contextId */ public function getEnabled($contextId = null) { return true; } /** * @copydoc Plugin::isSitePlugin() */ public function isSitePlugin() { return true; } /** * @copydoc Plugin::getCanEnable() */ public function getCanEnable() { return false; } /** * @copydoc Plugin::getCanDisable() */ public function getCanDisable() { return false; } // // Public methods. // /** * Get the unique site id. * * @return mixed string or null */ public function getUniqueSiteId() { return Application::get()->getUUID(); } // // Hook implementations. // /** * Get usage event and pass it to the registered plugins, if any. */ public function getUsageEvent($hookName, $args) { // Check if we have a registration to receive the usage event. if (Hook::getHooks('UsageEventPlugin::getUsageEvent')) { $usageEvent = $this->buildUsageEvent($hookName, $args); Hook::call('UsageEventPlugin::getUsageEvent', array_merge([$hookName, $usageEvent], $args)); } return false; } // // Protected methods. // /** * Get all hooks that must be used to * generate usage events. * * @return array */ protected function getEventHooks() { return [ 'TemplateManager::display', 'FileManager::downloadFileFinished' ]; } /** * Get all hooks that define the * finished file download. * * @return array */ protected function getDownloadFinishedEventHooks() { return [ 'FileManager::downloadFileFinished' ]; } /** * Build an usage event. * * @param string $hookName * @param array $args * * @return false|?array */ protected function buildUsageEvent($hookName, $args) { // Finished downloading a file? if (in_array($hookName, $this->getDownloadFinishedEventHooks())) { // The usage event for this request is already build and // passed to any other registered hook. return null; } $application = Application::get(); $request = $application->getRequest(); $router = $request->getRouter(); /** @var PageRouter $router */ $templateMgr = $args[0]; /** @var TemplateManager $templateMgr */ // We are just interested in page requests. if (!($router instanceof PageRouter)) { return false; } // Check whether we are in journal context. $context = $router->getContext($request); if (!$context) { return false; } // Prepare request information. [$pubObject, $downloadSuccess, $assocType, $idParams, $canonicalUrlPage, $canonicalUrlOp, $canonicalUrlParams] = $this->getUsageEventData($hookName, $args, $request, $router, $templateMgr, $context); if (!$pubObject) { return false; } // Timestamp. $time = Core::getCurrentDate(); // Actual document size, MIME type. $htmlPageAssocTypes = $this->getHtmlPageAssocTypes(); if (in_array($assocType, $htmlPageAssocTypes)) { // HTML pages with no file downloads. $mimeType = 'text/html'; } elseif ($pubObject instanceof \APP\issue\IssueGalley) { $mimeType = $pubObject->getFileType(); } else { // Files. $path = $pubObject->getData('path'); $mimeType = $pubObject->getData('mimetype'); } $canonicalUrl = $router->url( $request, null, $canonicalUrlPage, $canonicalUrlOp, $canonicalUrlParams ); // Make sure we log the server name and not aliases. $configBaseUrl = Config::getVar('general', 'base_url'); $requestBaseUrl = $request->getBaseUrl(); if ($requestBaseUrl !== $configBaseUrl) { // Make sure it's not an url override (no alias on that case). if (!in_array($requestBaseUrl, Config::getContextBaseUrls()) && $requestBaseUrl !== Config::getVar('general', 'base_url[index]')) { // Alias found, replace it by base_url from config file. // Make sure we use the correct base url override value for the context, if any. $baseUrlReplacement = Config::getVar('general', 'base_url[' . $context->getPath() . ']'); if (!$baseUrlReplacement) { $baseUrlReplacement = $configBaseUrl; } $canonicalUrl = str_replace($requestBaseUrl, $baseUrlReplacement, $canonicalUrl); } } // Public identifiers. // 1) A unique system internal ID that will help us to easily attribute // statistics to a specific publication object. array_unshift($idParams, 'c' . $context->getId()); $siteId = $this->getUniqueSiteId(); array_unshift($idParams, $siteId); $applicationName = $application->getName(); $applicationId = $applicationName . ':' . implode('-', $idParams); $idKey = 'other::' . $applicationName; $identifiers = [$idKey => $applicationId]; // 2) Standardized public identifiers, e.g. DOI, URN, etc. if ($this->isPubIdObjectType($pubObject)) { $pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $context->getId()); if (!empty($pubIdPlugins)) { foreach ($pubIdPlugins as $pubIdPlugin) { if (!$pubIdPlugin->getEnabled()) { continue; } $pubId = $pubObject->getStoredPubId($pubIdPlugin->getPubIdType()); if ($pubId) { $identifiers[$pubIdPlugin->getPubIdType()] = $pubId; } } } // Handle DOIs separately if ($context->areDoisEnabled()) { $pubId = $pubObject->getStoredPubId('doi'); if ($pubId) { $identifiers['doi'] = $pubId; } } } // Service URI. $serviceUri = $router->url($request, $context->getPath()); // IP and Host. $ip = $request->getRemoteAddr(); $host = null; if (isset($_SERVER['REMOTE_HOST'])) { // We do NOT actively look up the remote host to // avoid the performance penalty. We only set the remote // host if we get it "for free". $host = $_SERVER['REMOTE_HOST']; } // HTTP user agent. $userAgent = $request->getUserAgent(); // HTTP referrer. $referrer = ($_SERVER['HTTP_REFERER'] ?? null); // User and roles. $user = $request->getUser(); $roles = []; if ($user) { $roleDao = DAORegistry::getDAO('RoleDAO'); /** @var RoleDAO $roleDao */ $rolesByContext = $roleDao->getByUserIdGroupedByContext($user->getId()); foreach ([\PKP\core\PKPApplication::CONTEXT_SITE, $context->getId()] as $workingContext) { if (isset($rolesByContext[$workingContext])) { foreach ($rolesByContext[$workingContext] as $roleId => $role) { $roles[] = $roleId; } } } } // Try a simple classification of the request. $classification = null; if (!empty($roles)) { // Access by editors, authors, etc. $internalRoles = array_diff($roles, [Role::ROLE_ID_READER]); if (!empty($internalRoles)) { $classification = USAGE_EVENT_PLUGIN_CLASSIFICATION_ADMIN; } } if ($request->isBot()) { // The bot classification overwrites other classifications. $classification = USAGE_EVENT_PLUGIN_CLASSIFICATION_BOT; } // TODO: Classify LOCKSS or similar as 'internal' access. /* * Comparison of our event log format with Apache log parameters... * * 1) default parameters: * %h: remote hostname or IP => $ip, $host * %l: remote logname (identd) => not supported, see $user, $roles instead * %u: remote user => not supported, see $user, $roles instead * %t: request time => $time * %r: query => derived objects: $pubObject, $assocType, $canonicalUrl, $identifiers, $serviceUri, $classification * %s: status => not supported (always 200 in our case) * * 2) other common parameters * %O: bytes sent => not supported (cannot be reliably determined from within PHP) * %X: connection status => $downloadSuccess (not reliable!) * %{ContentType}o: => $mimeType * %{User-agent}i: => $userAgent * %{Referer}i: => $referrer * * Several items, e.g. time etc., may differ from what Apache * would actually log. But the differences do not matter for our use * cases. */ // Collect all information into an array. $usageEvent = compact( 'time', 'pubObject', 'assocType', 'canonicalUrl', 'mimeType', 'identifiers', 'downloadSuccess', 'serviceUri', 'ip', 'host', 'user', 'roles', 'userAgent', 'referrer', 'classification' ); return $usageEvent; } /** * Get usage event details based on the passed hook. * Subclasses should extend to implement application specifics. * * @param string $hookName * @param array $hookArgs * @param PKPRequest $request * @param PageRouter $router * @param PKPTemplateManager $templateMgr * @param Context $context * * @return array With the following data: * DataObject the published object, boolean download success, integer used published object assoc type, * string used published object id foreign keys lookup (all parent associated objects id, * preceded with a single letter to identify the object), string canonical url page, * string canonical url operation, array with canonical url parameters. * * @see PKPUsageEventPlugin::buildUsageEvent() */ protected function getUsageEventData($hookName, $hookArgs, $request, $router, $templateMgr, $context) { $nullVar = null; $pubObject = $nullVar; $downloadSuccess = false; $canonicalUrlPage = $canonicalUrlOp = $assocType = null; $canonicalUrlParams = $idParams = []; if ($hookName == 'TemplateManager::display') { $page = $router->getRequestedPage($request); $op = $router->getRequestedOp($request); // First check for a context index page view. if (($page == 'index' || empty($page)) && $op == 'index') { $pubObject = $templateMgr->getTemplateVars('currentContext'); if ($pubObject instanceof Context) { $assocType = Application::getContextAssocType(); $canonicalUrlOp = ''; $canonicalUrlPage = 'index'; $downloadSuccess = true; } } } return [$pubObject, $downloadSuccess, $assocType, $idParams, $canonicalUrlPage, $canonicalUrlOp, $canonicalUrlParams]; } // // Abstract protected methods. // /** * Get all assoc types that have their usage event * produced by html page access. * * @return array */ abstract protected function getHtmlPageAssocTypes(); /** * Whether or not the passed object is of a type that can have * different public identifiers, like DOI, URN, etc. * * @param DataObject $pubObject * * @return bool */ abstract protected function isPubIdObjectType($pubObject); }