foldershare-8.x-1.2/src/Form/EditFolderShare.php
src/Form/EditFolderShare.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | <?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(), ]); } } |