First of all thank you for taking your time takling this difficult topic.
Goal:
My URL structure is like facebook/twitter: site.com/Username
To let the user set the username, I need to check if a path of a route matches a username.
Possibilities & problems:
If somebody is interested, the possible solutions I came up with:
1.) Using getRouteCollection()
Problem: This method shouldn't be used because it rebuilds the route cache and kills performance (see https://github.com/symfony/symfony-docs/issues/6710)
2.) Using a blacklist as RegExp in the User (for Username) Entity to blacklist all sites like "settings", "about", "login", "register"...
* #Assert\Regex(pattern="/^(?!register)(?!login)...
Problem: This is nearly guaranteed to explode because sometimes a URL is forgotten to add into this RegExp
3.) Using CURL to check if the site.com/username results in a 404
Problem: This seems to be a "hack" for me and unprofessional.
Question:
Whats the professional, secure way of checking if a route doesn't already exists, so that the username can be set and is guaranteed to be "free" (via absolute path like site.com/Username and no other route is in the way)?
By example something like $app->hasURL('/username')
Add: use \Symfony\Component\RoutingException\ResourceNotFoundException;
Then, where $router is the routing service, you can attempt to match the url to a route. If it doesn't match anything it will throw an exception, but if you have a default catch-all route it will always match something so you would need to just detect if it matches the catch-all instead:
$routeIsAvailable = false;
try {
$route = $router->match( '/' . $username );
// If you are using a catch-all route to load profiles
if ( $route[ '_route' ] === 'name_of_catch_all_route' )
$routeIsAvailable = true;
} catch ( ResourceNotFoundException ) {
// If the url didn't match any route at all
$routeIsAvailable = true;
}
Make sure that you validate $username first so that it doesn't have any characters like ? or /. You can also urlencode it, but you probably don't want your user's profile urls to have encoded characters in them, so it's better to just restrict the valid usernames.
Related
I create a dynamic route pages/post/[postname].tsx . and when I send name to the dynamic route the url shows name with url-encode (%20,%E2,...)
I want to show name of the url with dash between words. like below url
https://stackoverflow.com/questions/68041539/using-dash-in-the-dynamic-route-name-in-nuxt-js
how can I do this?
I've used the getStaticPaths method and pass it an object of slugs before. This has worked for me when dealing with a headless CMS'.
export async function getStaticPaths() {
// Hit API to get posts as JSON
const posts = await getPosts()
// Map a new object with just the slugs
const paths = posts.map((post) => {
return { params: { slug: post.slug } }
})
// Return paths
return {
paths: paths,
fallback: true
}
}
I think I understand the problem, you're trying to use a set of strings with spaces e.g "the fundamentals of starting web development" as path param to achieve something like this https://www.geniushawlah.xyz/the-fundamentals-of-starting-web-development. That will most likely convert your spaces to %20 which is normal. I would have advised you to first use replace() method to change all spaces to hyphen before passing it as param but the replace() method only changes the first space and leave the rest. There are other ways to get rid of the spaces programmatically but may be stressful and not worth it, so I'll advise you use an hyphenated set of strings by default.
If not, try to use a for-loop with the replace() method to change all spaces to hyphens, then pass it as param.
I have a Dashboard Application in Webflow and I use Memberstack for User and Permissions.
Now the Issue I have is the following: I'm creating a new Memberstack User with Zapier, then I'm logging into Webflow with the new created User and it opens the Dashboard page. The Issue is, that I want, if a user loggs in for the first time, to redirect them to another page called "Onboarding".
My approach was to solve it in Memberstack, but it doesn't seem to work when I create the User with Zapier. Then I tried to find a solution in Webflow, to create a redirect if a Variable in the CMS Item is not set (For example "onboarding" = false).
Can someone help me to make this work?
Let me know if this works! I adapted some code I had which also will add the first time the member logged in to their Memberstack metadata.
Try adding this code before the site-wide </ body > tag:
<script>
MemberStack.onReady.then(async function(member) {
// if member is logged in
if(member.loggedIn){
var first_logged_in_timestamp = member["first-logged-in-timestamp"];
if(first_logged_in_timestamp === undefined || first_logged_in_timestamp == null || first_logged_in_timestamp.length < 1)
{ // set first-logged-in-timestamp
var first_logged_in_timestamp = timestamp;
member.updateProfile({
"first-logged-in-timestamp": timestamp,
}, false)
}
window.location.href='https://RedirectHereIfFirstLogIn.com';
}
else{
window.location.href='https://RedirectHereIfNotLoggedIn.com';
}
}
</script>
I am having the same problem here. It seems Memberstack doesn't have an off-the-shelf solution for that, but there are people setting timeouts to let Zapier to its thing before redirecting the user. As you can see right here:
https://forum.memberstack.io/t/member-specific-page-after-signup/1282
I've tried their suggestions in this post but they're not working.
I'd like to add a hook function when the "path namespace" changes. For example, when the router goes from myapp/fields/9837278993 to myapp/lists/183727856. Of course I could use some regex conditional logic in the main onBeforeAction hook. The more abstract logic would be "when namespace goes from fields to any except fields run the hook. What are your suggestions? Thanks for your help!
I think you're on the right track wanting to abstract the logic away from the router layer (makes switching routers much easier!). The problem is getting the data context before it changes.
To accomplish this, I'd recommend turning your Router.go line into a function. Chances are this is inside an event, so the IR reactive current() won't hurt you here.
function changePath(path) {
if (path !== 'fields' && Router.current().route.name === 'fields') {
//do stuff
}
Router.go(path);
}
I ended up with my own fully generic solution, using the reactive var Router.current().location.get() mixed with the Tracker.autorun function. So It's 100% independant of Router configuration, which I think is a good thing. Written in coffeescript.
Trivial to use :
Meteor.startup ->
RouterTransitions.register('/lists/' ,null,-> console.log("Switching from lists to any"))
Expected behaviour :
When the route changes from /lists/* to /(*) where (*)!='lists/', the hook function is run.
You can register as many transitions as you want from the same origin, as long as the destination is not a subpath of the origin (for example : from /lists/ to /lists/1864f will not trigger the hook). However the origin can be a subpath of the destination.
Here is the source :
class RouterTransitions
#could be any sequence of characters with at least one forbidden in URL standards (RFC 3986, section 2 characters)
WILDCARD:'>>'
constructor:->
Tracker.autorun =>
newPath=Router.current()?.location.get().path
if #_oldPath!=newPath
#_matchingDestinations.forEach (destinations) =>
origin=destinations.origin
Object.keys(destinations.targets).forEach (key) =>
if !newPath.match("#{origin}.*")&&(key==#WILDCARD or newPath.match("#{key}.*"))
#call the hook
destinations.targets[key]()
#_matchingOrigins =Object.keys(#dictionnary).filter (origin) => newPath.match("#{origin}.*")
#_matchingDestinations = #_matchingOrigins.map (key)=> {
targets:#dictionnary[key]
origin:key
}
#_oldPath=newPath
###
#param {String} origin : the namespace of the incoming path, null for any match. Origin can be a subset of destination.
#param {String} destination : the namespace of the forthcoming path, null for any match. Important! destination cannot be a subset of origin
#param {String} hook : the callback to be run on matching conditions
###
register:(origin,destination,hook) =>
origin=origin||#WILDCARD
destination=destination||#WILDCARD
if #dictionnary[origin]
#dictionnary[origin][destination]=hook
else
hooks=#dictionnary[origin]={}
hooks[destination]=hook
#A simple dict with keys='origin' and values plain objects with destinations mapped to hooks
dictionnary:{}
_oldPath:'/'
_matchingDestinations:[]
_matchingOrigins:[]
window.RouterTransitions=new RouterTransitions()
I'd like to disable all POSTs to an OpenACS/AOLServer installation. Is there an good singular place – a request-hook or wrapper/middleware – to do this?
(Bonus points if the intercept can let a few URI patterns or logged-in users through.)
Yes, this is straight forward to do. You have a choice here: you can register a proc to run instead of all POSTs, or can you register a filter to run before the POST and filter out certain users or whatever. I think the filter is a better choice.
To do this you register your proc or filter using ns_register_proc or ns_register_filter (with preauth). Put the following code in a .tcl file under the tcl folder of an OpenACS package or under the main AOLserver /web/servername/tcl directory.
Filter example:
ns_register_filter preauth POST / filter_posts
proc filter_posts {} {
set user_id [ad_verify_and_get_user_id]
set list_of_allowed_user_ids [21 567 8999]
if {[lsearch -exact $list_of_allowed_user_ids $user_id] == -1 } {
#this user isn't allowed - so redirect them
ns_returnredirect "/register/"
# tell AOLserver to abort this thread
return filter_return
} else {
# this user is allowed, tell AOLserver to continue
return filter_ok
}
}
Proc example:
ns_register_proc POST / handle_posts
proc handle_posts {} {
ns_returnredirect "http://someotherwebsite.com"
}
Is it possible to modify the routing based on the logged in user's id?
For example, lets say I have the route "/users/1/comments" to get all comments for user number 1, and "users/1/settings" for their settings.
Is there anyway "users/current/comments" and "users/current/settings" could go to the same endpoints if the logged in users {id} was 1?
I thought of using a listener and redirect, but that's sounds horrible.
You can base this on the logged in user without specifying its id in the url, so you would have
/users/comments
which redirects to a controller. In that controller you could just get the user id
$this->getUser()->getId()
For the 2 routes, you could just link those both to the same action.
In fact, I let it go to the same action in the controller as the the numbered {id} values.
ie. users/{id}/comments and users/current/comments both went to:
commentsAction($id)
{
if ($id=='current') {
$securityContext = $this->get('security.context');
$currentuser = $securityContext->getToken()->getUser();
$id=$currentuser->getId();
}
// etc...
}