Elgg  Version 6.3
Service.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Ajax;
4 
12 use Symfony\Component\HttpFoundation\JsonResponse;
13 
20 class Service {
21 
22  protected bool $response_sent = false;
23 
24  protected array $allowed_views = [];
25 
35  public function __construct(
36  protected EventsService $events,
37  protected SystemMessagesService $msgs,
38  protected Request $request,
39  protected ESMService $esm,
40  protected ExternalFiles $externalFiles
41  ) {
42  $message_filter = [$this, 'prepareResponse'];
43  $this->events->registerHandler(AjaxResponse::RESPONSE_EVENT, 'all', $message_filter, 999);
44  }
45 
51  public function isAjax2Request(): bool {
52  $version = $this->request->headers->get('X-Elgg-Ajax-API');
53  return ($version === '2');
54  }
55 
65  public function isReady(): bool {
66  return !$this->response_sent && $this->isAjax2Request();
67  }
68 
76  public function decodeJson($string) {
77  if (!is_string($string)) {
78  return $string;
79  }
80 
81  $object = json_decode($string);
82  return ($object === null) ? $string : $object;
83  }
84 
94  public function respondFromOutput($output, string $event_type = '', bool $try_decode = true) {
95  if ($try_decode) {
96  $output = $this->decodeJson($output);
97  }
98 
99  $api_response = new Response();
100  if (is_object($output) && isset($output->value)) {
101  $api_response->setData($output);
102  } elseif (is_array($output) && isset($output['value'])) {
103  $api_response->setData((object) $output);
104  } else {
105  $api_response->setData((object) ['value' => $output]);
106  }
107 
108  $api_response = $this->filterApiResponse($api_response, $event_type);
109  $response = $this->buildHttpResponse($api_response);
110 
111  $this->response_sent = true;
112  return _elgg_services()->responseFactory->send($response);
113  }
114 
123  public function respondFromApiResponse(AjaxResponse $api_response, string $event_type = '') {
124  $api_response = $this->filterApiResponse($api_response, $event_type);
125  $response = $this->buildHttpResponse($api_response);
126 
127  $this->response_sent = true;
128  return _elgg_services()->responseFactory->send($response);
129  }
130 
139  public function respondWithError(string $msg = '', int $status = 400) {
140  $response = new JsonResponse(['error' => $msg], $status);
141 
142  // clear already set system messages as we respond directly with an error as message body
143  $this->msgs->dumpRegister();
144 
145  $this->response_sent = true;
146  return _elgg_services()->responseFactory->send($response);
147  }
148 
158  private function filterApiResponse(AjaxResponse $api_response, string $event_type = ''): AjaxResponse {
159  $api_response->setTtl($this->request->getParam('elgg_response_ttl', 0, false));
160 
161  if ($event_type) {
162  $event_name = AjaxResponse::RESPONSE_EVENT;
163  $api_response = $this->events->triggerResults($event_name, $event_type, [], $api_response);
164  if (!$api_response instanceof AjaxResponse) {
165  throw new RuntimeException("The value returned by event [{$event_name}, {$event_type}] was not an ApiResponse");
166  }
167  }
168 
169  return $api_response;
170  }
171 
180  private function buildHttpResponse(AjaxResponse $api_response): JsonResponse {
181  if ($api_response->isCancelled()) {
182  return new JsonResponse(['error' => 'The response was cancelled'], 400);
183  }
184 
185  $response = _elgg_services()->responseFactory->prepareJsonResponse($api_response->getData());
186 
187  $ttl = $api_response->getTtl();
188  if ($ttl > 0) {
189  // JsonRequest sets a default Cache-Control header we don't want
190  $response->headers->remove('Cache-Control');
191 
192  $response->setClientTtl($ttl);
193 
194  // if we don't set Expires, Apache will add a far-off max-age and Expires for us.
195  $response->headers->set('Expires', gmdate('D, d M Y H:i:s \G\M\T', time() + $ttl));
196  }
197 
198  return $response;
199  }
200 
209  public function prepareResponse(\Elgg\Event $event) {
210  $response = $event->getValue();
211  if (!$response instanceof AjaxResponse) {
212  return;
213  }
214 
215  if ($this->request->getParam('elgg_fetch_messages', true)) {
216  $messages = $this->msgs->dumpRegister();
217  foreach ($messages as $type => $msgs) {
218  $messages[$type] = array_map(function($value) {
219  return (string) $value;
220  }, $msgs);
221  }
222 
223  $response->getData()->_elgg_msgs = (object) $messages;
224  }
225 
226  if ($this->request->getParam('elgg_fetch_deps', true)) {
227  $deps = [
228  'js' => $this->esm->getImports(),
229  'css' => [],
230  ];
231 
232  foreach ($this->externalFiles->getLoadedResources('css', 'head') as $name => $resource) {
233  if ($name === 'elgg') {
234  // prevent loading of elgg.css in admin context
235  continue;
236  }
237 
238  $deps['css'][] = [
239  'name' => $name,
240  'href' => $resource->url,
241  'integrity' => $resource->integrity,
242  ];
243  }
244 
245  $response->getData()->_elgg_deps = $deps;
246  }
247 
248  return $response;
249  }
250 
258  public function registerView(string $view): void {
259  $this->allowed_views[$view] = true;
260  }
261 
269  public function unregisterView(string $view): void {
270  unset($this->allowed_views[$view]);
271  }
272 
278  public function getViews(): array {
279  return array_keys($this->allowed_views);
280  }
281 }
if(! $user||! $user->canDelete()) $name
Definition: delete.php:22
if(!empty($avatar) &&! $avatar->isValid()) elseif(empty($avatar)) if(! $owner->saveIconFromUploadedFile('avatar')) if(!elgg_trigger_event('profileiconupdate', $owner->type, $owner)) $view
Definition: upload.php:39
$type
Definition: delete.php:21
return[ 'admin/delete_admin_notices'=>['access'=> 'admin'], 'admin/menu/save'=>['access'=> 'admin'], 'admin/plugins/activate'=>['access'=> 'admin'], 'admin/plugins/activate_all'=>['access'=> 'admin'], 'admin/plugins/deactivate'=>['access'=> 'admin'], 'admin/plugins/deactivate_all'=>['access'=> 'admin'], 'admin/plugins/set_priority'=>['access'=> 'admin'], 'admin/security/security_txt'=>['access'=> 'admin'], 'admin/security/settings'=>['access'=> 'admin'], 'admin/security/regenerate_site_secret'=>['access'=> 'admin'], 'admin/site/cache/invalidate'=>['access'=> 'admin'], 'admin/site/flush_cache'=>['access'=> 'admin'], 'admin/site/icons'=>['access'=> 'admin'], 'admin/site/set_maintenance_mode'=>['access'=> 'admin'], 'admin/site/set_robots'=>['access'=> 'admin'], 'admin/site/theme'=>['access'=> 'admin'], 'admin/site/unlock_upgrade'=>['access'=> 'admin'], 'admin/site/settings'=>['access'=> 'admin'], 'admin/upgrade'=>['access'=> 'admin'], 'admin/upgrade/reset'=>['access'=> 'admin'], 'admin/user/ban'=>['access'=> 'admin'], 'admin/user/bulk/ban'=>['access'=> 'admin'], 'admin/user/bulk/delete'=>['access'=> 'admin'], 'admin/user/bulk/unban'=>['access'=> 'admin'], 'admin/user/bulk/validate'=>['access'=> 'admin'], 'admin/user/change_email'=>['access'=> 'admin'], 'admin/user/delete'=>['access'=> 'admin'], 'admin/user/login_as'=>['access'=> 'admin'], 'admin/user/logout_as'=>[], 'admin/user/makeadmin'=>['access'=> 'admin'], 'admin/user/resetpassword'=>['access'=> 'admin'], 'admin/user/removeadmin'=>['access'=> 'admin'], 'admin/user/unban'=>['access'=> 'admin'], 'admin/user/validate'=>['access'=> 'admin'], 'annotation/delete'=>[], 'avatar/upload'=>[], 'comment/save'=>[], 'diagnostics/download'=>['access'=> 'admin'], 'entity/chooserestoredestination'=>[], 'entity/delete'=>[], 'entity/mute'=>[], 'entity/restore'=>[], 'entity/subscribe'=>[], 'entity/trash'=>[], 'entity/unmute'=>[], 'entity/unsubscribe'=>[], 'login'=>['access'=> 'logged_out'], 'logout'=>[], 'notifications/mute'=>['access'=> 'public'], 'plugins/settings/remove'=>['access'=> 'admin'], 'plugins/settings/save'=>['access'=> 'admin'], 'plugins/usersettings/save'=>[], 'register'=>['access'=> 'logged_out', 'middleware'=>[\Elgg\Router\Middleware\RegistrationAllowedGatekeeper::class,],], 'river/delete'=>[], 'settings/notifications'=>[], 'settings/notifications/subscriptions'=>[], 'user/changepassword'=>['access'=> 'public'], 'user/requestnewpassword'=>['access'=> 'public'], 'useradd'=>['access'=> 'admin'], 'usersettings/save'=>[], 'widgets/add'=>[], 'widgets/delete'=>[], 'widgets/move'=>[], 'widgets/save'=>[],]
Definition: actions.php:73
JSON endpoint response.
Definition: Response.php:13
Models the Ajax API service.
Definition: Service.php:20
respondFromOutput($output, string $event_type='', bool $try_decode=true)
Send a JSON HTTP response with the given output.
Definition: Service.php:94
respondFromApiResponse(AjaxResponse $api_response, string $event_type='')
Send a JSON HTTP response based on the given API response.
Definition: Service.php:123
isAjax2Request()
Did the request come from the elgg/Ajax module?
Definition: Service.php:51
__construct(protected EventsService $events, protected SystemMessagesService $msgs, protected Request $request, protected ESMService $esm, protected ExternalFiles $externalFiles)
Constructor.
Definition: Service.php:35
array $allowed_views
Definition: Service.php:24
bool $response_sent
Definition: Service.php:22
registerView(string $view)
Register a view to be available for ajax calls.
Definition: Service.php:258
prepareResponse(\Elgg\Event $event)
Prepare the response with additional metadata, like system messages and required ES modules.
Definition: Service.php:209
isReady()
Is the service ready to respond to the request?
Definition: Service.php:65
respondWithError(string $msg='', int $status=400)
Send a JSON HTTP 400 response.
Definition: Service.php:139
getViews()
Returns an array of views allowed for ajax calls.
Definition: Service.php:278
decodeJson($string)
Attempt to JSON decode the given string.
Definition: Service.php:76
unregisterView(string $view)
Unregister a view for ajax calls.
Definition: Service.php:269
External files service.
Models an event passed to event handlers.
Definition: Event.php:11
Events service.
Exception thrown if an error which can only be found on runtime occurs.
Elgg HTTP request.
Definition: Request.php:17
Keeps track of ES modules.
Definition: ESMService.php:16
System messages service.
$version
if($email instanceof \Elgg\Email) $object
Definition: body.php:24
if(!empty($children)) elseif($item->getData('show_with_empty_children')===false) $deps
Definition: item.php:54
if($item instanceof \ElggEntity) elseif($item instanceof \ElggRiverItem) elseif($item instanceof \ElggRelationship) elseif(is_callable([ $item, 'getType']))
Definition: item.php:48
$output
Definition: download.php:9
_elgg_services()
Get the global service provider.
Definition: elgglib.php:337
$value
Definition: generic.php:51
JSON endpoint response.
getTtl()
Get the max-age for client caching.
getData()
Get the response data, which will be a stdClass object with property "value".
$request
Definition: livesearch.php:12
$resource
if(parse_url(elgg_get_site_url(), PHP_URL_PATH) !=='/') if(file_exists(elgg_get_root_path() . 'robots.txt'))
Set robots.txt.
Definition: robots.php:10
$messages
Definition: admin.php:12
$response
Definition: content.php:10