routes not working after upgrade to 3.1 - silverstripe

I upgraded from SS 3.0 to 3.1 today and since updating, the system does not seem to be detecting my routes.yml file in my extensions' _config folders. Here is an example of my myextension/_config/routes.yml file:
---
Name: pusherroutes
After:
- '#rootroutes'
---
Director:
rules:
# handle old 2.4 style urls
'pusher/$Action': 'PusherController'
I have also set my allowed_actions static to whitelist my controller actions:
class PusherController extends Controller {
public static $allowed_actions = array (
'ChatAuth',
'SendMessage',
'NotifyAuth'
);
}
When I navigate to mysite.com/pusher/SendMessage the system is no longer directing me to the action method on the controller. The response being returned in my network tab is the Silverstripe getting started page.

This is because the $Action parameter is being matched twice. When the initial pusher/$Action route is matched, the first two parts of the URL are consumed as part of the match. Control is then passed to your PusherController object. This then tries to match the URL to an action again, but since there is no URL left to parse, it decides there is no action present, so it routes to the index action, which in turn displays the getting started info.
To fix this, just use:
Director:
rules:
'pusher': 'PusherController'
and rely on the action matching being done when control is handed to PusherController.

Related

Pages 404 when created via the django-cms API

I'm building a system which creates a site object for clients added to the system and off the back of that creates some basic pages for the site.
Pages are created with published=True however after browsing to them via pages admin or directly to URLs the result is a 404.
To fix the 404 I've noticed simply saving the page's settings then makes the pages load. I therefore suspect there's further actions which are triggered by the CMS admin, whether that be through the form used or signals.
The Code
The creation of pages comes from a post_save signal;
#receiver(post_save, sender=Client)
def create_site_on_client_creation(instance, **kwargs):
"""
When a Client is created, create a Site object with the client slug as a
sub-domain to the main sites base domain. Also update the site object
if the client slug changes.
"""
site = instance.site or Site()
if not instance.site_id:
instance.site = site
instance.save(update_fields=['site'])
create_initial_pages(
site, extra_pages=[
'Privacy Policy',
'Terms of Use'
]
)
The function then creating the content is;
def create_initial_pages(site, **kwargs):
""" Create the initial pages required for a site """
# Create a CMS "home" page on the initial site creation
created_root = False
root_page = Page.objects.on_site(site).drafts().filter(
title_set__language=settings.LANGUAGE_CODE,
title_set__title=HOME_TITLE,
is_home=True
).first()
if not root_page:
created_root = True
root_page = create_page(
title=HOME_TITLE,
template=TEMPLATE_INHERITANCE_MAGIC,
language=settings.LANGUAGE_CODE,
site=site,
published=True
)
if 'extra_pages' in kwargs:
for page_title in kwargs['extra_pages']:
# Check if we can find
existing = Page.objects.on_site(site).drafts().filter(
title_set__language=settings.LANGUAGE_CODE,
title_set__title=page_title,
).exists()
if not existing:
create_page(
title=page_title,
template=TEMPLATE_INHERITANCE_MAGIC,
language=settings.LANGUAGE_CODE,
site=site,
parent=root_page,
published=True
)
if created_root:
root_page.is_home = True
root_page.save()
I think what's happening here is that you are making changes to the root page after it is published which is therefore making changes to the draft:
if created_root:
root_page.is_home = True
root_page.save()
# You may need to publish here
root_page.publish(settings.LANGUAGE_CODE)
The API always fires the publish action on creation if the published kwarg is added: https://github.com/divio/django-cms/blob/develop/cms/api.py#L203
The tests are a good place to see how the api is used by code, finding a test that creates a page tree should help you to see if you have missed a specific setting that causes your page to not be seen: https://github.com/divio/django-cms/blob/develop/cms/tests/test_multilingual.py#L85
Also be sure that your new site has a language configuration in the settings file, it's possible that the admin is confused by the page and language combination and when you "save the page settings" you are actually creating an entry in your current language: http://docs.django-cms.org/en/latest/reference/configuration.html#internationalisation-and-localisation-i18n-and-l10n
I would advise rolling / commenting your code back to just the one root page and get that working as you expect, then when that is working you can expand upon it with more pages.
Try just calling the publish method (with a language) on the page after the call to create_page. You'll need to get the page from the create_page method. e.g...
my_page = create_page(
title=page_title,
template=TEMPLATE_INHERITANCE_MAGIC,
language=settings.LANGUAGE_CODE,
site=site,
parent=root_page,
published=True
)
my_page.publish("en")

