diff --git a/core/control/CollectionController.php b/core/control/CollectionController.php new file mode 100644 index 000000000..44d86a945 --- /dev/null +++ b/core/control/CollectionController.php @@ -0,0 +1,172 @@ + 'index', + '$Action' => 'handleAction', + ); + + static $page_size = 20; + + static $allowed_actions = array('index','search', 'SearchForm', 'ResultsForm'); + + /** + * @param string $parentController + * @param string $modelClass + */ + function __construct($parentController = null, $modelClass = null) { + if($parentController) $this->parentController = $parent; + if($modelClass) $this->modelClass = $modelClass; + } + + function init() { + parent::init(); + + Requirements::themedCSS('layout'); + Requirements::themedCSS('typography'); + Requirements::themedCSS('form'); + } + + /** + * Appends the model class to the URL. + * + * @return unknown + */ + function Link() { + die("not implemented yet"); + } + + /** + * Return the class name of the model being managed. + * + * @return unknown + */ + function getModelClass() { + return $this->modelClass; + } + + /** + * Delegate to the {@link RecordController} if a valid numeric ID appears in the URL + * segment. + * + * @param HTTPRequest $request + * @return RecordController + */ + function record($request) { + if(!$this->recordControllerClass) return $this->httpError(403); + + $class = $this->recordControllerClass; + return new $class($this, $this->modelClass); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////// + + function index($request) { + return $this->render(array( + 'Results' => $this->getRecords() + )); + } + + function getRecords($searchCriteria = array()) { + $start = ($this->request->getVar('start')) ? (int)$this->request->getVar('start') : 0; + $limit = $this->stat('page_size'); + + $context = singleton($this->modelClass)->getDefaultSearchContext(); + $query = $context->getQuery($searchCriteria, null, array('start'=>$start,'limit'=>$limit)); + $records = $context->getResults($searchCriteria, null, array('start'=>$start,'limit'=>$limit)); + if($records) { + $records->setPageLimits($start, $limit, $query->unlimitedRowCount()); + } + + return $records; + } + + /** + * Get a search form for a single {@link DataObject} subclass. + * + * @return Form + */ + public function SearchForm() { + $context = singleton($this->modelClass)->getDefaultSearchContext(); + $fields = $context->getSearchFields(); + $form = new Form($this, "SearchForm", + $fields, + new FieldSet( + new FormAction('search', _t('MemberTableField.SEARCH')) + ) + ); + $form->setFormMethod('get'); + + return $form; + } + + /** + * Action to render a data object collection, using the model context to provide filters + * and paging. + * + * @return string + */ + function search($data, $form, $request) { + return $this->render(array( + 'Results' => $this->getRecords($form->getData()), + 'SearchForm' => $form + )); + } + + /** + * Gets the search query generated on the SearchContext from + * {@link DataObject::getDefaultSearchContext()}, + * and the current GET parameters on the request. + * + * @return SQLQuery + */ + function getSearchQuery($searchCriteria) { + $context = singleton($this->modelClass)->getDefaultSearchContext(); + return $context->getQuery($searchCriteria); + } + + + ///////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * @return string + */ + public function ModelNameSingular() { + return singleton($this->modelClass)->i18n_singular_name(); + } + + /** + * @return string + */ + public function ModelNamePlural() { + return singleton($this->modelClass)->i18n_plural_name(); + } + + /** + * Use this to control permissions or completely disable + * links to detail records. + * @return boolean (Default: true) + */ + public function canDetailView() { + return true; + } +} +?> \ No newline at end of file diff --git a/core/control/RecordController.php b/core/control/RecordController.php new file mode 100644 index 000000000..630f8f595 --- /dev/null +++ b/core/control/RecordController.php @@ -0,0 +1,248 @@ + 'index', + 'add' => 'add', + 'AddForm' => 'AddForm', + '$ID/$Action' => 'handleAction', + ); + + /** + * @param string $parentController + * @param string $modelClass + */ + function __construct($parentController = null, $modelClass = null) { + if($parentController) $this->parentController = $parentController; + if($modelClass) $this->modelClass = $modelClass; + + parent::__construct(); + } + + function init() { + parent::init(); + + Requirements::themedCSS('layout'); + Requirements::themedCSS('typography'); + Requirements::themedCSS('form'); + } + + function handleAction($request) { + if(is_numeric($request->latestParam('ID'))) { + $this->currentRecord = DataObject::get_by_id($this->modelClass, $request->latestParam('ID')); + } + + return parent::handleAction($request); + } + + /** + * Link fragment - appends the current record ID to the URL. + * + */ + function Link() { + die("not implemented yet"); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////// + + function index($request) { + return $this->view($request); + } + + /** + * Edit action - shows a form for editing this record + */ + function edit($request) { + if(!$this->currentRecord) { + return $this->httpError(404); + } + if(!$this->currentRecord->canEdit(Member::currentUser())) { + return $this->httpError(403); + } + + return $this->render(array( + 'Form' => $this->EditForm() + )); + } + + /** + * Returns a form for editing the attached model + */ + public function EditForm() { + $fields = $this->currentRecord->getFormFields(); + $fields->push(new HiddenField("ID")); + + $validator = ($this->currentRecord->hasMethod('getValidator')) ? $this->currentRecord->getValidator() : null; + + $actions = new FieldSet( + new FormAction("doSave", "Save") + ); + + if($this->currentRecord->canDelete(Member::currentUser())) { + $actions->push($deleteAction = new FormAction('doDelete', 'Delete')); + $deleteAction->addExtraClass('delete'); + } + + $form = new Form($this, "EditForm", $fields, $actions, $validator); + $form->loadDataFrom($this->currentRecord); + + return $form; + } + + /** + * Postback action to save a record + * + * @param array $data + * @param Form $form + * @param HTTPRequest $request + * @return mixed + */ + function doSave($data, $form, $request) { + if(!$this->currentRecord->canEdit(Member::currentUser())) { + return $this->httpError(403); + } + + $form->saveInto($this->currentRecord); + $this->currentRecord->write(); + + $form->sessionMessage( + _t('RecordController.SAVESUCCESS','Saved record'), + 'good' + ); + + Director::redirectBack(); + } + + /** + * Delete the current record + */ + public function doDelete($data, $form, $request) { + if(!$this->currentRecord->canDelete(Member::currentUser())) { + return $this->httpError(403); + } + + $this->currentRecord->delete(); + $form->sessionMessage( + _t('RecordController.DELETESUCCESS','Successfully deleted record'), + 'good' + ); + + Director::redirectBack(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Renders the record view template. + * + * @param HTTPRequest $request + * @return mixed + */ + function view($request) { + if(!$this->currentRecord) { + return $this->httpError(404); + } + if(!$this->currentRecord->canDelete(Member::currentUser())) { + return $this->httpError(403); + } + + return $this->render(array( + 'Form' => $this->ViewForm() + )); + } + + /** + * Returns a form for viewing the attached model + * + * @return Form + */ + public function ViewForm() { + $fields = $this->currentRecord->getFormFields(); + $form = new Form($this, "EditForm", $fields, new FieldSet()); + $form->loadDataFrom($this->currentRecord); + $form->makeReadonly(); + return $form; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////// + + + /** + * Create a new model record. + * + * @param unknown_type $request + * @return unknown + */ + function add($request) { + if(!singleton($this->modelClass)->canCreate(Member::currentUser())) { + return $this->httpError(403); + } + + return $this->render(array( + 'Form' => $this->AddForm() + )); + } + + /** + * Returns a form for editing the attached model + */ + public function AddForm() { + $newRecord = new $this->modelClass(); + if($newRecord->hasMethod('getAddFormFields')) { + $fields = $newRecord->getAddFormFields(); + } else { + $fields = $newRecord->getFormFields(); + } + + $validator = ($newRecord->hasMethod('getValidator')) ? $newRecord->getValidator() : null; + + $actions = new FieldSet(new FormAction("doAdd", "Add")); + + $form = new Form($this, "AddForm", $fields, $actions, $validator); + + return $form; + } + + function doAdd($data, $form, $request) { + if(!singleton($this->modelClass)->canCreate(Member::currentUser())) { + return $this->httpError(403); + } + + $className = $this->modelClass; + $model = new $className(); + // We write before saveInto, since this will let us save has-many and many-many relationships :-) + $model->write(); + $form->saveInto($model); + $model->write(); + + Director::redirect(Controller::join_links($this->Link(), $model->ID , 'edit')); + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * @return string + */ + public function ModelNameSingular() { + return singleton($this->modelClass)->i18n_singular_name(); + } + + /** + * @return string + */ + public function ModelNamePlural() { + return singleton($this->modelClass)->i18n_plural_name(); + } +} +?> \ No newline at end of file diff --git a/templates/CollectionController.ss b/templates/CollectionController.ss new file mode 100644 index 000000000..c4b1f7e42 --- /dev/null +++ b/templates/CollectionController.ss @@ -0,0 +1,22 @@ + + + + + <% base_tag %> + $MetaTags + + +
+
+ + +
+ $Layout +
+ +
+
+ + \ No newline at end of file diff --git a/templates/Layout/CollectionController.ss b/templates/Layout/CollectionController.ss new file mode 100644 index 000000000..9f54797a0 --- /dev/null +++ b/templates/Layout/CollectionController.ss @@ -0,0 +1,48 @@ +
+
+ +

$Title

+ + <% if SearchForm %> +

<% _t('SEARCH','Search') %>

+ $SearchForm + <% end_if %> + + <% if Results %> + + <% else %> +

<% _t('NORESULTSFOUND','No records found') %>

+ <% end_if %> + + <% if Results.MoreThanOnePage %> +
+ <% if Results.NotLastPage %> + + <% end_if %> + <% if Results.NotFirstPage %> + + <% end_if %> + + <% control Results.Pages %> + <% if CurrentBool %> + $PageNum + <% else %> + $PageNum + <% end_if %> + <% end_control %> + +
+ <% end_if %> + +
+
\ No newline at end of file diff --git a/templates/Layout/RecordController.ss b/templates/Layout/RecordController.ss new file mode 100644 index 000000000..bbfe1e0d9 --- /dev/null +++ b/templates/Layout/RecordController.ss @@ -0,0 +1,9 @@ +
+
+ +

$Title

+ + $Form + +
+
\ No newline at end of file diff --git a/templates/RecordController.ss b/templates/RecordController.ss new file mode 100644 index 000000000..c4b1f7e42 --- /dev/null +++ b/templates/RecordController.ss @@ -0,0 +1,22 @@ + + + + + <% base_tag %> + $MetaTags + + +
+
+ + +
+ $Layout +
+ +
+
+ + \ No newline at end of file