Using ui-router, is there a way to capture a path that contains forward slashes and query parameters?
Let's say there's the catch-all rule in the state configuration:
var exState = {
url: '/example/*path',
...
};
$stateProvider.state(exState);
and then point my browser to
/example/test1?var1=a&var2=b/test2?var3=c&var4=d/
I now see that the forward slashes get encoded:
test1?var1=a&var2=b%2Ftest2%3Fvar3&var4=d%2F
and $stateParams.path is test1 - not what I wanted. Is it possible to get the actual 'raw' path for further processing while avoiding the automatic query parameter capturing which ui-router is doing here?
You can accomplish this but you'll need to use Regex with your URL state, this is from the UI-Router Guide (https://github.com/angular-ui/ui-router/wiki/URL-Routing):
Regex Parameters
A bonus to using curly brackets is the ability to set a Regular Expression rule for the parameter:
// will only match a contactId of one to eight number characters
url: "/contacts/{contactId:[0-9]{1,8}}"
Examples:
'/user/{id:[^/]*}' - Same as '/user/{id}' from the previous example.
'/user/{id:[0-9a-fA-F]{1,8}}' - Similar to the previous example, but only matches if the id parameter consists of 1 to 8 hex digits.
'/files/{path:.*}' - Matches any URL starting with '/files/' and captures the rest of the path into the parameter 'path'.
'/files/*path' - Ditto. Special syntax for catch all.`
This one should catch your url and the parameter string: '/files/{path:.*}' - Matches any URL starting with '/files/' and captures the rest of the path into the parameter 'path'.
EDIT: After capturing the the rest of the URL in the path parameter you'll need to URL decode in order to regain the / & ? and anything else encoded. You can use decodeURIComponent()
Related
I get my current path by using useLocation() hook.
I have several buttons and want them to navigate me to the current path combined with that concrete list ID.
Lets suppose its https://localhost:3000/list/3/items
How to insert that ID (here is 3) in between - list/{id}/items - with regex?
const navigate = useNavigate();
const location = useLocation();
// location.pathname - getting current path
// pinned to each button
const handleListChange = (listId: number) => {
// navigate(...);
};
I cant achieve that with just navigate("/list/{listId}/items") because /items can differ depending on what subpage im currently at ("/list" stays the same). So it can be for example list/{listId}/itemsSubpage/.../.... I just want to stay at the current page when navigating and only let id change.
Why not simply redirect starting from the root? I don't see the need for a regular expression here.
navigate(`/list/${listId}/items`);
or am I missing something?
Post Edit answer:
There are a few ways to achieve what you want but I'll just go ahead and post the one that came to me first, you can adjust it to your needs.
const exp = /(\/list\/)\d{1,}(\/?.+)?$/i;
const currentUrlMock = 'https://test.com/list/240/items/subpage/2/doesnt-matter'
const replaceId = (newId) => currentUrlMock.replace(exp, `$1${newId}$2`)
console.log(replaceId(3));
// logs: https://test.com/list/3/items/subpage/2/doesnt-matter
How this works is that the regular expression contains two capture groups:
(\/list\/) that matches the /list part
(\/?.+)?$ that matches everything that appears after your ID
Between them there's a \d{1,} that will capture all digits, it's not a capture group though - it has no parentheses around it ().
Replace method on string accepts regex as a first param and in the second param you can access captured groups by using dollar signs and group number, $1 to capture first group, $2 to capture the second group and so on.
By using $1${newId}$2 $1 will be replaced with fist captured group - /list and $2 will be replaced with second captured group - (\/?.+)?$. Between these a new ID which is passed in a function param will be inserted.
You should add more checks to make sure there's a proper match, I also tested the regular expression only for these URLs:
https://test.com/list/4
https://test.com/list/4/
https://test.com/list/120
https://test.com/list/120/
https://test.com/list/120/items
https://test.com/list/120/items/240
https://test.com/list/120/items/240/
and it works for these but your real use case might be different.
Our DNN website is rewriting our product SKU which is part of a Querystring when navigating from a Product Filter Page to a Detailed Product View page.
Unfortunately, some of our products have a forward slash in the SKU for example, BD0002/DSDS
The URL we are navigating to is https://dnndev.me/Product-View/sku/BD0002/DSDS, but DNN would cut off and rewrite the last part of the URL and would result in the following URL: https://dnndev.me/Product-View/sku/BD0002
I did try to add the following Regex code in the SEO settings section of DNN to ignore the re-writing of the page, but it does the same.
sku/(.*)/(.*)
I have also noticed that currently our website writes the sku without the = sign for the querystring. Currently it would be /sku/ and not ?sku= I discovered I can change this when I add |/sku| in the Keep in Querystring Regular Expression.
I have set the URL format to be Advanced in the web.config file. I don't want to change this to HumanFriendly as it breaks our module.
Our product filter page which contains the links to the Product View uses a mustache template with HttpUtility.UrlEncode for QueryStringSKU:
<a href='<%=DetailedPageRedirectLink%>/sku/{{QueryStringSKU}}'>More Info</a>
We then have a Detailed Product View module that listens for the QueryString. I did in the past try to use Encoding and Decoding, but DNN was doing its own thing and ignoring the Encoding and Decoding part so I wrote this crazy part of code that strips out part of the URL that is not part of the SKU.
string rawurlfromrequest = Request.RawUrl;
string checkifquerystringexist = Request.QueryString["sku"];
if(checkifquerystringexist != null)
{
var cleanSKU = rawurlfromrequest.Split(new[] { "sku/" }, StringSplitOptions.None)[1];
decodeprodCode = cleanSKU.Split(new[] { "&" }, StringSplitOptions.None)[0];
decodeprodCode = decodeprodCode.Split(new[] { "/search" }, StringSplitOptions.None)[0];
decodeprodCode = decodeprodCode.Split(new[] { "?fbclid=" }, StringSplitOptions.None)[0];
decodeSKU = HttpUtility.UrlDecode(decodeprodCode);
}
if (!string.IsNullOrWhiteSpace(decodeSKU) && IsEditable == false)
{
LoadProductDetails(decodeSKU);
}
So I would like to know, how can I only allow DNN to rewrite the first part of the URL and not the SKU part of the querystring when it contains a forward slash?
I found these links:
https://www.dnnsoftware.com/answers/disable-friendly-url-for-one-page
https://www.dnnsoftware.com/forums/threadid/542568/scope/posts/how-can-one-turn-off-friendly-urls-url-rewriting-etc-in-dnn-8
I had to escape the query string with uri.EscapedDataString() which will convert the / to %2F as mentioned by VDWWD.
I also discovered that some products contain a space in the SKU which made me decide to use EscapedDataString which will convert a space to %20.
I found this Table with the different Encoding methods on this post useful:
URL Encoding using C#
For some reason Request.Querystring['sku'] fetches the unencoded query string even though it is encoded in the URL. This is why I am using Request.RawUrl and stripping the query string from this.
I am trying to add a context data variable (CDV), which has a dot in its name. According to Adobe site this is correct:
s.contextData['myco.rsid'] = 'value'
Unfortunately, after calling s.t() the variable is split into two or more:
Context Variables
myco.:
rsid: value
.myco:
How can I set the variable and prevent splitting it into pieces?
You are setting it properly already. If you are referring to what you see in the request URL, that's how the Adobe library sends it. In your example, "myco" is a namespace, and "rsid" is a variable in that namespace. And you can have other variables in that namespace. For example if you have
s.contextData['myco.rsid1'] = 'value';
s.contextData['myco.rsid2'] = 'value';
You would see in the AA request URL (just showing the relevant part):
c.&myco.&rsid1=value&rsid2=value&.myco&.c
I assume you are asking because you want to more easily parse/qa AA collection request URLs from the browser network tab, extension, or some unit tester? There is no way to force AA to not behave like this when using dot syntax (namespaces) in your variables.
But, there isn't anything particularly special about using namespaces for your contextData variables; it's just there for your own organization if you choose. So if you want all variables to be "top level" and show full names in the request URL, then do not use dot syntax.
If you want to still have some measure of organization/hierarchy, I suggest you instead use an underscore _ :
s.contextData['myco_rsid1'] = 'value';
s.contextData['myco_rsid2'] = 'value';
Which will give you:
c.&myco_rsid1=value&myco_rsid2=value&.c
Side Note: You cannot do full object/dot notation syntax with s.contextData, e.g.
s.contextData = {
foo:'bar', // <--- this will properly parse
myco:{ // this will not properly parse
rsid:'value' //
} //
};
AA library does not parse this correctly; it just loops through top level properties of contextData when building the request URL. So if you do full object syntax like above, you will end up with:
c.&foo=bar&myco=%5Bobject%20Object%5D&&.c
foo would be okay, but you end up with just myco with "[object Object]" as the recorded value. Why Adobe didn't allow for full object syntax and just JSON.stringify(s.contextData) ? ¯\_(ツ)_/¯
I am reading data over a socket and parsing it with LibXML.
The problem that I am currently having is that sometimes there is a web link in the data which breaks the parser.
http://example.com/?key=value&key2=value
Is there any way to convert that to html characters?
Something like the above to
http://example.com/?key=value&key2=value
Example of socket data:
<node link="http://example.com/?key=value&key2=value" />
EDIT:
Found a solution that works for my problem here
You are going to have to do a pre-filter here. Contrary to other indications, search and replace just won't cut it. Consider your search side is &, which matches too much.
Construct the following finite state machine:
NORMAL:
if next matches "<" then TAG
TAG:
if next matches "![CDATA[" then CDATA
TAGSCAN
TAGSCAN:
if next matches whitespace then TAGSCAN2
if next matches > or next matches /> then NORMAL
TAGSCAN2:
if next matches whitespace then TAGSCAN2
if next matches SRC= or next matches HREF= then URL
TAGSCAN
URL:
we found an attribute with a URL in it. Do your search and replace
on the contents of the URL attribute value, advance past the URL and
go back to TAGSCAN
CDATA:
if next doesn't match ]]> then CDATA
NORMAL
I have found a nice solution using the code from
Find and Replace that uses a Find and Replace method suggested by bolov.
retval = str_replace(message, size, "&", "&");
if (!retval) {
printf("Not enough room to replace & with `&'\n");
}
Tried to find a solution, but I got always stuck a the docs or at answers include other bundles. In the documentation of the dynamic router you can find the hint:
"Of course you can also have several parameters, as with normal Symfony routes. The semantics and rules for patterns, defaults and requirements are exactly the same as in core routes."
Thats it.
...
/foo/{id}/bar
I tried (seems not) everything to get it done.
Same for all tries:
I tried it to apply a variable pattern and a child route.
use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route as PhpcrRoute;
$dm = $this->get('cmf_routing.route_provider');
$route = new PhpcrRoute();
$route->setPosition( $dm->find( null, '/cms/routes' ), 'foo' );
$route->setVariablePattern('/{id}');
$dm->persist( $route );
$child = new PhpcrRoute();
$child->setPosition( $route, 'bar' );
$dm->persist( $child );
$dm->flush();
With or without default value and requirement only '/foo/bar' and '/foo/*' return matches, but '/foo/1/bar' prompts me with a 'No route found for "GET /foo/1/bar"'.
...
Just now I nearly got it done.
use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route as PhpcrRoute;
$dm = $this->get('cmf_routing.route_provider');
$route = new PhpcrRoute();
$route->setPosition( $dm->find( null, '/cms/routes' ), 'example_route' );
$dm->persist( $route );
$route->setPrefix( '/cms/routes/example_route' );
$route->setPath( '/foo/{id}/bar' );
$dm->flush();
If prefix is '/cms/routes' and name is 'foo' everything works fine. But now that I got this far, assigning a speaking name would round it up.
Thanks in advice!
You got quite close to the solution, actually!
When using PHPCR-ODM, the route document id is its path in the repository. PHPCR stores all content in a tree, so every document needs to be in a specific place in the tree. We then use the prefix to get a URL to match. If the prefix is configured as /cms/routes and the request is for /foo, the router looks in /cms/routes/foo. To allow parameters, you can use setVariablePattern as you correctly assumed. For the use case of /foo/{id}/bar, you need to do setVariablePattern('/{id}/bar'). You could also have setVariablePattern('/{context}/{id}') (this is what the doc paragraph you quoted meant - i will look into adding an example there as its indeed not helpful to say "you can do this" but not explain how to).
Calling setPath is not recommended as its just less explicit - but as you noticed, it would get the job done. See the phpdoc and implementation of Model\Route::setPattern:
/**
* It is recommended to use setVariablePattern to just set the part after
* the static part. If you use this method, it will ensure that the
* static part is not changed and only change the variable part.
*
* When using PHPCR-ODM, make sure to persist the route before calling this
* to have the id field initialized.
*/
public function setPath($pattern)
{
$len = strlen($this->getStaticPrefix());
if (strncmp($this->getStaticPrefix(), $pattern, $len)) {
throw new \InvalidArgumentException('You can not set a pattern for the route that does not start with its current static prefix. First update the static prefix or directly use setVariablePattern.');
}
return $this->setVariablePattern(substr($pattern, $len));
}
About explicit names: The repository path is also the name of the route, in the example /cms/routes/foo. But it is not a good idea to use a route name of a dynamic route in your code, as those routes are supposed to be editable (and deletable) by an admin. If you have a route that exists for sure and is at a specific path, use the configured symfony routes (the routing.yml file). If its dynamic routes, have a look at the CMF Resource Bundle. It allows to define a role for a document and a way to look up documents by role. If you have a route with a specific role that you want to link to from your controller / template, this is the way to go. If you have a content document that is linked with a route document and have that content document available, your third and best option is to generate the URL from the content document. The CMF dynamic router can do that, just use the content object where you normally specify the route name.