silverstripe dopublish function for ModelAdmin managed Pages - content-management-system

in the silverstripe backend I´m managing certain PageTypes via a ModelAdmin. That works fine so far, the only thing i can´t figure out is how to make a Page being "Published" when saving it.
Thats my code:
class ProjectPage extends Page {
public function onAfterWrite() {
$this->doPublish();
parent::onAfterWrite();
}
}
At the moment I can still see the ModelAdmin-created Pages in the Sitetree and I can see they are in Draft Mode. If I use the code above I get this error:
Maximum execution time of 30 seconds exceeded in
.../framework/model/DataList.php
Many thx,
Florian

the reason why you get "Maximum execution time exceeded" is because $this->doPublish(); calls $this->write(); which then calls $this->onAfterWrite();. And there you have your endless loop.
So doing this in onAfterWrite() or write() doesn't really work
you should just display the save & publish button instead of the save button
But I guess its easier said than done.
Well adding a button is actually just a few lines, but we also need a provide the functions that do what the button says it does.
This sounds like the perfect call for creating a new Module that allows proper handling of Pages in model admin. I have done this in SS2.4, and I have a pretty good idea of how to do it in SS3, but no time this week, poke me on the silverstripe irc channel on the weekend, maybe I have time on the weekend.

I found the same need/lack and I built a workaround that seems to work for me, maybe it can be useful.
public function onAfterWrite()
{
if(!$this->isPublished() || $this->getIsModifiedOnStage())
{
$this->publish('Stage', 'Live');
Controller::curr()->redirectBack();
}
parent::onAfterWrite();
}

Create a class that extends ModelAdmin and define an updateEditForm function to add a publish button to the actions in the GridFieldDetailForm component of the GridField.
public function updateEditForm($form) {
if ( ! singleton($this->owner->modelClass)->hasExtension('Versioned') ) return;
$gridField = $form->Fields()->fieldByName($this->owner->modelClass);
$gridField->getConfig()->getComponentByType('GridFieldDetailForm')->setItemEditFormCallback(function ($form) {
$form->Actions()->push(FormAction::create('doPublish', 'Save & Publish'));
});
}
Then create a class that extends GridFieldDetailForm_ItemRequest to provide an action handler for your publish button.
public function doPublish($data, $form) {
$return = $this->owner->doSave($data, $form);
$this->owner->record->publish('Stage', 'Live');
return $return;
}
Make sure the extensions are applied and you're done.
Or, you could just grab all the code you need from GitHub.

Related

How to use Code org creating simple program without using a bunch of OnEvents

using code.org i need to be able to have a user click on a button that takes them to another screen without using OnEvent(s) constantly, can anyone help me out? Newb here. Thank you for your time.
You can create a function with a value for button and string and put a callback inside
function event(button,input_type, screen) {
onEvent(button, input_type, function() {
setScreen(screen);
});
}

condition not working sometimes with gwt

I'm having a rare issue in my code, I have a method that makes a very simple validation based on a string variable:
private void showNextStep(String psCondition,String poStepName){
int liCurrentStep=Integer.valueOf(poStepName);
String lsNextTrueStep=moSteps[liCurrentStep][4];
String liNextFalseStep=moSteps[liCurrentStep][5];
if ("Yes".equals(psCondition)){
moFrmStepsContainer.getField(liNextFalseStep).hide();
moFrmStepsContainer.getField(lsNextTrueStep).show();
}else{
moFrmStepsContainer.getField(liNextFalseStep).show();
moFrmStepsContainer.getField(lsNextTrueStep).hide();
}
}
Now, here is the ticky part: if I execute the application without debugging mode, it does the validation right all the time, how ever if don't it always goes to the else block (or at least I think) I tried to use JS alerts (I have a class that calls JS methods) to debug manually and check the valors of the variables; the valors where all right and the validation was also good. This means that only debugging or putting alerts before at the beggining of the IF block it does the validation right, otherwise it always goes to the ELSE, what do you think it could be?
It might be worth mentioning this is a web application made in netbeans 6.9, using the framework GWT 2.1. This application runs in firefox 25.0.1
Thank you!
UPDATE
Here is the code of the event that calls my method
final ComboBoxItem loYesNo=new ComboBoxItem("cmbYesNo" + moSteps[liStepIndex][0],"");
loYesNo.setValueMap("Yes","No");
loYesNo.setVisible(false);
loYesNo.setAttribute("parent", liStepIndex);
loYesNo.addChangedHandler(new ChangedHandler() {
public void onChanged(ChangedEvent poEvent){
String lsStepName=loYesNo.getName();
FormItem loItem=moFrmStepsContainer.getField(lsStepName);
String liStepNumber=String.valueOf(loItem.getAttributeAsInt("parent"));
showNextStep((String)poEvent.getItem().getValue(),liStepNumber);
}
});

Handle Window close event