Previous page location on IronRouter

Is there a way to get the previous page location before going to the next page in IronRouter?
Is there an event I can use to fetch this information?
Thanks in advance.
Since Iron Router uses the usual History API, you can just use the plain JS method:
history.go(-1);
or
history.back();
Edit: or to check the previous path without following it:
document.referrer;
You can achieve the behavior you want by using hooks.
// onStop hook is executed whenever we LEAVE a route
Router.onStop(function(){
// register the previous route location in a session variable
Session.set("previousLocationPath",this.location.path);
});
// onBeforeAction is executed before actually going to a new route
Router.onBeforeAction(function(){
// fetch the previous route
var previousLocationPath=Session.get("previousLocationPath");
// if we're coming from the home route, redirect to contact
// this is silly, just an example
if(previousLocationPath=="/"){
this.redirect("contact");
}
// else continue to the regular route we were heading to
this.next();
});
EDIT : this is using iron:router#1.0.0-pre1
Apologies for bumping an old thread but good to keep these things up to date saimeunt's answer above is now deprecated as this.location.path no longer exists in Iron Router so should resemble something like the below:
Router.onStop(function(){
Session.set("previousLocationPath",this.originalUrl || this.url);
});
Or if you have session JSON installed (see Session JSON)
Router.onStop(function(){
Session.setJSON("previousLocationPath",{originalUrl:this.originalUrl, params:{hash:this.params.hash, query:this.params.query}});
});
Only caveats with thisis that first page will always populate url fields (this.url and this.originalUrl there seems to be no difference between them) with full url (http://...) whilst every subsequent page only logs the relative domain i.e. /home without the root url unsure if this is intended behaviour or not from IR but it is currently a helpful way of determining if this was a first page load or not

Redirect service requests to another server

I've created a filter so that I can intercept all controller actions and forward the request to a different server.
I need to temporarily redirect based on user-agent.
So I have the following in http://www.mysite1.com
class DealsFilters {
def filters = {
all(controller: '*', action: '*') {
before = {
if (someConditionHere)
{
redirect(url:"http://www.mysite2/")
return
}
}
}
}
What I am wondering is will the request and all it params be correctly passed to mysite2?
i.e. mysite1 acts as a service and receives requests to get user data, update deals, add new users, etc...
mysite2 is a new version of mysite1 (mysite1 will be decommisioned after mysite2 has been tested).
Is it as simple as a redirect?
Thanks
I'm using grails 1.3.7 as pointed out to lucke84 (I should have stated this in original question).
Found following link gave me most of the answer:
Best practice for redirecting from one web domain to another in Grails?
redirect(url:"http://localhost:8080${request.forwardURI}?${request.queryString}",params:params)
The documentation says that if you perform a redirect with the url parameter, your url should contain all the information needed to send the new request.
I guess you should recreate your url, more or less like this:
redirect(base: 'http://www.mysite2/', controller: params.controller, action: params.action, params: params)
Not tested, but it should work for most of the cases. Let me know :)

Webforms routing with ASP.NET4

I need to provide a pageId on every URL however I don't need the user to see this pageId. For example
http://{domain}/{product-name}/{product-id}/{pageid} <-- I don't want to provide this
I have in my Global.asax:
routes.MapPageRoute("route-name", "path/{productName}/{product-id}", "~/ProductPage.aspx");
Is there some way to configure this route so it has a "hard coded" parameter page id for example something like this ---
routes.MapPageRoute("route-name", "path/{productName}/{product-id}", "~/ProductPage.aspx?pageid=1");
Is there some way to configure this route so it has a "hard coded"
parameter page id
Why yes... yes there is.
MapPageRoute has an overload that accepts a set of defaults for route values.
//Create route and set default values
routes.MapPageRoute(
"route-name",
"path/{productName}/{product-id}",
"~/ProductPage.aspx",
false,
new RouteValueDictionary{
{"product-id", 1}
});
So now if you hit this route without a "product-id" specified, it will always default to 1

restful-like CRUD operation url pattern for nested model

Generally,the CRUD operation url pattern for model can be like this(Suppose the Model is Post):
new: /posts/new(get)
create:/posts/(post)
edit:/posts/edit(get)
update:/posts/1(put)
get:/posts/1(get)
However if there is a nested model "Comment".
And the association of the "Post" and "Comment" is one-many.
So what should the CURD operation url pattern like for comments ?
new: /posts/1/comments/new or /comments/new
create:?
edit:?
update:?
.......
What is the best practice?
Update:
It seems that the url for comment should be like this:
Get one comment for one post: /posts/1/comments/1
create: /posts/1/comments
update: /posts/1/comments/1
delete: /posts/1/comments/1
Now I am confused with the update and delete operation.
For update and delete: /posts/1/comments/1
SInce the comment id is specified,so I wonder if the /posts/1 inside the url is necessary?
I think the key is whether a comment is "contained" by the post resource. Remember that RESTful urls should be permalinks so under all of your scenarios, the end point to a specific comment(s) must not change. It sounds like it's containted so the url pattern can have the comment nested within the post. If that's not the case (e.g. a comment could move to another post which if nested would change the url) then you want a more flat structure with /comment/{id} urls referenced by the post resource).
The key is if it's a RESTful "Hypermedia API" then like the web it constantly links to the nested or referenced resources. It doesn't rely on the client necessarily understanding the REST pattern or special knowledge as to what end point holds the referenced or contained
resource.
http://blog.steveklabnik.com/posts/2012-02-23-rest-is-over
If a 'comment' is the resource(s) under a 'post' resource:
([httpVerb] /url)
get a post:
[get] /posts/{id}
body has a couple options - either it contains the full deep comments array
(depends on how much data, chat pattern)
{
id:xxx,
title:my post,
comments: [...]
}
... or it just contains the post resource with a url reference to the comments e.g.
{
id: xxx,
title: my post,
commentsUrl: /posts/xxx/comments
}
could also have an option like this (or other options to control depth):
[get] /posts/{id}?deep=true
get a collection of comments within a post:
[get] /posts/{id}/comments
returns 200 and an array of comments in the response body
create a comment for a post:
[post] /posts/{id}/comments
body contains json object to create
returns a 201 created
edit a comment under post:
[patch|post] /posts/{id}/comments/{id}
body contains json object with subset of fields/data to update
returns a 200
replace a post:
[put] /posts/{id}/comment/{id}
body contains json object to *replace*
returns a 200
If you have tons of comments per post, you could also consider a paging pattern:
{
id: xxx,
title: myPost,
pages:6,
commentsUrl:/posts/xxx/comments/page/1
}
then:
/posts/{id}/comments/pages/{pageNo}
{
nextPage: /posts/xxx/comments/2,
pages:7,
comments:
[ { ...,...,}]
}
each page would reference the next page, the page count and an array of comments for that page. If you went with a paging pattern then each comment in the array would have a reference url to the individual comment.
If you find yourself putting an action in the url, you're probably doing something wrong. Good read: http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http
You want to use the canonical URL for delete/update, which would be the complete one. More importantly, you shouldn't be exposing your Primary Key values which come from the Database as ids in the public api that is your restful URL space. These values could change in the future based on a database update, restore from backup, or anything else, depending on vendor. And you don't want an infrastructure change making all your URLs invalid if you can help it.
If you're using /posts/{postnum}/comments/{commentnum}, you can number the public comment ids starting from 1 for every post, making for shorter, nicer URLs.

Resources