Open Journal Systems  3.3.0
PKPUsageEventPlugin.inc.php
1 <?php
2 
18 import('lib.pkp.classes.plugins.GenericPlugin');
19 
20 // User classification types.
21 define('USAGE_EVENT_PLUGIN_CLASSIFICATION_BOT', 'bot');
22 define('USAGE_EVENT_PLUGIN_CLASSIFICATION_ADMIN', 'administrative');
23 
24 abstract class PKPUsageEventPlugin extends GenericPlugin {
25 
26  //
27  // Implement methods from PKPPlugin.
28  //
32  function register($category, $path, $mainContextId = null) {
33  $success = parent::register($category, $path, $mainContextId);
34 
35  if ($success) {
36  $eventHooks = $this->getEventHooks();
37  foreach ($eventHooks as $hook) {
38  HookRegistry::register($hook, array($this, 'getUsageEvent'));
39  }
40  }
41 
42  return $success;
43  }
44 
48  function getName() {
49  return 'usageeventplugin';
50  }
51 
56  return PKP_LIB_PATH . DIRECTORY_SEPARATOR . $this->getPluginPath() . DIRECTORY_SEPARATOR . 'settings.xml';
57  }
58 
62  function getDisplayName() {
63  return __('plugins.generic.usageEvent.displayName');
64  }
65 
69  function getDescription() {
70  return __('plugins.generic.usageEvent.description');
71  }
72 
76  function getEnabled($contextId = null) {
77  return true;
78  }
79 
83  function isSitePlugin() {
84  return true;
85  }
86 
87 
88  //
89  // Public methods.
90  //
95  function getUniqueSiteId() {
96  return $this->getSetting(CONTEXT_SITE, 'uniqueSiteId');
97  }
98 
99 
100  //
101  // Hook implementations.
102  //
106  function getUsageEvent($hookName, $args) {
107  // Check if we have a registration to receive the usage event.
108  if (HookRegistry::getHooks('UsageEventPlugin::getUsageEvent')) {
109 
110  $usageEvent = $this->buildUsageEvent($hookName, $args);
111  HookRegistry::call('UsageEventPlugin::getUsageEvent', array_merge(array($hookName, $usageEvent), $args));
112  }
113  return false;
114  }
115 
116 
117  //
118  // Protected methods.
119  //
125  protected function getEventHooks() {
126  return array(
127  'TemplateManager::display',
128  'FileManager::downloadFileFinished'
129  );
130  }
131 
137  protected function getDownloadFinishedEventHooks() {
138  return array(
139  'FileManager::downloadFileFinished'
140  );
141  }
142 
149  protected function buildUsageEvent($hookName, $args) {
150  // Finished downloading a file?
151  if (in_array($hookName, $this->getDownloadFinishedEventHooks())) {
152  // The usage event for this request is already build and
153  // passed to any other registered hook.
154  return null;
155  }
156 
158  $request = $application->getRequest();
159  $router = $request->getRouter(); /* @var $router PageRouter */
160  $templateMgr = $args[0]; /* @var $templateMgr TemplateManager */
161 
162  // We are just interested in page requests.
163  if (!is_a($router, 'PageRouter')) return false;
164 
165  // Check whether we are in journal context.
166  $context = $router->getContext($request);
167  if (!$context) return false;
168 
169  // Prepare request information.
170  list($pubObject, $downloadSuccess, $assocType, $idParams, $canonicalUrlPage, $canonicalUrlOp, $canonicalUrlParams) =
171  $this->getUsageEventData($hookName, $args, $request, $router, $templateMgr, $context);
172 
173  if (!$pubObject) return false;
174 
175  // Timestamp.
176  $time = Core::getCurrentDate();
177 
178  // Actual document size, MIME type.
179  $htmlPageAssocTypes = $this->getHtmlPageAssocTypes();
180  if (in_array($assocType, $htmlPageAssocTypes)) {
181  // HTML pages with no file downloads.
182  $docSize = 0;
183  $mimeType = 'text/html';
184  } else {
185  // Files.
186  $docSize = (int)$pubObject->getFileSize();
187  $mimeType = $pubObject->getFileType();
188  }
189 
190  $canonicalUrl = $router->url(
191  $request, null, $canonicalUrlPage, $canonicalUrlOp, $canonicalUrlParams
192  );
193 
194  // Make sure we log the server name and not aliases.
195  $configBaseUrl = Config::getVar('general', 'base_url');
196  $requestBaseUrl = $request->getBaseUrl();
197  if ($requestBaseUrl !== $configBaseUrl) {
198  // Make sure it's not an url override (no alias on that case).
199  if (!in_array($requestBaseUrl, Config::getContextBaseUrls()) &&
200  $requestBaseUrl !== Config::getVar('general', 'base_url[index]')) {
201  // Alias found, replace it by base_url from config file.
202  // Make sure we use the correct base url override value for the context, if any.
203  $baseUrlReplacement = Config::getVar('general', 'base_url['.$context->getPath().']');
204  if (!$baseUrlReplacement) $baseUrlReplacement = $configBaseUrl;
205  $canonicalUrl = str_replace($requestBaseUrl, $baseUrlReplacement, $canonicalUrl);
206  }
207  }
208 
209  // Public identifiers.
210  // 1) A unique system internal ID that will help us to easily attribute
211  // statistics to a specific publication object.
212  array_unshift($idParams, 'c' . $context->getId());
213  $siteId = $this->getUniqueSiteId();
214  if (empty($siteId)) {
215  // Create a globally unique, persistent site ID
216  // so that we can uniquely identify publication
217  // objects from this site, even if the URL or any
218  // other externally influenced information changes.
219  $siteId = uniqid();
220  $this->updateSetting(0, 'uniqueSiteId', $siteId);
221  }
222  array_unshift($idParams, $siteId);
223  $applicationName = $application->getName();
224  $applicationId = $applicationName . ':' . implode('-', $idParams);
225  $idKey = 'other::' . $applicationName;
226  $identifiers = array($idKey => $applicationId);
227 
228  // 2) Standardized public identifiers, e.g. DOI, URN, etc.
229  if ($this->isPubIdObjectType($pubObject)) {
230  $pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $context->getId());
231  if (!empty($pubIdPlugins)) {
232  foreach ($pubIdPlugins as $pubIdPlugin) {
233  if (!$pubIdPlugin->getEnabled()) continue;
234  $pubId = $pubObject->getStoredPubId($pubIdPlugin->getPubIdType());
235  if ($pubId) {
236  $identifiers[$pubIdPlugin->getPubIdType()] = $pubId;
237  }
238  }
239  }
240  }
241 
242  // Service URI.
243  $serviceUri = $router->url($request, $context->getPath());
244 
245  // IP and Host.
246  $ip = $request->getRemoteAddr();
247  $host = null;
248  if (isset($_SERVER['REMOTE_HOST'])) {
249  // We do NOT actively look up the remote host to
250  // avoid the performance penalty. We only set the remote
251  // host if we get it "for free".
252  $host = $_SERVER['REMOTE_HOST'];
253  }
254 
255  // HTTP user agent.
256  $userAgent = $request->getUserAgent();
257 
258  // HTTP referrer.
259  $referrer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null);
260 
261  // User and roles.
262  $user = $request->getUser();
263  $roles = array();
264  if ($user) {
265  $roleDao = DAORegistry::getDAO('RoleDAO'); /* @var $roleDao PKPRoleDAO */
266  $rolesByContext = $roleDao->getByUserIdGroupedByContext($user->getId());
267  foreach (array(CONTEXT_SITE, $context->getId()) as $workingContext) {
268  if(isset($rolesByContext[$workingContext])) {
269  foreach ($rolesByContext[$workingContext] as $roleId => $role) {
270  $roles[] = $roleId;
271  }
272  }
273  }
274  }
275 
276  // Try a simple classification of the request.
277  $classification = null;
278  if (!empty($roles)) {
279  // Access by editors, authors, etc.
280  $internalRoles = array_diff($roles, array(ROLE_ID_READER));
281  if (!empty($internalRoles)) {
282  $classification = USAGE_EVENT_PLUGIN_CLASSIFICATION_ADMIN;
283  }
284  }
285  if ($request->isBot()) {
286  // The bot classification overwrites other classifications.
287  $classification = USAGE_EVENT_PLUGIN_CLASSIFICATION_BOT;
288  }
289  // TODO: Classify LOCKSS or similar as 'internal' access.
290 
291  /*
292  * Comparison of our event log format with Apache log parameters...
293  *
294  * 1) default parameters:
295  * %h: remote hostname or IP => $ip, $host
296  * %l: remote logname (identd) => not supported, see $user, $roles instead
297  * %u: remote user => not supported, see $user, $roles instead
298  * %t: request time => $time
299  * %r: query => derived objects: $pubObject, $assocType, $canonicalUrl, $identifiers, $serviceUri, $classification
300  * %s: status => not supported (always 200 in our case)
301  * %b: response size => $docSize
302  *
303  * 2) other common parameters
304  * %O: bytes sent => not supported (cannot be reliably determined from within PHP)
305  * %X: connection status => $downloadSuccess (not reliable!)
306  * %{ContentType}o: => $mimeType
307  * %{User-agent}i: => $userAgent
308  * %{Referer}i: => $referrer
309  *
310  * Several items, e.g. time etc., may differ from what Apache
311  * would actually log. But the differences do not matter for our use
312  * cases.
313  */
314 
315  // Collect all information into an array.
316  $usageEvent = compact(
317  'time', 'pubObject', 'assocType', 'canonicalUrl', 'mimeType',
318  'identifiers', 'docSize', 'downloadSuccess', 'serviceUri',
319  'ip', 'host', 'user', 'roles', 'userAgent', 'referrer',
320  'classification'
321  );
322 
323  return $usageEvent;
324  }
325 
342  protected function getUsageEventData($hookName, $hookArgs, $request, $router, $templateMgr, $context) {
343  $nullVar = null;
344  $pubObject = $nullVar;
345  $downloadSuccess = false;
346  $canonicalUrlPage = $canonicalUrlOp = $assocType = null;
347  $canonicalUrlParams = $idParams = array();
348 
349  if ($hookName == 'TemplateManager::display') {
350  $page = $router->getRequestedPage($request);
351  $op = $router->getRequestedOp($request);
352 
353  // First check for a context index page view.
354  if (($page == 'index' || empty($page)) && $op == 'index') {
355  $pubObject = $templateMgr->getTemplateVars('currentContext');
356  if (is_a($pubObject, 'Context')) {
357  $assocType = Application::getContextAssocType();
358  $canonicalUrlOp = '';
359  $canonicalUrlPage = 'index';
360  $downloadSuccess = true;
361  }
362  }
363  }
364 
365  return array($pubObject, $downloadSuccess, $assocType, $idParams, $canonicalUrlPage, $canonicalUrlOp, $canonicalUrlParams);
366  }
367 
368  //
369  // Abstract protected methods.
370  //
376  abstract protected function getHtmlPageAssocTypes();
377 
384  abstract protected function isPubIdObjectType($pubObject);
385 
386 }
387 
388 
$op
$op
Definition: lib/pkp/pages/help/index.php:18
PKPUsageEventPlugin\getDownloadFinishedEventHooks
getDownloadFinishedEventHooks()
Definition: PKPUsageEventPlugin.inc.php:137
Plugin\updateSetting
updateSetting($contextId, $name, $value, $type=null)
Definition: Plugin.inc.php:495
PKPUsageEventPlugin
Base class for usage event plugin. Provide usage events to other statistics plugins.
Definition: PKPUsageEventPlugin.inc.php:24
$application
$application
Definition: index.php:65
DAORegistry\getDAO
static & getDAO($name, $dbconn=null)
Definition: DAORegistry.inc.php:57
PKPUsageEventPlugin\getDisplayName
getDisplayName()
Definition: PKPUsageEventPlugin.inc.php:62
PKPUsageEventPlugin\getEnabled
getEnabled($contextId=null)
Definition: PKPUsageEventPlugin.inc.php:76
PKPUsageEventPlugin\getDescription
getDescription()
Definition: PKPUsageEventPlugin.inc.php:69
PKPUsageEventPlugin\getEventHooks
getEventHooks()
Definition: PKPUsageEventPlugin.inc.php:125
PKPUsageEventPlugin\getInstallSitePluginSettingsFile
getInstallSitePluginSettingsFile()
Definition: PKPUsageEventPlugin.inc.php:55
PKPUsageEventPlugin\getHtmlPageAssocTypes
getHtmlPageAssocTypes()
PluginRegistry\loadCategory
static loadCategory($category, $enabledOnly=false, $mainContextId=null)
Definition: PluginRegistry.inc.php:103
PKPUsageEventPlugin\getName
getName()
Definition: PKPUsageEventPlugin.inc.php:48
Config\getContextBaseUrls
static & getContextBaseUrls()
Definition: Config.inc.php:94
Application\getContextAssocType
static getContextAssocType()
Definition: Application.inc.php:199
PKPUsageEventPlugin\isSitePlugin
isSitePlugin()
Definition: PKPUsageEventPlugin.inc.php:83
Plugin\getSetting
getSetting($contextId, $name)
Definition: Plugin.inc.php:473
Config\getVar
static getVar($section, $key, $default=null)
Definition: Config.inc.php:35
PKPUsageEventPlugin\getUsageEventData
getUsageEventData($hookName, $hookArgs, $request, $router, $templateMgr, $context)
Definition: PKPUsageEventPlugin.inc.php:342
HookRegistry\getHooks
static & getHooks($hookName=null)
Definition: HookRegistry.inc.php:28
PKPUsageEventPlugin\isPubIdObjectType
isPubIdObjectType($pubObject)
Plugin\getPluginPath
getPluginPath()
Definition: Plugin.inc.php:330
Plugin\$request
$request
Definition: Plugin.inc.php:68
Core\getCurrentDate
static getCurrentDate($ts=null)
Definition: Core.inc.php:63
HookRegistry\register
static register($hookName, $callback, $hookSequence=HOOK_SEQUENCE_NORMAL)
Definition: HookRegistry.inc.php:70
PKPUsageEventPlugin\getUsageEvent
getUsageEvent($hookName, $args)
Definition: PKPUsageEventPlugin.inc.php:106
PKPApplication\get
static get()
Definition: PKPApplication.inc.php:235
HookRegistry\call
static call($hookName, $args=null)
Definition: HookRegistry.inc.php:86
PKPUsageEventPlugin\buildUsageEvent
buildUsageEvent($hookName, $args)
Definition: PKPUsageEventPlugin.inc.php:149
GenericPlugin
Abstract class for generic plugins.
Definition: GenericPlugin.inc.php:18
PKPUsageEventPlugin\getUniqueSiteId
getUniqueSiteId()
Definition: PKPUsageEventPlugin.inc.php:95