champion-8.x-3.1-no-core/modules/contrib/hax/src/Controller/HaxController.php
modules/contrib/hax/src/Controller/HaxController.php
<?php
namespace Drupal\hax\Controller;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityInterface;
use Drupal\node\Controller\NodeViewController;
use Drupal\node\NodeInterface;
use Symfony\Component\HttpFoundation\Response;
/**
* Defines a controller to render a single node in HAX Mode.
*/
class HaxController extends NodeViewController {
/**
* {@inheritdoc}
*/
public function title(EntityInterface $node) {
// TODO Doesn't appear to be working, but shows up in router. What gives?
return t("HAX edit @label", [
'@label' => $this->entityManager->getTranslationFromContext($node)->label(),
]);
}
/**
* Hax node edit form.
*
* @param \Drupal\Core\Entity\EntityInterface $node
* The node.
* @param string $view_mode
* The node's view mode.
* @param null $langcode
* The node's langcode.
*
* @return array
* The node's view render array.
*
* @todo: There's a good chance this logic isn't invoked.
*/
public function form(EntityInterface $node, $view_mode = 'full', $langcode = NULL) {
// Based on NodeViewController's view() method.
$build = parent::view($node, $view_mode, $langcode);
// This method only seems useful for adding attachments, but not for
// altering. Much of the contents of $build['#node'] are protected
// Is hax_node_view() a better place for altering the node field output?
// Or are there other hooks we're missing?
// TODO maybe just route to the canonical if we end up not actually using
// this controller.
return $build;
}
/**
* Permission + Node access check.
*
* @param mixed $op
* The operation.
* @param \Drupal\node\NodeInterface $node
* The node.
*
* @return \Drupal\Core\Access\AccessResultAllowed|\Drupal\Core\Access\AccessResultForbidden
* Either allowed or forbidden access response.
*/
public function access($op, NodeInterface $node) {
if (\Drupal::currentUser()->hasPermission('use hax') && node_node_access($node, $op, \Drupal::currentUser())) {
return AccessResult::allowed();
}
return AccessResult::forbidden();
}
/**
* Custom node save logic for hax.
*
* @param \Drupal\node\NodeInterface $node
* The hax node.
* @param mixed $token
* A token.
*
* @return \Symfony\Component\HttpFoundation\Response
* The http response.
*
* @throws \Drupal\Core\Entity\EntityStorageException
* @throws \Drupal\Core\TypedData\Exception\ReadOnlyException
*/
public function save(NodeInterface $node, $token) {
if (
$_SERVER['REQUEST_METHOD'] == 'PUT' &&
\Drupal::csrfToken()->validate($token)) {
// We're not using the Drupal entity REST system outright here, as PUT
// isn't supported. But we can, ahem, "patch" the behavior in ourselves.
// HAX submitted value is right here.
$body = file_get_contents('php://input');
// Get the current format, and retain it. User will have to manage that
// via the edit tab. We don't want to auto-set it. Making changes like
// that without user intentionality is bad practice.
$current_format = $node->get('body')->getValue()[0]['format'];
// TODO Should we leverage the Text Editor API ?
// https://www.drupal.org/docs/8/api/text-editor-api/overview
// TODO Any santization or security checks on $body?
$node->get('body')->setValue([
'value' => $body,
'format' => $current_format,
]);
$node->save();
// Build the response object.
$response = new Response();
$response->headers->set('Content-Type', 'application/json');
$response->setStatusCode(200);
$response->setContent(json_encode([
'status' => 200,
'message' => 'Save successful',
'data' => $node,
]));
return $response;
}
$response = new Response();
$response->headers->set('Content-Type', 'application/json');
$response->setStatusCode(403);
$response->setContent(json_encode([
'status' => 403,
'message' => 'Unauthorized',
'data' => NULL,
]));
return $response;
}
/**
* Permission + File access check.
*
* @param mixed $op
* The operation?
*
* @return \Drupal\Core\Access\AccessResultAllowed|\Drupal\Core\Access\AccessResultForbidden
* Whether the file access is allowed or forbidden.
*
* @todo: param does not appear to be used. Remove?
*/
public function fileAccess($op) {
// Ensure there are entity permissions to create a file via HAX.
// See https://www.drupal.org/project/hax/issues/2962055#comment-12617576
if (\Drupal::currentUser()->hasPermission('use hax') &&
\Drupal::currentUser()->hasPermission('upload files via hax')) {
return AccessResult::allowed();
}
return AccessResult::forbidden();
}
/**
* Save a file to the file system.
*
* @param mixed $token
* Is this a token object?
*
* @return \Symfony\Component\HttpFoundation\Response
* The http response.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*
* @todo: Update data type for token.
*/
public function fileSave($token) {
$status = 403;
$return = '';
// Check for the uploaded file from our 1-page-uploader app
// and ensure there are entity permissions to create a file via HAX.
// See https://www.drupal.org/project/hax/issues/2962055#comment-12617576.
if (\Drupal::csrfToken()->validate($token) &&
\Drupal::currentUser()->hasPermission('upload files via hax') && isset($_FILES['file-upload'])) {
$upload = $_FILES['file-upload'];
// Check for a file upload.
if (isset($upload['tmp_name']) && is_uploaded_file($upload['tmp_name'])) {
// Get contents of the file if it was uploaded into a variable.
$data = file_get_contents($upload['tmp_name']);
$params = filter_var_array($_POST, FILTER_SANITIZE_STRING);
// See if we had a file_wrapper defined, otherwise this is public.
if (isset($params['file_wrapper'])) {
$file_wrapper = $params['file_wrapper'];
}
else {
$file_wrapper = 'public';
}
// See if Drupal can load from this data source.
if ($file = file_save_data($data, $file_wrapper . '://' . $upload['name'])) {
$return = array(
'file' => array(
'url' => file_create_url($file->getFileUri()),
'uri' => $file->getFileUri(),
'status' => $file->status,
'status' => $file->timestamp,
'uid' => $file->uid,
'uuid' => $file->uuid,
'filemime' => $file->getMimeType(),
'filename' => $file->getFilename(),
),
);
$status = 200;
}
}
}
// Build the response object.
$response = new Response();
$response->headers->set('Content-Type', 'application/json');
$response->setStatusCode($status);
$response->setContent(json_encode([
'status' => $status,
'data' => $return,
]));
return $response;
}
/**
* Load app store.
*
* @param mixed $token
* The app store token.
*
* @return \Symfony\Component\HttpFoundation\Response
* The http response.
*/
public function loadAppStore($token) {
// Ensure we had data PUT here and it is valid.
if (\Drupal::csrfToken()->validate($token)) {
// Hooks and alters.
// Add/alter apps.
$appStore = \Drupal::moduleHandler()->invokeAll('hax_app_store');
\Drupal::moduleHandler()->alter('hax_app_store', $appStore);
// Add/alter layouts.
$autoloaderList = \Drupal::moduleHandler()->invokeAll('hax_autoloader');
\Drupal::moduleHandler()->alter('hax_autoloader', $autoloaderList);
// Add/alter templates. For reference, see appstore.json in
$staxList = \Drupal::moduleHandler()->invokeAll('hax_stax');
\Drupal::moduleHandler()->alter('hax_stax', $staxList);
// Add/alter layouts.
$bloxList = \Drupal::moduleHandler()->invokeAll('hax_blox');
\Drupal::moduleHandler()->alter('hax_blox', $bloxList);
// Send the Response object with Apps and StaxList.
$response = new Response();
$response->headers->set('Content-Type', 'application/json');
$response->setStatusCode(200);
$response->setContent(json_encode([
'status' => 200,
'apps' => $appStore,
'stax' => $staxList,
'blox' => $bloxList,
'autoloader' => $autoloaderList,
]));
return $response;
}
// "Unauthorized" response.
$response = new Response();
$response->setStatusCode(403);
return $response;
}
}
