/** * 1. Register a page handler for `/foo` * 2. Register a plugin hook that uses the "handler" result param * to route all `/bar/*` requests to the `/foo` handler. * 3. Route a request for a `/bar` page. * 4. Check that the `/foo` handler was called. */ function testRouteSupportsSettingHandlerInHookResultForBackwardsCompatibility() { $this->router->registerPageHandler('foo', array($this, 'foo_page_handler')); $this->hooks->registerHandler('route', 'bar', array($this, 'bar_route_handler')); $query = http_build_query(array('__elgg_uri' => 'bar/baz')); ob_start(); $this->router->route(\Elgg\Http\Request::create("http://localhost/?{$query}")); ob_end_clean(); $this->assertEquals(1, $this->fooHandlerCalls); }
/** * Process logging data * * @param mixed $data The data to process * @param bool $display Whether to display the data to the user. Otherwise log it. * @param int $level The logging level for this data * @return void */ protected function process($data, $display, $level) { // plugin can return false to stop the default logging method $params = array('level' => $level, 'msg' => $data, 'display' => $display, 'to_screen' => $display); if (!$this->hooks->trigger('debug', 'log', $params, true)) { return; } // Do not want to write to screen before page creation has started. // This is not fool-proof but probably fixes 95% of the cases when logging // results in data sent to the browser before the page is begun. if (!isset($this->CONFIG->pagesetupdone)) { $display = false; } // Do not want to write to JS or CSS pages if (elgg_in_context('js') || elgg_in_context('css')) { $display = false; } if ($display == true) { echo '<pre class="elgg-logger-data">'; echo htmlspecialchars(print_r($data, true), ENT_QUOTES, 'UTF-8'); echo '</pre>'; } else { error_log(print_r($data, true)); } }
public function testCanAlterViewOutput() { $this->hooks->registerHandler('view', 'js/interpreted.js', function ($h, $t, $v, $p) { return '// Hello'; }); $this->assertEquals("// Hello", $this->views->renderView('js/interpreted.js')); }
public function testCanFilterResponseBuilder() { $this->hooks->registerHandler('response', 'action:output4', function ($hook, $type, $response, $params) { $this->assertEquals('response', $hook); $this->assertEquals('action:output4', $type); $this->assertEquals($response, $params); $this->assertInstanceOf(OkResponse::class, $response); $response->setContent('good bye'); $response->setStatusCode(ELGG_HTTP_BAD_REQUEST); return $response; }); $this->assertTrue($this->actions->register('output4', "{$this->actionsDir}/output4.php", 'public')); $this->request = $this->prepareHttpRequest('action/output4', 'POST', [], 2, true); $this->createService(); set_input('output', ['foo', 'bar']); set_input('system_message', 'success'); set_input('forward_reason', ELGG_HTTP_OK); set_input('forward_url', 'index'); $this->route(); $response = _elgg_services()->responseFactory->getSentResponse(); $this->assertInstanceOf(Response::class, $response); $this->assertEquals(ELGG_HTTP_BAD_REQUEST, $response->getStatusCode()); $this->assertContains('application/json', $response->headers->get('Content-Type')); //$this->assertContains('charset=utf-8', strtolower($response->headers->get('Content-Type'))); $output = json_encode(['error' => 'good bye']); $this->assertEquals($output, $response->getContent()); }
/** * @group IconService */ public function testCanListenToIconsSavedHook() { $file = new \ElggFile(); $file->owner_guid = 1; $file->setFilename('600x300.jpg'); $this->hooks->registerHandler('entity:icon:saved', 'object', function ($hook, $type, $return, $params) { // make sure we passed in documented params if (!$params['entity'] instanceof \ElggEntity) { return; } if (!isset($params['x1']) || !isset($params['y1']) || !isset($params['x2']) || !isset($params['y2'])) { return; } _elgg_services()->iconService->deleteIcon($params['entity']); }); $this->assertTrue($this->hooks->hasHandler('entity:icon:saved', 'object')); $service = $this->createService(); _elgg_services()->setValue('iconService', $service); $service->saveIconFromElggFile($this->entity, $file); // icons were deleted by the hook $this->assertFalse($service->hasIcon($this->entity, 'master')); $this->assertFalse($service->hasIcon($this->entity, 'large')); $this->assertFalse($service->hasIcon($this->entity, 'medium')); $this->assertFalse($service->hasIcon($this->entity, 'small')); $this->assertFalse($service->hasIcon($this->entity, 'tiny')); $this->assertFalse($service->hasIcon($this->entity, 'topbar')); }
/** * Split a menu into sections, and pass it through the "prepare" hook * * @param UnpreparedMenu $menu Menu * * @return Menu */ public function prepareMenu(UnpreparedMenu $menu) { $name = $menu->getName(); $params = $menu->getParams(); $sort_by = $menu->getSortBy(); $builder = new ElggMenuBuilder($menu->getItems()); $params['menu'] = $builder->getMenu($sort_by); $params['selected_item'] = $builder->getSelected(); $params['menu'] = $this->hooks->trigger('prepare', "menu:{$name}", $params, $params['menu']); return new Menu($params); }
/** * @access private */ public function renderView($view, array $vars = array(), $bypass = false, $viewtype = '', $issue_missing_notice = true) { $view = $this->canonicalizeViewName($view); if (!is_string($view) || !is_string($viewtype)) { $this->logger->log("View and Viewtype in views must be a strings: {$view}", 'NOTICE'); return ''; } // basic checking for bad paths if (strpos($view, '..') !== false) { return ''; } if (!is_array($vars)) { $this->logger->log("Vars in views must be an array: {$view}", 'ERROR'); $vars = array(); } // Get the current viewtype if ($viewtype === '' || !_elgg_is_valid_viewtype($viewtype)) { $viewtype = elgg_get_viewtype(); } // allow altering $vars $vars_hook_params = ['view' => $view, 'vars' => $vars, 'viewtype' => $viewtype]; $vars = $this->hooks->trigger('view_vars', $view, $vars_hook_params, $vars); $view_orig = $view; // Trigger the pagesetup event if (!isset($GLOBALS['_ELGG']->pagesetupdone) && !empty($this->CONFIG->boot_complete)) { $GLOBALS['_ELGG']->pagesetupdone = true; _elgg_services()->events->trigger('pagesetup', 'system'); } // Set up any extensions to the requested view if (isset($this->views->extensions[$view])) { $viewlist = $this->views->extensions[$view]; } else { $viewlist = array(500 => $view); } $content = ''; foreach ($viewlist as $view) { $rendering = $this->renderViewFile($view, $vars, $viewtype, $issue_missing_notice); if ($rendering !== false) { $content .= $rendering; continue; } // attempt to load default view if ($viewtype !== 'default' && $this->doesViewtypeFallback($viewtype)) { $rendering = $this->renderViewFile($view, $vars, 'default', $issue_missing_notice); if ($rendering !== false) { $content .= $rendering; } } } // Plugin hook $params = array('view' => $view_orig, 'vars' => $vars, 'viewtype' => $viewtype); $content = $this->hooks->trigger('view', $view_orig, $params, $content); return $content; }
/** * Filter an AjaxResponse through a plugin hook * * @param AjaxResponse $api_response The API Response * @param string $hook_type The hook type. If given, the response will be filtered by hook * * @return AjaxResponse */ private function filterApiResponse(AjaxResponse $api_response, $hook_type = '') { $api_response->setTtl($this->input->get('response_ttl', 0, false)); if ($hook_type) { $hook = AjaxResponse::RESPONSE_HOOK; $api_response = $this->hooks->trigger($hook, $hook_type, null, $api_response); if (!$api_response instanceof AjaxResponse) { throw new \RuntimeException("The value returned by hook [{$hook}, {$hook_type}] was not an ApiResponse"); } } return $api_response; }
function testRouteOverridenFromHook() { $this->router->registerPageHandler('foo', array($this, 'foo_page_handler')); $this->hooks->registerHandler('route', 'foo', array($this, 'bar_route_override')); $query = http_build_query(array(Application::GET_PATH_KEY => 'foo')); ob_start(); $this->router->route(\Elgg\Http\Request::create("http://localhost/?{$query}")); $result = ob_get_contents(); ob_end_clean(); $this->assertEquals("Page handler override from hook", $result); $this->assertEquals(0, $this->fooHandlerCalls); }
/** * Is someone using the deprecated override * * @param \Elgg\Notifications\Event $event Event * @return boolean */ protected function existsDeprecatedNotificationOverride(\Elgg\Notifications\Event $event) { $entity = $event->getObject(); if (!elgg_instanceof($entity)) { return false; } $params = array('event' => $event->getAction(), 'object_type' => $entity->getType(), 'object' => $entity); $hookresult = $this->hooks->trigger('object:notifications', $entity->getType(), $params, false); if ($hookresult === true) { elgg_deprecated_notice("Using the plugin hook 'object:notifications' has been deprecated by the hook 'send:before', 'notifications'", 1.9); return true; } else { return false; } }
/** * Get the notification body using a pre-Elgg 1.9 plugin hook * * @param Notification $notification Notification * @param NotificationEvent $event Event * @param string $method Method * @return Notification */ protected function getDeprecatedNotificationBody(Notification $notification, NotificationEvent $event, $method) { $entity = $event->getObject(); if (!$entity) { return $notification; } $params = array('entity' => $entity, 'to_entity' => $notification->getRecipient(), 'method' => $method); $subject = $this->getDeprecatedNotificationSubject($entity->getType(), $entity->getSubtype()); $string = $subject . ": " . $entity->getURL(); $body = $this->hooks->trigger('notify:entity:message', $entity->getType(), $params, $string); if ($subject) { $notification->subject = $subject; $notification->body = $body; } return $notification; }
/** * @group InstantNotificationsService */ public function testCanNotifyUserViaCustomMethods() { $object = $this->getTestObject(); $from = $this->mocks()->getUser(); $to1 = $this->mocks()->getUser(); create_metadata($to1->guid, 'notification:method:test_method', true, '', $to1->guid, ACCESS_PUBLIC); $to2 = $this->mocks()->getUser(); create_metadata($to2->guid, 'notification:method:test_method', true, '', $to2->guid, ACCESS_PUBLIC); $subject = 'Test message'; $body = 'Lorem ipsum'; $this->hooks->registerHandler('send', 'notification:test_method', [Values::class, 'getFalse']); $this->hooks->registerHandler('send', 'notification:test_method2', [Values::class, 'getTrue']); $this->setupServices(); $this->notifications->registerMethod('test_method'); $this->notifications->registerMethod('test_method2'); $expected = [$to1->guid => ['test_method2' => true], $to2->guid => ['test_method2' => true]]; $this->assertEquals($expected, notify_user([$to1->guid, $to2->guid, 0], $from->guid, $subject, $body, [], 'test_method2')); }
/** * Removes a user from an access collection. * * Triggers the 'access:collections:remove_user', 'collection' plugin hook. * * @param int $user_guid The user GUID * @param int $collection_id The access collection ID * * @return bool */ function removeUser($user_guid, $collection_id) { $collection_id = (int) $collection_id; $user_guid = (int) $user_guid; $user = get_user($user_guid); $collection = $this->get($collection_id); if (!$user instanceof ElggUser || !$collection) { return false; } $params = array('collection_id' => $collection_id, 'user_guid' => $user_guid); if (!$this->hooks->trigger('access:collections:remove_user', 'collection', $params, true)) { return false; } $db = $this->db; $prefix = $db->prefix; $q = "DELETE FROM {$prefix}access_collection_membership\n\t\t\tWHERE access_collection_id = {$collection_id}\n\t\t\t\tAND user_guid = {$user_guid}"; return (bool) $db->deleteData($q); }
/** * Can a user annotate an entity? * * @tip Can be overridden by registering for the plugin hook [permissions_check:annotate:<name>, * <entity type>] or [permissions_check:annotate, <entity type>]. The hooks are called in that order. * * @tip If you want logged out users to annotate an object, do not call * canAnnotate(). It's easier than using the plugin hook. * * @param ElggEntity $entity Objet entity * @param int $user_guid User guid (default is logged in user) * @param string $annotation_name The name of the annotation (default is unspecified) * * @return bool */ public function canAnnotate(ElggEntity $entity, $user_guid = 0, $annotation_name = '') { if ($annotation_name === null || $annotation_name === false) { // accepting these for BC $annotation_name = ''; } elseif (!is_string($annotation_name)) { throw new InvalidArgumentException(__METHOD__ . ' expects \\$annotation_name to be a string'); } try { $user = $this->entities->getUserForPermissionsCheck($user_guid); } catch (UserFetchFailureException $e) { return false; } $return = (bool) $user; $params = ['entity' => $entity, 'user' => $user, 'annotation_name' => $annotation_name]; if (!empty($annotation_name)) { $return = $this->hooks->trigger("permissions_check:annotate:{$annotation_name}", $entity->getType(), $params, $return); } return $this->hooks->trigger('permissions_check:annotate', $entity->getType(), $params, $return); }
/** * @group AjaxService */ public function testCanFilterResponseToAjax2ViewRequestForARegisteredFormView() { $this->hooks->registerHandler('response', 'form:query_view', function ($hook, $type, $response, $params) { $this->assertEquals('response', $hook); $this->assertEquals('form:query_view', $type); $this->assertEquals($response, $params); $this->assertInstanceOf(OkResponse::class, $response); return elgg_error_response('good bye', REFERRER, ELGG_HTTP_BAD_REQUEST); }); $vars = ['query_value' => 'hello']; $this->request = $this->prepareHttpRequest('ajax/form/query_view', 'GET', $vars, 2); $this->createService(); elgg_register_ajax_view('form/query_view'); $this->route(); $response = _elgg_services()->responseFactory->getSentResponse(); $this->assertInstanceOf(Response::class, $response); $this->assertEquals(ELGG_HTTP_BAD_REQUEST, $response->getStatusCode()); $this->assertContains('application/json', $response->headers->get('Content-Type')); $output = json_encode(['error' => 'good bye'], ELGG_JSON_ENCODING); $this->assertEquals($output, $response->getContent()); }
/** * Returns a configuration array of icon sizes * * @param string $entity_type Entity type * @param string $entity_subtype Entity subtype * @param string $type The name of the icon. e.g., 'icon', 'cover_photo' * @return array * @throws InvalidParameterException */ public function getSizes($entity_type = null, $entity_subtype = null, $type = 'icon') { $sizes = []; if (!$type) { $type = 'icon'; } if ($type == 'icon') { $sizes = $this->config->get('icon_sizes'); } $params = ['type' => $type, 'entity_type' => $entity_type, 'entity_subtype' => $entity_subtype]; if ($entity_type) { $sizes = $this->hooks->trigger("entity:{$type}:sizes", $entity_type, $params, $sizes); } if (!is_array($sizes)) { throw new InvalidParameterException("The icon size configuration for image type '{$type}' " . "must be an associative array of image size names and their properties"); } if (empty($sizes)) { $this->logger->error("Failed to find size configuration for image of type '{$type}' for entity type " . "'{$entity_type}'. Use the 'entity:{$type}:sizes, {$entity_type}' hook to define the icon sizes"); } return $sizes; }
/** * @group UserCapabilities */ public function testCanOverrideContainerLogicWithAHook() { $owner = $this->mocks()->getUser(); $entity = $this->mocks()->getObject(['owner_guid' => $owner->guid]); $this->assertTrue($entity->canWriteToContainer($owner->guid, 'object', 'bar')); $this->hooks->registerHandler('container_logic_check', 'object', function ($hook, $type, $return, $params) use($entity, $owner) { $this->assertInstanceOf(ElggEntity::class, $params['container']); $this->assertInstanceOf(ElggUser::class, $params['user']); $this->assertEquals($entity, $params['container']); $this->assertEquals($owner, $params['user']); $this->assertEquals('object', $type); $this->assertEquals('bar', $params['subtype']); $this->assertNull($return); return false; }); $this->assertFalse($entity->canWriteToContainer($owner->guid, 'object', 'bar')); // make sure container permission hooks are not triggered $this->hooks->registerHandler('container_permissions_check', 'object', function () { return true; }); $this->assertFalse($entity->canWriteToContainer($owner->guid, 'object', 'bar')); }
/** * Do hook handlers exist to modify the view? * * @param string $view View name * * @return bool * @internal Plugins should not use this * @access private */ public function viewHasHookHandlers($view) { return $this->hooks->hasHandler('view', $view) || $this->hooks->hasHandler('view_vars', $view); }
/** * Prepares a redirect response * * @param string $forward_url Redirection URL * @param mixed $status_code HTTP status code or forward reason * @return SymfonyRedirectResponse * @throws InvalidParameterException */ public function redirect($forward_url = REFERRER, $status_code = ELGG_HTTP_FOUND) { if ($forward_url === REFERRER) { $forward_url = $this->request->headers->get('Referer'); } $forward_url = elgg_normalize_url($forward_url); // allow plugins to rewrite redirection URL $current_page = current_page_url(); $params = ['current_url' => $current_page, 'forward_url' => $forward_url]; $forward_reason = (string) $status_code; $forward_url = $this->hooks->trigger('forward', $forward_reason, $params, $forward_url); if ($this->response_sent) { // Response was sent from a forward hook // Clearing handlers to void infinite loops return $this->response_sent; } if ($forward_url === REFERRER) { $forward_url = $this->request->headers->get('Referer'); } if (!is_string($forward_url)) { throw new InvalidParameterException("'forward', '{$forward_reason}' hook must return a valid redirection URL"); } $forward_url = elgg_normalize_url($forward_url); switch ($status_code) { case 'system': case 'csrf': $status_code = ELGG_HTTP_OK; break; case 'admin': case 'login': case 'member': case 'walled_garden': default: $status_code = (int) $status_code; if (!$status_code || $status_code < 100 || $status_code > 599) { $status_code = ELGG_HTTP_SEE_OTHER; } break; } if ($this->isXhr()) { if ($status_code < 100 || $status_code >= 300 && $status_code <= 399 || $status_code > 599) { // We only want to preserve OK and error codes // Redirect responses should be converted to OK responses as this is an XHR request $status_code = ELGG_HTTP_OK; } $output = ob_get_clean(); if (!$this->isAction() && !$this->ajax->isAjax2Request()) { // legacy ajax calls are always OK // actions are wrapped by ResponseFactory::respond() $status_code = ELGG_HTTP_OK; $output = $this->wrapLegacyAjaxResponse($output, $forward_url); } $response = new OkResponse($output, $status_code, $forward_url); $headers = $response->getHeaders(); $headers['Content-Type'] = 'application/json; charset=UTF-8'; $response->setHeaders($headers); return $this->respond($response); } if ($this->isAction()) { // actions should always redirect on non xhr-calls if (!is_int($status_code) || $status_code < 300 || $status_code > 399) { $status_code = ELGG_HTTP_SEE_OTHER; } } $response = new OkResponse('', $status_code, $forward_url); if ($response->isRedirection()) { return $this->send($this->prepareRedirectResponse($forward_url, $status_code)); } return $this->respond($response); }
/** * Get the configuration of AMD * * @return array */ public function getConfig() { $defaults = array('baseUrl' => $this->baseUrl, 'paths' => $this->paths, 'shim' => $this->shim, 'deps' => $this->getDependencies(), 'waitSeconds' => 20); $params = array('defaults' => $defaults); return $this->hooks->trigger('config', 'amd', $params, $defaults); }