foldershare-8.x-1.2/src/Form/EditFolderShare.php
src/Form/EditFolderShare.php
<?php
namespace Drupal\foldershare\Form;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Drupal\foldershare\Constants;
use Drupal\foldershare\ManageLog;
use Drupal\foldershare\Entity\FolderShare;
/**
* Defines a user form for editing folder share entity fields.
*
* The form presents the current values of all fields, except
* for the name and other internal fields. The set of fields
* shown always includes the description. Additional fields may
* include the language choice, if the Drupal core Languages module
* is enabled, plus any other fields a site has added to the folder type.
*
* The user is prompted to change any of the presented fields. A 'Save'
* button triggers the form and saves the edits.
*
* <b>Access control:</b>
* The route to this form must invoke the access control handler to
* insure that the user is the admin or the owner of the folder subject.
*
* <b>Route:</b>
* The route to this form must include a $foldershare argument.
*
* <B>Warning:</B> This class is strictly internal to the FolderShare
* module. The class's existance, name, and content may change from
* release to release without any promise of backwards compatability.
*
* @ingroup foldershare
*/
final class EditFolderShare extends ContentEntityForm {
/*---------------------------------------------------------------------
*
* Form.
*
*---------------------------------------------------------------------*/
/**
* {@inheritdoc}
*/
public function getFormId() {
return str_replace('\\', '_', get_class($this));
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $formState) {
//
// Validate.
// ---------
// Hidden and disabled items cannot be edited.
$item = $this->getEntity();
if ($item->isSystemHidden() === TRUE) {
// Hidden items do not exist.
throw new NotFoundHttpException(
FolderShare::getStandardHiddenMessage($item->getName()));
}
if ($item->isSystemDisabled() === TRUE) {
// Disabled items cannot be edited.
throw new ConflictHttpException(
FolderShare::getStandardDisabledMessage('edited', $item->getName()));
}
//
// Setup
// -----
// Let the parent class build the default form for the entity.
// The form may include any editable fields accessible by the
// current user. This will not include the name field, or other
// internal fields, which are blocked from being listed by
// the base field definitions for the Folder entity.
//
// The order of fields on the form will honor the site's choices
// via the field_ui.
//
// The EntityForm parent class automatically adds a 'Save' button
// to submit the form.
$form = parent::form($form, $formState);
$form['#title'] = $this->t(
'Edit "@name"',
[
'@name' => $item->getName(),
]);
//
// Do NOT acquire a process lock.
// ------------------------------
// Normally, edit operations on a FolderShare entity should acquire
// a process lock on the entity's root folder tree in order to prevent
// other edit operations from modifying the entity at the same time.
//
// However, the edit form requests user input and users can take a
// long time to edit a description or other fields. They can also
// abort and close the window or back up to another page. We cannot
// hold a lock for all of that time.
//
// Instead, we do not acquire any locks on presentation of the form.
// Later, during a save of the form's results, locking will resolve
// any potential conflicts.
return $form;
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $formState) {
// The parent classes handle creating an un-saved entity:
//
// - The form controller call's the form's getEntityFromRouteMatch()
// to get the entity ID from the URL and load it.
//
// - On form submit, submitForm() calls buildEntity() to build
// an entity object, which it then sets as the form's entity
// available via getEntity().
//
// - buildEntity() clones the loaded entity and copies the form's
// values into it, but does not save it.
//
// - On form submit, after calling submitForm(), save() is called to
// save the updated entity. The parent class just calls entity save().
//
// If the entity has been deleted by another process during the edit,
// then getEntityFromRouteMatch() earlier will have failed with a
// NotFoundHttpException. This doesn't explain much to the user, but
// it is correct.
//
// If the entity has been changed by another process during the edit,
// then those changes are in the loaded entity. buildEntity() will
// have overwritten any of those fields that are in this edit form.
// Since key internal fields cannot be in an edit form, systemhidden,
// systemdisabled, parentid, rootid, uid, etc., are all unchanged from the
// loaded entity.
$item = $this->getEntity();
//
// Validate.
// ---------
// If the entity has been marked as hidden or disabled by another process
// during the edit, then the edit has to be rejected.
//
// Hidden entities are being deleted, so it doesn't make sense to set
// fields in an entity that is doomed.
//
// Disabled entities are being moved, copied, or having their ownership
// changed. If we save this entity, it could overwrite whatever change
// is in progress.
if ($item->isSystemHidden() === TRUE) {
// Hidden items do not exist.
throw new NotFoundHttpException(
FolderShare::getStandardHiddenMessage($item->getName()));
}
if ($item->isSystemDisabled() === TRUE) {
// Disabled items cannot be edited.
throw new ConflictHttpException(
FolderShare::getStandardDisabledMessage('saved', $item->getName()));
}
//
// Lock.
// -----
// Another process could be working on the root folder tree that
// contains this entity. They could be working on this entity itself.
// If we save the entity without a lock, we could collide.
//
// For example, imagine that a change ownership operation is in progress
// on the folder containing this entity. The loaded entity in hand doesn't
// have the new owner ID yet. The other process, which has a lock on
// the root folder tree, traverses down past this entity and sets and
// saves the owner ID, acting under the belief that it has an exclusive
// lock on all entities in the folder tree. And now, here, we just save
// the edited entity back to the database without getting a lock first.
// We'll overwrite the entity and lose the ownership change just made,
// causing a file system corruption.
//
// So, we MUST lock before saving the changed entity.
$rootId = $item->getRootItemId();
if (FolderShare::acquireRootOperationLock($rootId) === FALSE) {
// Lock failed. There is another operation in progress.
// We cannot save this entity now. The user can hit their browser's
// BACK button and most browsers will have saved all field values
// in the edit form. They can then try to save again and, hopefully,
// the lock will be done by then.
throw new ConflictHttpException(
FolderShare::getStandardLockMessage('saved', $item->getName()));
}
// Save
// -----
// The parent class handles saving the entity.
parent::save($form, $formState);
//
// Unlock.
// -------
// We're done with the save. Unlock.
FolderShare::releaseRootOperationLock($rootId);
//
// Hook & log.
// -----------
// Announce the change.
FolderShare::postOperationHook(
'edit',
[
$item,
\Drupal::currentUser()->id(),
]);
ManageLog::activity(
"Edited @kind '@name' (# @id).",
[
'@id' => $item->id(),
'@kind' => $item->getKind(),
'@name' => $item->getName(),
'entity' => $item,
'uid' => \Drupal::currentUser()->id(),
]);
//
// Redirect
// --------
// Return to the view page for the folder.
$formState->setRedirect(
Constants::ROUTE_FOLDERSHARE,
[
Constants::ROUTE_FOLDERSHARE_ID => $item->id(),
]);
}
}
