Wrong 'View live' url in Wagtail admin message after page creation when using id as slug - wagtail

I am using a page model 'EventPage', which shall have a slug based on the id of the page. To achieve this, I have modified the full_clean method like this (similar to this question):
class EventPage(Page):
...
def full_clean(self, *args, **kwargs):
super().full_clean(*args, **kwargs)
# set slug to id, if already existing
if self.id is not None:
self.slug = str(self.id)
This seems to work fine in principle. However, after the page is published,the Wagtail admin/pages view shows a message box at the top ('Page ... created and published') with a View live button that links to the wrong url (i.e. using the default slug created from the page title).
In the list of pages below that, the just created page's own View live and Add child page links show the correct page url using the page id. It is just the message box's View live url at the top that needs to be corrected. Here's a screenshot:
How can I get the correct link in the message box at the top as well?
In case it matters, I am currently using Wagtail 2.9, Django 2.2.9 and Python 3.6.
I guess my problem has something to do with the fact that the page id is not known until the page is saved for the first time, and the View live link in the message box somehow uses an initial page.url which is overridden later on.
Any ideas how to solve this?

You could probably achieve this by using Hooks in Wagtail. Hooks are typically used to customize the view-level behaviour of the Wagtail admin and front end. I would suggest overwriting the button functionality this way, or possibly removing the existing button and adding your own new one in. More info in the docs: https://docs.wagtail.io/en/latest/reference/hooks.html

For anyone ending up here, the solution was given in the accepted answer to a similar question here: Wagtail 'View live' button provides wrong url after page creation while using id as slug.
In short: use Django signals instead of hooks, i.e. here a the post_save signal for EventPage.
For documentation purposes, I'm repeating here the solution from the linked question, slightly adjusted. All credits and thanks go to gasman.
Create following signal in the calendar app, calendar/signals.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import EventPage
#receiver(post_save)
def set_number_and_slug_after_event_page_created(sender, instance, created, **kwargs):
if issubclass(sender, EventPage) and created:
page = instance
page.number = page.slug = str(page.id)
page.save()
new_revision = page.save_revision()
if page.live:
new_revision.publish()
And, if not yet done, register signals in the corresponding calendar/apps.py config:
from django.apps import AppConfig
class CalendarConfig(AppConfig):
name = 'calendar'
def ready(self):
from . import signals
This worked like a charm for me.

Related

Redirect to an external page from a specific article - Drupal 7

