Rewrite node-links in Drupal 8 with anchor-hashtag e.g. /termname#title instead of /termname/title.html - anchor

in my Drupal 8 site, i have sections tagged with terms like "Therapies" and "Team", where the single contents (each therapy, each person ...) are nodes with anchors. The nodes (the whole team for example) are displayed on one page.
In my menu its not a problem to append the anchor-hashtag to exactly scroll to the single therapy or team-member.
But: I also use entity-references to link to the different nodes. Those links are generated inside the system automatically (e.g. therapy/massage.html). I now want to create a rewrite rule that rewrites all node-links to (e.g.) xxx.xxx.com/therapy#massage, where "massage" is the title of the node.
Hint: It is NOT possible to do this with pathauto! I already tried that.
Thanks in advance and regard,
Fab

Looks like it's possible with Url::fromUserInput()
Take a look at the following link: https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Url.php/function/Url%3A%3AfromUserInput/8.1.x
Your code will look something like this
$url = Url::fromUserInput('#' . $node->getTitle());
Where to put this depends on how you create your page.
If it's a standard view (which I'm assuming) you have to hook into the creation of your link and change the URL.
For the above you need to create a module and add the hook into your .module file.
The hook you're going to need is hook_link_alter(), you can find the documentation here: https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Menu!menu.api.php/function/hook_link_alter/8.1.x
/**
* Implements hook_link_alter().
*/
function module_link_alter(&$variables) {
/** #var NodeInterface $node */
$node = $variables['options']['node'];
if ($node != NULL){
$variables['url'] = Url::fromUserInput('#' . $node->getTitle());
}
The code above should get you on your way, I'm not entirely sure if it'll work since I haven't tried it yet.
Hope this helps!

Related

Set the Visitor ID in Adobe Analytics through DTM

I'm trying to set the Visitor ID in Adobe Analytics through DTM.
Above the s_code I have:
var visitor = new Visitor("xxxx")
visitor.trackingServer = "xxx.xx.xx.omtrdc.net"
I've created a data element where the legacy code used to call the
Visitor.getInstance("xxxx");
and set the Visitor ID to %Visitor ID%
That's not working however, and my visitor ID is always just set to %Visitor ID% and obviously not reading any values. I'd really appreciate any input that someone can give me.
Thanks,
Mike
The Visitor ID pops s.visitorID and is in general related to visitor id, but is not the same as s.visitor which is what gets popped for the VisitorAPI integration. DTM does not currently have a built-in field for the s.visitor variable, so you will have to set it yourself within the config, either in the Library Management code editor (assuming you are opting to c/p the core lib and not the "Managed by Adobe" option) or else in the Custom Page Code section.
Since you are popping it in a data layer first, you can reference the data layer like this:
s.visitor = _satellite.getVar('Visitor ID');
NOTE: A separate potential issue you may have is with whether or not the Visitor object is available for your data element. Since data elements are the first thing to be evaluated by DTM, you will need to ensure that the VisitorAPI.js library is output before your top page DTM script include.
If this is a problem for you, or if you are wanting to host VisitorAPI.js within DTM, then you may need to adjust where you are popping that stuff. For example, place the VisitorAPI core code above the custom code as the first stuff within the data element, before:
var visitor = new Visitor("xxxx") visitor.trackingServer = "xxx.xx.xx.omtrdc.net
Or, don't use the data element at all. Instead, put the VisitorAPI code within the Adobe Analytics custom code or core lib section and pop all that stuff (aboove the s.visitor assignment). Or a number of other methods; point is, VisitorAPI stuff must be loaded before the data element can make use of it, same as it must be loaded before Adobe Analytics can make use of it.
So DTM is changing pretty fast and furious right now. They have a "Marketing Cloud Service ID" that works well. Before I used that, however, I did find a way to fix the code. Crayon Violent was right, as usual, that the problem was that the script wasn't available yet. I fixed this by putting the following code in between the VisitorAPI.js and the AppMeasurement stuff in the DTM managed library.
var aA = new AppMeasurement();
aA.visitorNamespace="companyname";
aA.visitor = Visitor.getInstance("companyname");
In addition, there were also some issues using my localhost for testing while trying to see if I had this correct or not. If you are having issues and think you have it correct, it may be worthwhile to elevate it to a different environment.

Where should I put data like testimonials and addresses in Umbraco?

I am fairly new to Umbraco and I am wondering where I should put pieces of random data.
I have a lot of experience with Sitecore, used it for several years, I am certified etc etc. But now I have to build something with Umbraco. At a first glance Umbraco looks inferior to Sitecore in every way, but I might be wrong about that.
So what I am wondering is, where should I put non-browsable pieces of data that are visible on several places throughout the website? I'd like to create data items in Umbraco for things like Testimonials, Offices? I'd like to have a centralized place in Umbraco where they can be maintained and reference them from a page node. But the way it looks now is that everything has to be on the page node. Which is sort ok, for your average family webpage.
Could someone please shed some light on this?
You could create another node under the man content and call it site settings and store them there that way all pages under the home page are just visible pages on the front end and all data nodes are in a separate area.
There is one property in umbraco that you can add to document types and name it "umbracoNaviHide" (for alias, the name can be anything). This allows wires in automatically to the .IsVisible() method.
var children = Model.Content.Children.Where(x => x.IsVisible());
I find this situation to be very frequent, think of slideshows. When I make an Umbraco website, under my root node I normalle havea Slideshow document type (that contains slides) and I programmatically traverse those to build the slideshow on the home page for example. This Slideshow document has that "umbracoNaviHide" property and I skip it from my menus either using the .IsVisible() method or by manually skipping specific document types.
var menuItems = Model.Content.Children.Where(x => x.DocumentTypeAlias != "Slideshow" && x.DocumentTypeAlias != "Search");
On the other hand, if you are looking for "labels", you can look at "Dictionnary" items under the "Settings" tab.
To directly answer your questions, I reccomend putting non-browsable pieces of data as children of the relevant browsable content node. (But there are other valid ways to do this, it really is up to you and what's best for your content editors.)
Hope this helps.

How to dynamically generate url for image map in Oracle ApEx?

The scenario:
I have an ApEx page which pulls a record from a table. The record contains an id, the name of the chart (actually a filename) and the code for an image map as an NVARCHAR2 column called image_map.
When I render the page I have an embedded HTML region which pulls the image in using the #WORKSPACE_IMAGES#&P19_IMAGE. substitution as the src for the image.
Each chart has hot spots (defined in the image_map html markup) which point to other charts on the same ApEx page. I need to embed the:
Application ID (like &APP_ID.)
Session (like &APP_SESSION.)
My problem:
When I try to load the &APP_ID as part of the source into the database it pre-parses it and plugs in the value for the ApEx development app (e.g. 4500) instead of the actual target application (118).
Any help would be greatly appreciated.
Not a lot of feedback - guess I'm doing something atypical?
In case someone else is trying to do this, the workaround I ended up using was to have a javascript run and replace some custom replacement flags in the urls. The script is embedded in the template of the page and assigns the APEX magic fields to local variables, e.g.:
var my_app_id = '&APP_ID';
Not pretty, but it works...
Ok - I think I've left this open long enough... In the event that anyone else is trying to (mis)use apex in a similar way, it seems like the "apex way" is to use dynamic actions (which seem stable from 4.1.x) and then you can do your dynamic replace from there rather than embedding js in the page(s) themselves.
This seems to be the most maintainable, so I'll mark this as the answer - but if someone else has a better idea, I'm open to education!
I found it difficult to set a dynamic URL on a link to another page - directly - attempting to include the full URL as an individual link target doesn't work, at least in my simplistic world, I'm not an expert (as AJ said: any wisdom appreciated).
Instead, I set individual components of the url via the link, and a 'Before Header' PL/SQL process on the targeted page to combine the elements into a full url and assign it to the full url page-item:
APEX_UTIL.set_session_state(
'PG_FULL_URL',
'http...'||
v('PG_URL_COMPONENT1')||
v('PG_URL_COMPONENT2')||
'..etc..'
);
...where PG_FULL_URL is an item of Type 'Display Image', 'Based On' 'Image URL stored in Page Item Value'.
This is Apex 5.1 btw, I don't know if some of these options are new in this release.

Is there a way to automatically grab all the elements on the page using Selenium?

When creating tests for .Net applications, I can use the White library to find all elements of a given type. I can then write these elements to an Xml file, so they can be referenced and used for GUI tests. This is much faster than manually recording each individual element's info, so I would like to do the same for web applications using Selenium. I haven't been able to find any info on this yet.
I would like to be able to search for every element of a given type and save its information (location/XPath, value, and label) so I can write it to a text file later.
Here is the ideal workflow I'm trying to get to:
navigate_to_page(http://loginscreen.com)
log_in
open_account
button_elements = grab_elements_of_type(button) # this will return an array of XPaths and Names/IDs/whatever - some way of identifying each grabbed element
That code can run once, and I can then re-run it should any elements get changed, added, or removed.
I can then have another custom function iterate through the array, saving the info in a format I can use later easily; in this case, a Ruby class containing a list of constants:
LOGIN_BUTTON = "//div[1]/loginbutton"
EXIT_BUTTON = "//div[2]/exitbutton"
I can then write tests that look like this:
log_in # this will use the info that was automatically grabbed beforehand
current_screen.should == "Profile page"
Right now, every time I want to interact with a new element, I have to manually go to the page, select it, open it with XPather, and copy the XPath to whatever file I want my code to look at. This takes up a lot of time that could otherwise be spent writing code.
Ultimately what you're looking for is extracting the information you've recorded in your test into a reusable component.
Record your tests in Firefox using the Selenium IDE plugin.
Export your recorded test into a .cs file (assuming .NET as you mentioned White, but Ruby export options are also available)
Extract the XPath / CSS Ids and encapsulate them into a reusable classes and use the PageObject pattern to represent each page.
Using the above technique, you only need to update your PageObject with updated locators instead of re-recording your tests.
Update:
You want to automate the record portion? Sounds awkward. Maybe you want to extract all the hyperlinks off a particular page and perform the same action on them?
You should use Selenium's object model to script against the DOM.
[Test]
public void GetAllHyperLinks()
{
IWebDriver driver = new FireFoxDriver();
driver.Navigate().GoToUrl("http://youwebsite");
ReadOnlyCollection<IWebElement> query
= driver.FindElements( By.XPath("//yourxpath") );
// iterate through collection and access whatever you want
// save it to a file, update a database, etc...
}
Update 2:
Ok, so I understand your concerns now. You're looking to get the locators out of a web page for future reference. The challenge is in constructing the locator!
There are going to be some challenges with constructing your locators, especially if there are more than one instance, but you should be able to get far enough using CSS based locators which Selenium supports.
For example, you could find all hyperlinks using an xpath "//a", and then use Selenium to construct a CSS locator. You may have to customize the locator to suit your needs, but an example locator might be using the css class and text value of the hyperlink.
//a[contains(#class,'adminLink')][.='Edit']
// selenium 2.0 syntax
[Test]
public void GetAllHyperLinks()
{
IWebDriver driver = new FireFoxDriver();
driver.Navigate().GoToUrl("http://youwebsite");
ReadOnlyCollection<IWebElement> query
= driver.FindElements( By.XPath("//a") );
foreach(IWebElement hyperLink in query)
{
string locatorFormat = "//a[contains(#class,'{0}')][.='{1}']";
string locator = String.Format(locatorFormat,
hyperlink.GetAttribute("class"),
hyperlink.Value);
// spit out the locator for reference.
}
}
You're still going to need to associate the Locator to your code file, but this should at least get you started by extracting the locators for future use.
Here's an example of crawling links using Selenium 1.0 http://devio.wordpress.com/2008/10/24/crawling-all-links-with-selenium-and-nunit/
Selenium runs on browser side, even if you can grab all the elements, there is no way to save it in a file. As I know , Selenium is not design for that kinds of work.
You need to get the entire source of the page? if so, try the GetHtmlSource method
http://release.seleniumhq.org/selenium-remote-control/0.9.0/doc/dotnet/html/Selenium.DefaultSelenium.GetHtmlSource.html

Symfony 1.4 and global variables

I've got a very old php application (1999) that has been worked on during the last ten years. At this point the app starts to show it's age so i'm in te progress of migrating to a "new" framework, symfony 1.4. But since the app is very large, i cannot do this at once. So i'm planning to wrap the old app into the new symfony app, and convert functionality by functionality.
First step in this transition was making the old app appear in the new symfony app. So, i've created the "frontend" application, added a "legacy" module, made it the default homepage, and i've put everyhting i had in my index.php (all pages went through this index.php) in the indexSuccess.php file for the indexAction. I've added the code in the "view" because there are also functions in it and changing that setup would take me more time than i want to spend on the old app.
Unfortunately i've now got an issue with global variables. Let me give you an example (i would have never made this register function like this, but it is, so please look past that.
$session = new ps_session;
$demo = "this is a demo variable";
$session->register('demo');
In ps_session i have this method
public function register($var) {
global $$var;
$_SESSION [$var] = $$var;
}
So it should put the content of $demo in a session var named "demo". Clever right :) Anyway, var_dumping shows me the that $$var is "null" and $demo is filled if i var_dump before and after calling the function. Exact same code without symfony and it returns the correct content.
What am i missing? The global call is spread out in all area's of this massive app so i really don't want to switch to something else, so i'm hoping for a quick fix :)
Maybe relevant, the all code except the index.php content are in frontend/lib/legacy/ folder, the index is in frontend/modules/legacy/ (if there is some scope issue i'm missing)
I think that since your indexSuccess.php file is included inside a function (more precisely, here : lib/vendor/symfony/lib/view/sfPHPView.class.php:185 ), this can't work, because $demo is no longer in the global scope. I don't see any easy workaround for this...
I think you should create a legacy folder in /web , and use routing to redirect to it if the url corresponds to something not migrated yet.
I went with putting the entire old site under web/legacy and redirecting from the default index action to the legacy folder. Most of the url's were made by mod_rewrite so easily fixed. The other url's went through a function so fixing was ok, and only a few were hardcoded. To make it totally transparant, i only need to redo the homepage to start from, so i don't have a visible /legacy/ in my url. Thanks for the help!
I agree with greg0ire that this is an issue with the way sfPHPView includes indexSuccess.
Could you simply require index.php in the default/index action?

Resources