I wish to create the edit form in Silverstripe 4.2, much like this Stack Overflow's edit function that i'm looking for.
EDITED: I want to be able to have a page that is only available to the registered member of my website that they can post their class listings on the Frontend (not in CMS) as an owner, and need to have a 'edit' click that takes you to an identical form (same ClassListingForm) that lets member owner to edit/update their own class listings that they have posted. I have everything working except the edit and submit functions which I'm stuck on at the moment.
I have a link for editing the specific class listing:
Edit class listing</div>
It does redirected to 404 page not found with this url shown:
"http://.../learners/class-listings/edit/61"
Here's the code below I have so far, the ClassListingForm is working fine, just need to get the EditListingForm and doClassListing functions to work properly, and i may be doing something wrong in these codes? or is there a better way of doing the edit form properly which i'm unable to find anywhere on the search for specific on what i need as there's not much tutorial that covers the EditForm function on the SilverStripe lessons.
<?php
class ClassListings extends DataObject {
private static $table_name = 'ClassListings';
private static $db = [
'CourseTitle' => 'Varchar(255)',
'CourseLocation' => 'Varchar(255)',
];
private static $has_one = [
'ClassListingPage' => ClassListingPage::class,
];
}
<?php
class ClassListingPageController extends PageController {
private static $allowed_actions = [
'ClassListingForm',
'ClassEditForm'
];
public function ClassListingForm() {
$id = (int)$this->urlParams['ID'];
$data = ($id)? $data = ClassListings::get()->byID($id) : false;
$form = Form::create(
$this,
__FUNCTION__,
FieldList::create(
TextField::create('CourseTitle', 'Course title:')
->setAttribute('placeholder', 'NZSL Level 1, NZSL 1A')
->setCustomValidationMessage('Please enter the course title field')
->addExtraClass('requiredField CourseTitle'),
TextField::create('CourseLocation','Region:')
->setAttribute('placeholder', 'Enter region')
->setCustomValidationMessage('Please enter the region field')
->addExtraClass('requiredField'),
HiddenField::create('ID', 'ID')->setValue($ClassListingPageID)
),
FieldList::create(
FormAction::create('handleClassListing')
->setTitle('Post your class listing')
->addExtraClass('btn btn-primary primary')
),
RequiredFields::create(
'CourseTitle',
'CourseLocation'
)
);
$form->loadDataFrom(Member::get()->byID(Member::currentUserID()));
$form->getSecurityToken()->getValue();
if ($form->hasExtension('FormSpamProtectionExtension')) {
$form->enableSpamProtection();
}
$data = $this->getRequest()->getSession()->get("FormData.{$form->getName()}.data");
return $data ? $form->loadDataFrom($data) : $form;
}
public function handleClassListing($data, $form) {
$session = $this->getRequest()->getSession();
$session->set("FormData.{$form->getName()}.data", $data);
$class = ClassListings::create($this->owner);
$class->CourseTitle = $data['CourseTitle'];
$class->CourseLocation = $data['CourseLocation'];
$class->ID = $data['ID'];
$class->ClassListingPageID = $this->ID;
$form->saveInto($class);
$class->write();
$session->clear("FormData.{$form->getName()}.data");
$form->sessionMessage('Your class listing has been posted!','good');
$session = $this->getRequest()->getSession();
return $this->redirect($this->Link());
}
public function ClassEditForm() {
$ClassListingPageID = (int)$this->urlParams['ID'];
$data = ($ClassListingPageID)? $data = ClassListings::get()->byID($ClassListingPageID) : false;
$var = $this->getRequest()->getVar('$data');
if($var){
$form = Form::create(
$this,
__FUNCTION__,
FieldList::create(
TextField::create('CourseTitle', 'Course title:')
->setAttribute('placeholder', 'NZSL Level 1, NZSL 1A')
->setCustomValidationMessage('Please enter the course title field')
->addExtraClass('requiredField CourseTitle'),
TextField::create('CourseLocation','Region:')
->setAttribute('placeholder', 'Enter region')
->setCustomValidationMessage('Please enter the region field')
->addExtraClass('requiredField'),
HiddenField::create('ID', 'ID')->setValue($ClassListingPageID)
),
FieldList::create(
FormAction::create('doClassListing')
->setTitle('Post your class listing')
->addExtraClass('btn btn-primary primary')
),
RequiredFields::create(
'CourseTitle',
'CourseLocation',
)
);
$form->loadDataFrom(ClassListings::get()->filter(['ClassListingPageID' => $var])[0]);
$form->getSecurityToken()->getValue();
if ($form->hasExtension('FormSpamProtectionExtension')) {
$form->enableSpamProtection();
}
$data = $this->getRequest()->getSession()->get("FormData.{$form->getName()}.data");
return $data ? $form->loadDataFrom($data) : $form;
}
return;
}
public function doUpdateClassListing($data, Form $form) {
$session = $this->getRequest()->getSession();
$session->set("FormData.{$form->getName()}.data", $data);
$class = ClassListings::create($this->owner);
$class->CourseTitle = $data['CourseTitle'];
$class->CourseLocation = $data['CourseLocation'];
$class->ID = $data['ID'];
$class->ClassListingPageID = $this->ID;
$form->saveInto($class);
$class->write();
$session->clear("FormData.{$form->getName()}.data");
$form->sessionMessage('Your class listing has been updated!','good');
$session = $this->getRequest()->getSession();
return $this->redirect($this->Link());
}
}
Thought i post this answer to share in case if others have the same issue i had.
Finally got it working and solved the issue now, have replaced whole the codes for both ClassEditForm and doUpdateClassListing methods, and also created another funcation called Edit:
public function Edit(HTTPRequest $request) {
$id = (int)$request->param('ID');
$class = ClassListings::get()->byID($id);
if (!$class || !$class->exists()) {
return ErrorPage::response_for(404);
}
$form = $this->ClassEditForm($class);
$return = $this->customise(array(
'Title' => 'Edit: ' . $class->CourseTitle,
'Form' => $form,
));
return $return = $return->renderWith(array('ClassListingPage_edit', 'Page'));
}
public function ClassEditForm() {
$id = (int)$this->urlParams['ID'];
$class = ClassListings::get()->byID($id);
$fields = new FieldList(
HiddenField::create('ID')->setValue($id),
TextField::create('CourseTitle', 'Course title:')
->setAttribute('placeholder', 'NZSL Level 1, NZSL 1A')
->setCustomValidationMessage('Please enter the course title field')
->addExtraClass('requiredField CourseTitle'),
TextField::create('CourseLocation','Region:')
->setAttribute('placeholder', 'Enter region')
->setCustomValidationMessage('Please enter the region field')
->addExtraClass('requiredField')
);
$actions = new FieldList(
FormAction::create('doUpdateClassListing')
->setTitle('Update your class listing')
->addExtraClass('btn btn-primary primary')
);
$validator = new RequiredFields([
'CourseTitle',
'CourseLocation'
]);
$form = Form::create($this, 'ClassEditForm', $fields, $actions, $validator);
if ($class) $form->loadDataFrom($class);
return $form;
}
public function doUpdateClassListing($data, Form $form) {
if($data['ID']){
$id = $data['ID'];
$class = ClassListings::get()->byID($id);
} else {
$class = ClassListings::create();
}
$form->saveInto($class);
$id = $class->write();
$form->sessionMessage('Your class listing has been updated!','good');
$this->redirect($this->Link() . "edit/".$id);
}
A couple years ago, I setup a paginated list of News Article Pages that were sorted by year based on a dropdown field selection: SilverStripe - Create pagination based on dropdown selection
I'm now trying to do the same thing but this time, the News Articles are data objects. Otherwise, everything else is the same.
The problem is I cannot get it to work this time and I'm not sure why. I'm following the same steps but when I select a year from the dropdown, I am taken to a 404 page on my site, and, in the Network tab in Chrome, there is a 404 status for the url:
I can see that the year value is not getting passed to the page's code for processing, but I'm not sure why as it did work -- and still works -- on the other SilverStripe site I was working on from the old post I linked to.
Here is the code that I have right now:
;(function($) {
$(function(){
// hide form actions, as we want to trigger form submittal
var newsYearFilterForm = $("#Form_YearFilterForm");
// automatically when dropdown changes
newsYearFilterForm.find(".Actions").hide();
// bind a change event on the dropdown to automatically submit
newsYearFilterForm.on("change", 'select[name="Year"]', function(e){
newsYearFilterForm.submit();
});
// handle pagination clicks
$("body").on("click", "a.pagination", function (e) {
e.preventDefault();
// $("#ArticleList").addClass("loading");
$.get(
$(this).attr("href"),
function(data, status, xhr){
$("#ArticleList").replaceWith($(data));
}
);
return false;
});
});
})(jQuery);
NewsLandingPage.php
<?php
class NewsLandingPage extends Page
{
private static $description = 'News Landing page.';
private static $db = array();
private static $has_one = array();
}
class NewsLandingPage_Controller extends Page_Controller
{
private static $allowed_actions = array(
'renderNewsItem',
'YearFilterForm',
'year',
);
public function init()
{
parent::init();
}
private static $url_handlers = array(
'$NewsItem!' => 'renderNewsItem',
);
public function getAllNews()
{
$newsList = NewsItem::get()->sort('NewsDate', 'DESC');
return new PaginatedList($newsList, $this->getRequest());
}
public function renderNewsItem(SS_HTTPRequest $request)
{
$newsItemName = $request->param('NewsItem');
if ($newsItemName != "") {
$newsItem = NewsItem::get()->filterAny(array(
'NewsItemSlug' => $newsItemName
))->first();
if ($newsItem) {
$arrayData = array(
'NewsItem' => $newsItem,
);
return $this->renderWith(array('NewsArticle', 'Page'), new ArrayData($arrayData));
} else {
return $this->httpError(404);
}
}
}
public function handleYearRequest(SS_HTTPRequest $request)
{
$year = $request->param('ID');
$data = array(
'Year' => $year,
'PaginatedReleases' => $this->PaginatedReleases($year)
);
if ($request->isAjax()) {
// in case of an ajax request, render only the partial template
return $this->renderWith('ArticleList', $data);
} else {
// returning an array will cause the page to render normally
return $data;
}
}
//creates a form to filter through news releases by year
public function YearFilterForm()
{
// get an array of all distinct years
$list = SQLSelect::create()
->addFrom('NewsItem')
->selectField('YEAR("NewsDate")', 'Year')
->setOrderBy('Year', 'DESC')
->execute()->column('Year');
// create an associative array with years as keys & values
$values = array_combine($list, $list);
// our fields just contain the dropdown, which uses the year values
$fields = FieldList::create(array(
DropdownField::create(
'Year',
'',
$values,
$this->getRequest()->param('ID')
)->setHasEmptyDefault(true)
->setEmptyString('Show all')
->setAttribute('data-urlpattern', $this->Link('year') . '/YYYY')
));
$actions = new FieldList(
FormAction::create('doFilter', 'Submit')
);
return Form::create($this, 'YearFilterForm', $fields, $actions);
}
public function year()
{
return $this->handleYearRequest($this->request);
}
public function index()
{
return $this->handleYearRequest($this->request);
}
//redirects to the proper url depending on which year is selected for sorting news
public function doFilter($data, $form)
{
if (empty($data['Year'])) {
return $this->redirect($this->Link());
} else {
return $this->redirect($this->Link('year/' . $data['Year']));
}
}
//created a paginated list of news released by year
public function PaginatedReleases($year = null)
{
$list = NewsItem::get()->sort('NewsDate', 'DESC');
if ($year) {
$list = $list->where(array('YEAR("NewsDate") = ?' => $year));
}
return PaginatedList::create($list, $this->getRequest())->setLimitItems(10);
}
}
NewsItem.php
class NewsItem extends DataObject{
private static $db = array(
'NewsName' => 'Varchar(255)',
'NewsItemSlug' => 'Varchar(250)',
'NewsDate' => 'SS_Datetime',
'NewsLocation' => 'Varchar(255)',
'NewsArticle' => 'HTMLText',
'NewsArticleSummary' => 'HTMLText',
'SortOrder' => 'Int',
);
private static $summary_fields = array(
'NewsName',
'NewsDate',
'NewsItemSlug',
);
private static $has_one = array(
'NewsImage' => 'Image',
'NewsUrlForHomePage' => 'SiteTree',
'Page' => 'Page'
);
public function onBeforeWrite() {
parent::onBeforeWrite();
if ($this->NewsItemSlug == ""){
$linkName = $this::getLinkName();
if ($linkName == ""){
$linkName = str_replace(array(" ",":","%","$","#","#","!","^","&","*","(",")","'",";","<",">","/","?","[","]","{","}","\\","|","`","~","=","+","’",",","."),"-",strtolower(str_replace("&","and",str_replace(".","",$this->NewsName))));
}
$this->NewsItemSlug = $linkName;
} else {
$this->NewsItemSlug = str_replace(array(" ",":","%","$","#","#","!","^","&","*","(",")","'",";","<",">","/","?","[","]","{","}","\\","|","`","~","=","+","’",",","."),"-",strtolower(str_replace("&","and",str_replace(".","",$this->NewsItemSlug))));
}
}
public function getLinkName() {
if ($this->NewsItemSlug != "" && !is_null($this->NewsItemSlug)){
return $this->NewsItemSlug;
}
return str_replace(" ","-",strtolower(str_replace("&","and",$this->NewsName)));
}
public function LinkingMode() {
return Controller::curr()->getRequest()->param('ID') == $this::getLinkName() ? 'current' : 'link';
}
public function Link() {
$newsLandingPage = NewsLandingPage::get()->first();
$linkName = $this::getLinkName();
if ($linkName){
return $newsLandingPage->Link($linkName);
} else {
return false;
}
}
public function canView($member = null){
return true;
}
public function canEdit($member = null) {
return true;
}
public function canCreate($member = null) {
return true;
}
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeFieldFromTab("Root.Main","PageID");
return $fields;
}
}
class NewsItemAdmin extends ModelAdmin {
private static $managed_models = array(
'NewsItem',
);
private static $url_segment = 'NewsItems';
private static $menu_title = 'News';
}
NewsLandingPage.ss
<% include BreadCrumbs %>
<div class="container container-intro-copy" style="background-color:#fff;opacity:1.0;">
<h1 class="intro-copy h1">$H1</h1>
<div class="intro-copy">$Content</div>
$YearFilterForm
<% include NewsList %>
</div>
NewsList.ss
<div id="ArticleList" class="container careers-list">
<div class="row">
<% loop $PaginatedReleases %>
<div class="career-item col-lg-10 col-lg-offset-1 col-md-10 col-md-offset-1 col-sm-10 col-sm-offset-1 col-xs-12 item bio-detail aos-init aos-animate" data-aos="fade-up" data-aos-delay="200" style="margin-top:30px;">
<div class="box-for-resources top-box-style">
<div class="box-resources-left"><img src="$NewsImage.URL" class="image-resources-cmmm"></div>
<div class="box-resources-right">
<h3 class="name left" style="color:#002f65 !important; float: left; clear: both;">$NewsName</h3>
<p class="box-resource-copy text-date"><em>$NewsDate.FormatI18N('%B %d, %Y') ($NewsLocation)</em></p><br />
<div class="careers-copy">$NewsArticleSummary</div><br />
Read Article
</div>
</div>
</div>
<% end_loop %>
</div>
</div>
The news page successfully loads all the articles by default and the links to each article work properly. The dropdown form has the correct list of years based on the range the articles have.
I think you have a conflict with your url_handlers and the actions you're calling on the controller.
'$NewsItem!' => 'renderNewsItem',
The above line matches all actions to renderNewsItem. Eg. yoursite.test/controller/YearFilterForm will also be matched by this…
You should add some static part to your handler that displays a news item. So your url_handlers would be:
'show/$NewsItem!' => 'renderNewsItem',
Then you'd have to adjust your NewsItem->Link method accordingly.
I also suggest you use the URLSegmentFilter to create your slugs… eg.
public function onBeforeWrite() {
parent::onBeforeWrite();
// only rebuild the slug when news-name has changed
if ($this->isChanged('NewsName', DataObject::CHANGE_VALUE)) {
$filter = URLSegmentFilter::create();
$t = $filter->filter($this->NewsName);
// Fallback to generic name if path is empty (= no valid, convertable characters)
if (!$t || $t == '-' || $t == '-1') {
$t = "news-{$this->ID}";
}
$this->NewsItemSlug = $t;
}
}
Somehow, I have to create admin pages of my module. And this is how I am creating tabs
private function createTab()
{
$data = array(
'id_tab' => '',
'id_parent' => 0,
'class_name' => 'AdminSomeMenu',
'module' => $this->name,
'position' => 1, 'active' => 1
);
$res = Db::getInstance()->insert('tab', $data);
$id_tab = Db::getInstance()->Insert_ID();
$lang = (int)Configuration::get('PS_LANG_DEFAULT');
//Define tab multi language data
$data_lang = array(
'id_tab' => $id_tab,
'id_lang' => $lang,
'name' => $this->name
);
// Now insert the tab lang data
$res &= Db::getInstance()->insert('tab_lang', $data_lang);
$arrayTabs = array('TAB1','TAB2','TAB3');
foreach ($arrayTabs as $requiredTabs)
{
$tab = new Tab();
// Need a foreach for the language
$tab->name[$lang] = $this->l($requiredTabs);
$tab->class_name = 'Admin'.$requiredTabs;
$tab->id_parent = $id_tab;
$tab->module = $this->name;
$tab->add();
}
return true;
}
I hope I am going fine.
Once the tabs are created am trying linking with the following code.
<?php
class AdminMenuController extends ModuleAdminController
{
public function __construct()
{
$module = "mymodulename"
Tools::redirectAdmin('index.php?controller=AdminModules&configure='.$module.'&token='.Tools::getAdminTokenLite('AdminModules'));
}
}
This way the controller not found was gone. But I can create only the configure page through such link.
How should I go to achieve personalized page for each tabs.
Ah ! That was a missing parent::__construct(); in controllers causing the problem for not letting tabs behave the way they should.
<?php
class AdminTAB1Controller extends ModuleAdminController
{
public function __construct()
{
parent::__construct();
/* Tools::redirectAdmin('index.php?controller=AdminModules&configure='.$module.'&token='.Tools::getAdminTokenLite('AdminModules')); */
echo "Support page";
/* or further function can be called to load tpl files from views/templates/admin/ */
}
}
Now I have different pages for my tabs in both PS 1.6 and 1.7 !
As an exercise to help me learn about writing custom reports, I've written a very simple custom report to list pages by their page type. I wrote the code based on the standard report cms/code/reports/BrokenLinksReport.php (included as part of the CMS) but am getting an error:
[Notice] Object of class GridState_Data could not be converted to int
I dumped the contents of $data to ensure it was as expected, and it was. What might be causing the problem?
My code is as follows:
class PageListByType extends SS_Report {
function title() {
return "Page List by Type";
}
function description() {
return "List all the pages in the site, along with their page type";
}
public function sourceRecords($params = array(), $sort = null, $limit = null) {
$data = Page::get()->sort($sort);
$returnSet = new ArrayList();
if ($data) foreach ($data as $record) {
$returnSet->push($record);
}
return $returnSet;
}
public function columns() {
return array(
array(
'title'=>_t('PageListByTypeReport.PageName', 'Page name')
),
array(
'className'=>_t('PageListByTypeReport.ClassName', 'Page type')
)
);
}
}
There error is the two dimensional array in the columns function is not being set correctly. The variable name is missing and in the incorrect place for each column.
Either you can set the columns like this:
public function columns() {
return array(
'Title' => array(
'title'=>_t('PageListByTypeReport.PageName', 'Page name')
),
'ClassName' => array(
'title'=>_t('PageListByTypeReport.ClassName', 'Page type')
)
);
}
Or even simpler like this:
public function columns() {
return array(
'Title' => _t('PageListByTypeReport.PageName', 'Page name'),
'ClassName' => _t('PageListByTypeReport.ClassName', 'Page type')
);
}
The current sourceRecords function will work, although we can make this much simpler by just returning the results of Page::get() like this:
public function sourceRecords($params = array(), $sort = null, $limit = null) {
$pages = Page::get()->sort($sort);
return $pages;
}
Here is a working and simplified version of the Report code:
class PageListByType extends SS_Report {
function title() {
return 'Page List by Type';
}
function description() {
return 'List all the pages in the site, along with their page type';
}
public function sourceRecords($params = array(), $sort = null, $limit = null) {
$pages = Page::get()->sort($sort);
return $pages;
}
public function columns() {
return array(
'Title' => _t('PageListByTypeReport.PageName', 'Page name'),
'ClassName' => _t('PageListByTypeReport.ClassName', 'Page type')
);
}
}
For my company I'm working on a custom translation module for Magento.
On the form for translating an existing string, I would like to change the behaviour of the "Save And Continue"-button to a "Save And Next"-button.
With which I mean that instead of still editing thesame string, you get the next one in line.
I have tried to edit the link that is called for the Save And Continue:
Original:
[save-link] + "/back/edit/"
To:
[save-link] + "/back/edit/id/[id]/"
But to no avail. I'm hoping someone can set me in the right direction.
The unchanged code of the edit-form:
<?php
class Phpro_Advancedtranslate_Block_Adminhtml_Edit extends Mage_Adminhtml_Block_Widget_Form_Container
{
public function __construct()
{
parent::__construct();
$this->_objectId = 'id';
$this->_blockGroup = 'advancedtranslate';
$this->_controller = 'adminhtml';
$this->_updateButton('save', 'label', Mage::helper('advancedtranslate')->__('Save Item'));
$this->_updateButton('delete', 'label', Mage::helper('advancedtranslate')->__('Delete Item'));
$this->_addButton('saveandcontinue', array(
'label' => Mage::helper('adminhtml')->__('Save And Next'),
'onclick' => 'saveAndContinueEdit()',
'class' => 'save',
), -100);
$currentId = Mage::getSIngleton('adminhtml/session')->getTranslateId();
$strings = Mage::getModel("advancedtranslate/advancedtranslate")->getCollection();
foreach ($strings as $string) {
$id = $string->getId();
if ($id != $currentId && $id < $nextId) {
$nextId = $id;
}
}
$this->_formScripts[] = "
function toggleEditor() {
if (tinyMCE.getInstanceById('advancedtranslate_content') == null) {
tinyMCE.execCommand('mceAddControl', false, 'advancedtranslate_content');
} else {
tinyMCE.execCommand('mceRemoveControl', false, 'advancedtranslate_content');
}
}
function saveAndContinueEdit(){
editForm.submit($('edit_form').action+'back/edit/');
}
";
}
public function getHeaderText()
{
return Mage::helper('advancedtranslate')->__("Edit Item '%s'", 'test');
}
}
This functionality has to happen in the controller that handles the Post. Set the _redirect to redirect to the next item.