/** * Builds an item edit form. The arguments to getCMSFields() are the popupController and * popupFormName, however this is an experimental API and may change. * * @todo In the future, we will probably need to come up with a tigher object representing a partially * complete controller with gaps for extra functionality. This, for example, would be a better way * of letting Security/login put its log-in form inside a UI specified elsewhere. * * @return Form */ public function ItemEditForm() { $list = $this->gridField->getList(); if (empty($this->record)) { $controller = $this->getToplevelController(); $noActionURL = $controller->removeAction($_REQUEST['url']); $controller->getResponse()->removeHeader('Location'); //clear the existing redirect return $controller->redirect($noActionURL, 302); } $canView = $this->record->canView(); $canEdit = $this->record->canEdit(); $canDelete = $this->record->canDelete(); $canCreate = $this->record->canCreate(); if (!$canView) { $controller = $this->getToplevelController(); // TODO More friendly error return $controller->httpError(403); } $actions = new FieldList(); if ($this->record->ID !== 0) { if ($canEdit) { $actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Save', 'Save'))->setUseButtonTag(true)->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept')); } if ($canDelete) { $actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete'))->setUseButtonTag(true)->addExtraClass('ss-ui-action-destructive action-delete')); } } else { // adding new record //Change the Save label to 'Create' $actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Create', 'Create'))->setUseButtonTag(true)->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'add')); // Add a Cancel link which is a button-like link and link back to one level up. $curmbs = $this->Breadcrumbs(); if ($curmbs && $curmbs->count() >= 2) { $one_level_up = $curmbs->offsetGet($curmbs->count() - 2); $text = sprintf("<a class=\"%s\" href=\"%s\">%s</a>", "crumb ss-ui-button ss-ui-action-destructive cms-panel-link ui-corner-all", $one_level_up->Link, _t('GridFieldDetailForm.CancelBtn', 'Cancel')); $actions->push(new LiteralField('cancelbutton', $text)); } } $fields = $this->component->getFields(); if (!$fields) { $fields = $this->record->getCMSFields(); } // If we are creating a new record in a has-many list, then // pre-populate the record's foreign key. Also disable the form field as // it has no effect. if ($list instanceof HasManyList) { $key = $list->getForeignKey(); $id = $list->getForeignID(); if (!$this->record->isInDB()) { $this->record->{$key} = $id; } if ($field = $fields->dataFieldByName($key)) { $fields->makeFieldReadonly($field); } } // Caution: API violation. Form expects a Controller, but we are giving it a RequestHandler instead. // Thanks to this however, we are able to nest GridFields, and also access the initial Controller by // dereferencing GridFieldDetailForm_ItemRequest->getController() multiple times. See getToplevelController // below. $form = new Form($this, 'ItemEditForm', $fields, $actions, $this->component->getValidator()); $form->loadDataFrom($this->record, $this->record->ID == 0 ? Form::MERGE_IGNORE_FALSEISH : Form::MERGE_DEFAULT); if ($this->record->ID && !$canEdit) { // Restrict editing of existing records $form->makeReadonly(); // Hack to re-enable delete button if user can delete if ($canDelete) { $form->Actions()->fieldByName('action_doDelete')->setReadonly(false); } } elseif (!$this->record->ID && !$canCreate) { // Restrict creation of new records $form->makeReadonly(); } // Load many_many extraData for record. // Fields with the correct 'ManyMany' namespace need to be added manually through getCMSFields(). if ($list instanceof ManyManyList) { $extraData = $list->getExtraData('', $this->record->ID); $form->loadDataFrom(array('ManyMany' => $extraData)); } // TODO Coupling with CMS $toplevelController = $this->getToplevelController(); if ($toplevelController && $toplevelController instanceof LeftAndMain) { // Always show with base template (full width, no other panels), // regardless of overloaded CMS controller templates. // TODO Allow customization, e.g. to display an edit form alongside a search form from the CMS controller $form->setTemplate('LeftAndMain_EditForm'); $form->addExtraClass('cms-content cms-edit-form center'); $form->setAttribute('data-pjax-fragment', 'CurrentForm Content'); if ($form->Fields()->hasTabset()) { $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet'); $form->addExtraClass('cms-tabset'); } $form->Backlink = $this->getBackLink(); } $cb = $this->component->getItemEditFormCallback(); if ($cb) { $cb($form, $this); } $this->extend("updateItemEditForm", $form); return $form; }
/** * Saves the data in this form field into a database record. * * @param DataObject $record The record being updated by the form */ public function saveInto(DataObject $record) { // Can't do has_many without a parent id if(!$record->isInDB()) { $record->write(); } if(!$file_class = $this->getFileClass($record)) { return false; } if(isset($_REQUEST[$this->name]) && is_array($_REQUEST[$this->name])) { if($relation_name = $this->getForeignRelationName($record)) { // Null out all the existing relations and reset. $currentComponentSet = $record->{$this->name}(); $currentComponentSet->removeAll(); // Assign all the new relations (may have already existed) foreach($_REQUEST[$this->name] as $id) { if($file = DataObject::get_by_id($this->baseFileClass, $id)) { $new = ($file_class != $this->baseFileClass) ? $file->newClassInstance($file_class) : $file; $new->write(); $currentComponentSet->add($new); } } } } }
/** * Saves the form data into a record. Nulls out all of the existing file relationships * and rebuilds them, in order to accommodate any deletions. * * @param DataObject $record The record associated with the parent form * * @See Modified for pull request by Micah Sheets to add manymanysortable */ public function saveInto(DataObject $record) { // Can't do has_many without a parent id if (!$record->isInDB()) { $record->write(); } if (!($file_class = $this->getFileClass($record))) { return false; } // Null out all the existing relations and reset. $currentComponentSet = $record->{$this->name}(); $currentComponentSet->removeAll(); if (isset($_REQUEST[$this->name]) && is_array($_REQUEST[$this->name])) { if ($relation_name = $this->getForeignRelationName($record)) { // Assign all the new relations (may have already existed) $data = $_REQUEST; for ($count = 0; $count < count($data[$this->name]); ++$count) { $id = $data[$this->name][$count]; $sort = $data['sort'][$count]; if ($file = DataObject::get_by_id("File", $id)) { $new = $file_class != "File" ? $file->newClassInstance($file_class) : $file; $new->write(); $currentComponentSet->add($new, array('ManyManySort' => $sort)); } } } } }
/** * Scaffolds the relation WidgetArea into the context CMSFields and configurates * the GridField. * * @param DataObject $context Context to get Widget admin for * * @return void * * @author Sebastian Diel <*****@*****.**> * @since 05.03.2014 */ public static function scaffold_widget_area_fields_for($context) { $fields = $context->WidgetArea()->scaffoldFormFields(array('includeRelations' => $context->isInDB(), 'tabbed' => false, 'ajaxSafe' => true)); if ($context->isInDB()) { $widgetsField = $fields->dataFieldByName('Widgets'); $widgetsFieldConfig = $widgetsField->getConfig(); $widgetsFieldConfig->removeComponentsByType('GridFieldAddExistingAutocompleter'); if (class_exists('GridFieldSortableRows')) { $widgetsFieldConfig->addComponent(new GridFieldSortableRows('Sort')); } $widgetsFieldConfig->getComponentByType('GridFieldDataColumns')->setDisplayFields(array('Title' => $context->fieldLabel('Title'), 'ClassName' => _t('WidgetSetWidget.TYPE'))); // this is configured with a remove relation button by default which results in unaccessible widgets $widgetsFieldConfig->removeComponentsByType('GridFieldDeleteAction'); // so we add a new one without a relation button $widgetsFieldConfig->addComponent(new GridFieldDeleteAction()); } return $fields; }
/** * Clone of DataObject::getCMSFields() with some additional SilverCart * related features. * <ul> * <li>Restricted fields can be updated by DataExtension (updateRestrictCMSFields).</li> * <li>Translation fields of DataObjects with SilverCart based translation model will be scaffolded.</li> * </ul> * * @param DataObject $dataObject DataObject to get CMS fields for * @param string $neighbourFieldOfLanguageFields Name of the field to insert language fields after or before * @param bool $insertLangugeFieldsAfter Determines whether to add language fields before or after the given neighbour field * @param bool $tabbed Determines whether get tabbed fields or not * * @return FieldList */ public static function getCMSFields(DataObject $dataObject, $neighbourFieldOfLanguageFields = null, $insertLangugeFieldsAfter = true, $tabbed = true) { $params = array('includeRelations' => $dataObject->isInDB(), 'tabbed' => $tabbed, 'ajaxSafe' => true); $restrictFields = array(); $dataObject->extend('updateRestrictCMSFields', $restrictFields); if (!empty($restrictFields)) { $params['restrictFields'] = $restrictFields; } $tabbedFields = self::scaffoldFormFields($dataObject, $params); if ($dataObject->has_extension($dataObject->class, 'SilvercartDataObjectMultilingualDecorator')) { $languageFields = SilvercartLanguageHelper::prepareCMSFields($dataObject->getLanguageClassName()); foreach ($languageFields as $languageField) { if (!is_null($neighbourFieldOfLanguageFields)) { if ($insertLangugeFieldsAfter) { $tabbedFields->insertAfter($languageField, $neighbourFieldOfLanguageFields); /* * Change the name of the field the insert the next field * Otherwise the sort order would be inverted */ $neighbourFieldOfLanguageFields = $languageField->getName(); } else { $tabbedFields->insertBefore($languageField, $neighbourFieldOfLanguageFields); } } else { $tabbedFields->addFieldToTab('Root.Main', $languageField); } } } $dataObject->extend('updateCMSFields', $tabbedFields); return $tabbedFields; }
/** * Builds an item edit form. The arguments to getCMSFields() are the popupController and * popupFormName, however this is an experimental API and may change. * * @todo In the future, we will probably need to come up with a tigher object representing a partially * complete controller with gaps for extra functionality. This, for example, would be a better way * of letting Security/login put its log-in form inside a UI specified elsewhere. * * @return Form */ public function ItemEditForm() { $list = $this->gridField->getList(); if (empty($this->record)) { $controller = $this->getToplevelController(); $url = $controller->getRequest()->getURL(); $noActionURL = $controller->removeAction($url); $controller->getResponse()->removeHeader('Location'); //clear the existing redirect return $controller->redirect($noActionURL, 302); } $canView = $this->record->canView(); $canEdit = $this->record->canEdit(); $canDelete = $this->record->canDelete(); $canCreate = $this->record->canCreate(); if (!$canView) { $controller = $this->getToplevelController(); // TODO More friendly error return $controller->httpError(403); } // Build actions $actions = $this->getFormActions(); // If we are creating a new record in a has-many list, then // pre-populate the record's foreign key. if ($list instanceof HasManyList && !$this->record->isInDB()) { $key = $list->getForeignKey(); $id = $list->getForeignID(); $this->record->{$key} = $id; } $fields = $this->component->getFields(); if (!$fields) { $fields = $this->record->getCMSFields(); } // If we are creating a new record in a has-many list, then // Disable the form field as it has no effect. if ($list instanceof HasManyList) { $key = $list->getForeignKey(); if ($field = $fields->dataFieldByName($key)) { $fields->makeFieldReadonly($field); } } // Caution: API violation. Form expects a Controller, but we are giving it a RequestHandler instead. // Thanks to this however, we are able to nest GridFields, and also access the initial Controller by // dereferencing GridFieldDetailForm_ItemRequest->getController() multiple times. See getToplevelController // below. $form = new Form($this, 'ItemEditForm', $fields, $actions, $this->component->getValidator()); $form->loadDataFrom($this->record, $this->record->ID == 0 ? Form::MERGE_IGNORE_FALSEISH : Form::MERGE_DEFAULT); if ($this->record->ID && !$canEdit) { // Restrict editing of existing records $form->makeReadonly(); // Hack to re-enable delete button if user can delete if ($canDelete) { $form->Actions()->fieldByName('action_doDelete')->setReadonly(false); } } elseif (!$this->record->ID && !$canCreate) { // Restrict creation of new records $form->makeReadonly(); } // Load many_many extraData for record. // Fields with the correct 'ManyMany' namespace need to be added manually through getCMSFields(). if ($list instanceof ManyManyList) { $extraData = $list->getExtraData('', $this->record->ID); $form->loadDataFrom(array('ManyMany' => $extraData)); } // TODO Coupling with CMS $toplevelController = $this->getToplevelController(); if ($toplevelController && $toplevelController instanceof LeftAndMain) { // Always show with base template (full width, no other panels), // regardless of overloaded CMS controller templates. // TODO Allow customization, e.g. to display an edit form alongside a search form from the CMS controller $form->setTemplate('LeftAndMain_EditForm'); $form->addExtraClass('cms-content cms-edit-form center'); $form->setAttribute('data-pjax-fragment', 'CurrentForm Content'); if ($form->Fields()->hasTabset()) { $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet'); $form->addExtraClass('cms-tabset'); } $form->Backlink = $this->getBackLink(); } $cb = $this->component->getItemEditFormCallback(); if ($cb) { $cb($form, $this); } $this->extend("updateItemEditForm", $form); return $form; }