I'm very new with drupal.
I have a blog, List of posts.
I need redirect a specific post to a specific external webpage, Is this possible ?
By example: I need to redirect the post (with the arrow red) to www.facebook.com/somePage. (if it's possible in other tab)
But the other posts should be redirect normally to its internal pages.
In this moment I have this configuration:
Any suggestions?
Method 1
Add a URL Redirect pointing from the 'node/#' URL to your external URL.
Render View as you have it now, and when you click the article Title it'll send you to the external page.
Method 2
Install the 'Link' module.
Add a 'Link' field to your Content Type. Configure the link field to open links in a new window, and so on.
Add that new field to your View. Set it to not appear in display, so it can be used later. Move this link above your Title Field in the View, because the order matters.
Edit the Title field settings and use the Link field value as a Token for the Title field.

Change href of Live Status link in Wagtail Admin

I made a one page scrolling site using wagtail. I have a homepage model and everything else is child pages such as about us, events, etc. On the admin page it creates a slug with the title of the child page which is what the live status link uses. For example it is <a href="/about-us/">. Is there a way to change the live status link at all?
The page URL is constructed by calling the get_url_parts method on the page model - by overriding this method, you can customise the resulting URL:
http://docs.wagtail.io/en/v1.13.1/reference/pages/model_reference.html#wagtail.wagtailcore.models.Page.get_url_parts
http://docs.wagtail.io/en/v1.13.1/topics/pages.html#customising-url-patterns-for-a-page-model
Normally, if you're overriding get_url_parts, you'd want to make a corresponding customisation to your site's URL routing behaviour, to ensure that the page is actually available at the URL in question; this can be done with RoutablePageMixin. In this case, though, it sounds like you're just using these subpages as placeholders for page content, and aren't bothered about them being accessible at their own URL - so you can get away with simply returning '/' as the page path:
class MyChildPage(Page):
# ...
def get_url_parts(self, *args, **kwargs):
url_parts = super(MyChildPage, self).get_url_parts(*args, **kwargs)
if url_parts is None:
# in this case, the page doesn't have a well-defined URL in the first place -
# for example, it's been created at the top level of the page tree
# and hasn't been associated with a site record
return None
site_id, root_url, page_path = url_parts
# return '/' in place of the real page path
return (site_id, root_url, '/')
I ran into this question when using Wagtail as a headless CMS for a Gatsby site. As I do use the URLs generated by Wagtail to structure the Gatsby site, redefining the get_url_parts method does not help me. I do want to keep the page URL as is and use a different host when clicking on the "View Live" or "Live" button in the admin.
I found that overriding the serve() method of the pages that you want to be served by a different host allows you to do just that.
from django.shortcuts import redirect
class BlogPage(Page):
...
def serve(self, request):
site_id, site_root, relative_page_url = self.get_url_parts(request)
return redirect('http://localhost:8001' + relative_page_url)
In the code above, the serve method of the BlogPage model is overridden to use the request and the get_url_parts method to extract the requested relative_page_url (that is relative to the site root -- in development that would be http://localhost). Since my frontend keeps the same URL structure as the Wagtail site, I just use the extracted relative_page_url and combine it with the host of the frontend (in this development case localhost:8001) and return a redirect response (generated with django.shortcuts.redirect) to that full URL.
It probably makes sense to turn this override into a mixin and a setting, so it does not have to be defined on every page model and the redirect host can be differentiated between environments.
You could also use the same logic of overriding the serve() method to redirect to any other location you may choose.

CakePHP - can't create more than one method (admin_index) in plugin

I'm a newbie in CakePHP, please have patience with me :)
So, I'm trying to create a plugin called References. I've baked "plugin's core" through cake's console. Then I've created ReferencesController class that extends ReferencesAppController and Reference class (model) that extends ReferencesAppModel. My next step was creating action admin_index (just code to save form), it's view and a little bit validation in Reference model. To my problem, I'm unable to create any other action, ex. admin_add. When I do (I create new action and I add it's view), then I try to access it through the URL (localhost/my_project/admin/references/add), and there comes the message "Error: References.AddController could not be found.". I am not sure, what I do wrong, I don't want to create another controller, just action. Thank you
Because only the plugin index action (when plugin and controller have the same name) is directly routed.
For all others you need to verbosly add the plugin name and the controller name to the url:
/my_project/admin/references/references/add
If you had created a link to this action, the routing would have shown you that.

Backbone.js: How to utilize router.navigate to manipulate browser history?

I am writing something like a registration process containing several steps, and I want to make it a single-page like system so after some studying Backbone.js is my choice.
Every time the user completes the current step they will click on a NEXT button I create and I use the router.navigate method to update the url, as well as loading the content of the next page and doing some fancy transition with javascript.
Result is, URL is updated which the page is not refreshed, giving a smooth user experience. However, when the user clicks on the back button of the browser, the URL gets updated to that of a previous step, but the content stays the same. My question is through what way I can capture such an event and currently load the content of the previous step and present that to the user? Or even better, can I rely on browser cache to load that previously loaded page?
EDIT: in particular, I'm trying something like mentioned in this article.
You should not use route.navigate but let the router decide which form to display based on the current route.
exemple :
a link in your current form of the registration process :
<a href="#form/2" ...
in the router definition :
routes:{
"form/:formNumber" : "gotoForm"
},
gotoForm:function(formNumber){
// the code to display the correct form for the current url based on formNumber
}
and then use Backbone.history.start() to bootstrap routing

Custom Button or Link to a Visualforce page with a custom controller

I have a Visualforce page using a custom controller that is used to edit multiple records under an opportunity.
I'd like to create a custom button or link from Opportunities to this Visualforce page.
Currently the link looks like:
/apex/ExamplePage?oppId={!Opportunity.Id}
This works fine in the development sandbox, but when it is deployed as part of a managed package the link breaks as the page reference doesn't have the namespace prefix.
I found the post Managed Package Redirecting Problem on the Force.com Discussion Boards which implied it should be possible to use $Page to reference the Visualforce page in the URL. E.g.
{!URLFOR($Page.MyExamplePage,'',[objectId = campaign.id])}
But doing so only gives me the syntax error:
Error: Field $Page.MyExamplePage does not exist. Check spelling.
There is another part to the post that suggests using an Apex class and Execute Javascript to work around it. But it appears to me that this has just moved the namespace issue into the Javascript.
How can I safely reference the Visualforce page to work both inside and outside a managed package?
Best to do this from an Apex PageReference return value. Something like this will work:
public PageReference returnPage()
{
return Page.MyExamplePage;
}
Then call this from Visualforce:
<apex:commandButton value="Go To New Page" action="{!returnPage}"/>
The Apex Page call will handle the translation for you.
[EDIT]
Create a bare bones Visualforce page like this:
<apex:page standardController="Opportunity" extensions="TheController" action="{!returnPage}"/>
Add the above returnPage() method to a new TheController (or whatever) class. It doesn't even need a constructor. The class can look like this:
public TheController
{
public PageReference returnPage()
{
return Page.MyExamplePage;
}
}
Then from the Opportunity settings page go to Buttons and Links and create a new custom Visualforce button selecting the new page you just created.
That should do it.
It occurred to me that one less than ideal option would be to create two custom buttons in each case. One with the managed package namespace and one without.
When building the package the correct custom button could be selected.
One issue with this approach is the need to maintain two custom buttons.
It seems the answer is simply /apex/package__Page as provided here by #zachelrath. I can confirm this works in managed packages in production orgs as well as in development.
The post on the developer boards that you've linked to shows the following javascript being used for the button:
{!REQUIRESCRIPT("/soap/ajax/15.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/15.0/apex.js")}
var pageUrl = sforce.apex.execute("mynamespace.PageUrl", "getPageUrl", {objectId:"{!Campaign.Id}"});
window.location.href = pageUrl;
i.e. they're using javascript to call a webservice method in the class they've defined in order to get the page reference. Doing this would allow you to get the URL of the page in apex, where the managed package won't play an impacting part.
That said, the first parameter is the fully-qualified class name, so you could probably check the return value for an error (I don't know the error return value, so I'm assuming it's null here):
// try the namespace first
var pageUrl = sforce.apex.execute("mynamespace.myClass", "getPageUrl", {objectId:"{!Campaign.Id}"});
if (pageUrl == null)
{
pageUrl = sforce.apex.execute("myClass", "getPageUrl", {objectId:"{!Campaign.Id}"});
}
window.location.href = pageUrl;
Obviously you need to check what happens when sforce.apex.execute() fails, and you'll likely want some more error handling.

Resources