I'm trying to handle the event when the close button of a Window is clicked:
// View Code
#Override
public void attachWindowListener(WindowListener listener) {
window.addWindowListener(listener);
}
// Presenter code
view.attachWindowListener(new WindowListener(){
public void windowHide(WindowEvent we) {
GWT.log("Window Event - Processing fields");
processFields();
}
});
However, the windowHide function seems to be not executed since I can't see the log I placed there.
How to properly handle that event?
How about
Window.addCloseHandler(
new CloseHandler<Window>()
{
public void onClose( CloseEvent<Window> windowCloseEvent )
{
// Do your worst here
}
} );
I usually put this in onModuleLoad() in my EntryPoint class.
Cheers,
Based on the information provided I would guess that either a.) the events you think are firing do not fire for the Window component (even if it seems like they should) or b.) the events are firing but in a different order than you expect.
For example, it's possible that a BrowserEvent or some other event is firing first as the window is being closed and the Window object's WindowEvent never fires. According to the API docs for GXT 2.x, the WindowEvent will fire on hide and deactivate but it does not specify that it fires on close. The GXT 3.0.x API doc is less clear on this point but I would assume the same behavior. Unfortunately Sencha does not provide good documentation on what events fire for a given component and in what order.
With that said, I have had some luck working through similar issues to this by using a debug class which outputs all the events on a component to which it is attached. This may shed some light on which events are firing and their order of execution, and you may find an optimal event to which you can attach your processFields() method.
For a good example of a debugger class, see this answer from a related post: https://stackoverflow.com/a/2891746/460638. It also includes an example of how to attach the debugger to your component.
API Doc for Window, GXT 2.x: http://dev.sencha.com/deploy/gxt-2.2.5/docs/api/com/extjs/gxt/ui/client/widget/Window.html
API Doc for Window, GXT 3.0.x: http://dev.sencha.com/deploy/gxt-3.0.0/javadoc/gxt/com/sencha/gxt/widget/core/client/Window.html
This worked:
window.addListener(Events.Hide, new Listener<ComponentEvent>() {
#Override
public void handleEvent(ComponentEvent be) {
// Do stuff
}
});

How to do code split in a typical GWT mvp project?

My app is a typical GWT application, it has a navigation bar, when click one bar item, the place will be changed and the app will find corresponding activity for that place(done in MasterActivityMapper), then call activity.start(). During the init of activities, the corresponding views were also created. When my app first starts, it will by default select the first navigation bar item.
public class MasterActivitiesMapper implements Activity
{
public Activity getActivity( Place place )
{
if(place equals place1)
{
Activity1 a1= new Activity1();
return a1;
}
else if(place equals place2)
{
Activity1 a2= new Activity2();
return a2;
}
else if(...)
{
....
}
}
}
My app works well except that the initial download size is too large, it will takes 10 seconds to load. I have to use code split the solve this problem.
I read the doc
https://developers.google.com/web-toolkit/doc/latest/DevGuideCodeSplitting
However still can not figure out where/how to split the code, could anybody has experience with this help?
Activity.start, being asynchronous by nature, seems like a good place to put a GWT.runAsync.
There's also the possibility of returning an activity async proxy from your ActivityMapper. See http://code.google.com/p/google-web-toolkit/issues/detail?id=5129 and https://gist.github.com/3038878 for some sample code.

pause viewmodel process for user input

I've been looking at a view examples of the typical "raise dialog from viewmodel" problem, noting 3 main solutions:
use attached behaviors
use a mediator pattern
use a service
I'm getting a bit bogged down though and struggling to find a solution that easily fits into my problem space - which is a very simple file copy problem:
My viewmodel is processing a loop (copying a list of files)
When a file already exists at the destination I need to raise a modal dialog to get confirmation to replace
The vm needs to wait for and receive confirmation before continuing
The "modal dialog" is actually not a new window but a hidden overlay in my MainWindow, as per http://www.codeproject.com/KB/WPF/wpfmodaldialog.aspx (thanks Ronald!)
I'm mostly there but the biggest struggles I have are:
- how to pause the loop in the viewmodel while it waits for input
- how to get input back to the viewmodel within the loop so it can carry on
So far I'm leaning towards the service solution because it seems a direct method call with a return that the vm must wait for. However, it does mean the service needs to tie directly to the view in order to make an element visible?
If anyone can post some simple code that deals directly with this problem I (and the net) would be very happy! Thanks!
For example, you have a service called IDialogService with the following interface:
public interface IDialogService
{
bool ConfirmAction(string title, string confirmationText);
}
As you mentioned, in order for the service to be able to show the actual dialog it needs to have a reference to the view that will show the actual overlay element. But instead of directly referencing the view I prefer to reference it via an interface. Lets call it ICanShowDialog and it will have the following members:
public interface ICanShowDialog
{
void ShowDialog(object dialogContent);
void HideDialog();
}
This interface will be implemented by your view that owns the dialog overlay (e.g. your main window).
Now the interesting part: suspending the code execution while the dialog is shown. First of all, I would recommend you not to use overlay elements but use usual windows if possible. Then you will not have that problem. You can style the dialog window so it will look just like the overlay element.
Anyway, if you still want to use overlay elements then you can do the following trick to suspend the code execution:
Here is pseudo code of the ConfirmAction method of the IDialogService inteface:
public bool ConfirmAction(string title, string confirmationText)
{
ConfirmationDialogView dialogView = new ConfirmationDialogView(title, confirmationText);
DialogShower.ShowDialog(dialogView); // DialogShower is of type ICanShowDialog
while (!dialogView.ResultAvailable)
{
DispatcherUtils.DoEvents();
}
DialogShower.HideDialog();
return dialogView.Result;
}
Here is the code of DispatcherUtils.DoEvents() (that was taken from here: http://dedjo.blogspot.com/2007/08/how-to-doevents-in-wpf.html):
public static class DispatcherUtils
{
public static void DoEvents()
{
DispatcherFrame f = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(
DispatcherPriority.Background,
(SendOrPostCallback)delegate(object arg) {
DispatcherFrame fr = arg as DispatcherFrame;
fr.Continue=True;
}, f);
Dispatcher.PushFrame(frame);
}
}
But I must warn you. Using DoEvents can result in some subtle bugs caused by inner dispatcher loops.
As an alternative to suspending the code execution while a dialog is shown you can use callbacks:
public interface IDialogService
{
void ConfirmAction(string title, string confirmationText, Action<bool> dialogResultCallback);
}
But it will not be so convenient to use.