In our case, the html links (hrefs) in TextDocuments (based on the CWS samples) get updated properly when a content "move" is done, but NOT when a content copy is done. And a "copy then move" doesn't work, since, apparently, the links are somehow messed up in the copy. So, off we went into the sources, for lack of a better solution.
We found that Move and Copy are in two different places and apparently unrelated. Move is in CMSNode.cs, Copy is in Document.cs. The Move command apparently uses XmlDocument classes to migrate the content, the Copy does not. They are very different procedures indeed. So, what we'd like to achieve -- the "embedded href link manipulation" as done by Move() but still do a Copy() -- looks risky for a "non core member" even if we we're willing to change the source.
Our question/request: Are completely missing the point here. Is there another way to achieve this using normal Umbraco XSLT or macros that can be handled by Umbraco mortals, or do we need to have source changes? If source changes necessary: who know how to do this? :-)
I think there might be a slight misconception of how things work here. When linking in the richtext editor, umbraco stores the link as {locallink:1234} where 1234 is the node that is being linked to. So let us take an example:
Now, if the ChildPage has a link to SomePage, that link will be saved as {locallink:1001}. If we decide to move the SomePage the new structur will look like this:
In this case the link will be intact, because the target page still has the same id. If we instead copied the page, the structure would now look like this:
Now the SomePage (1004) has gotten an ID that is different from the original page. So the link that is in ChildPage(1005) still points to the page with id 1001. So this is all by design, and not something i believe would be changed in the source.
Maybe if you explain the scenario that you want to use this sort of functionality in, we can identify a design or method that might help you out? :-)
I have nodes structured like this for a multilingual site:
Content
-en
--english pages
-fr
--french pages
Within the english pages there are links within the copy that refer to other english pages, what I'd like to achieve is when I add a new language, I copy the entire "en" node under the main "Content" node, I'd like to have a way to make all of the links within the copy of the "en" node i just copied refer to other pages within the COPIED node instead of the original one. Is there a way to achieve this? It's just a bit time consuming and error prone to sift through each new language and make sure all my links are correct.
You could make some code that runs through all the properties looking for {locallink:1234} type tags, and check if the id is in the current site. If not, then check if there is a related node that _is_ in the current site. To make the relation, just remember to check the "Relate to original" when copying the node in the first place. Otherwise, you have no way of knowing which node in the french site to link to instead.
So you think something like ancestor-or-self of whatever node is the main node of the site and then go from there? I guess what I don't understand is how to predict the new id of the copied node, I could definitely just be looking at this the wrong way...
Thanks Morten, Amir. Glad to see some action here, because I was just about to go fishing around in the sources here -- one eyed bandit, so to say! Morten, certainly it is by design, no question there, but we need another (or variant) design :-) Our scenarion is similar to Amir's I expect: we use Umbraco to support several languages of the same site. We chose the option (based on Umbraco forums and docs) to have separate trees, each in a give language. The trees are not related except that they have the same top-level root and similar structure, different lingo:
-Umbraco root - en --Home ---TreeLevel1 ----TL1a ---TreeLevel2
- de
--Startseite
---BaumLevel1 ----TL1a(from EN, now for tranlation to DE)
---BaumLevel2
The "reference" tree is the EN tree. ALL other trees will have exactly the same structure. So once we write TextPage TL1a in the TinyHTML, it has links to Home or Home/TreeLevel2 etc. Our intent: copy the EN version of TL1a into the DE tree at the same location (---BaumLevel1) and figure a way to make the links to Home now point to Startseite (same place/page as Home) ... and then use the Umbraco Translation process to export/import the Tl1a page in this location, translating it from EN to DE. This works with a move, because the IDs are the same, so all realationships are the same. If we could only get the copy to just add a common prefix or postfix to the nodeIDs (eg. localLink:{1001} in EN => localLink:{10000000001001} in DE and localLink:{20000000001001} in FR) so their relationships stay intact, just as if they we're moved ... they will never need to refer to another language tree, only nodes within this language tree. Make sense? Other solutions? Ideas? Clearly, this is not perfect from all perspectives, but it gets us to a point where we can work with translators and content providers in parallel. As for the current status: the EN tree is (full), we are now starting our translatsions ... and thus ran into this problem that didn't turn up in our mini tests where we were more focused onthe translation import/export stuff (which we also had to manip a bit via scripts for external tools, but works now) ...
I would still say that you could put an eventhandler on your site that parses the copied page for licallinks, and then checks if that id is in the current tree or not. If not, then find a related page that is, or make a qualified guess.
It is the sort of thing that is really nice to already have in place before you start creating content, so might not be so effective if you alread have a lot of unrelated content pages.
Hi Morten, thanks for the suggestion. We do not yet have a lot of dangling links, so we're not too late. I guess I'll need to learn even more about Umbraco. I'm advanced in ASP.NET, and I have written macros and extended the XLST scripts in Umbraco, but no event handlers and parsers ...completely new territory. That's the catch here. Could you give me a little startup help (or pointer, example...) about how to add the event and, above all, how to parse that content for the links and replace them without corrupting something?
Well, the first step would be to create the original site tree.
Whenever something is copied from the original tree to the localized version, remember to check the "relate to original" box. This will make it possible to locate the translated version of a page later.
Now, whenever we copy something, we want our code to start looking for links. EventHandlers to the rescue:
As you can see, there are BeforeCopy and AfterCopy events. I have not used those, but I am guessing that they will be fired by the node that is being created.
Now, in your eventhandler, you need to get a hold of your content through the Document API. Something like this:
var originalText = sender.getProperty("bodyText").Value;
sender.getProperty("bodyText").Value = yourTextParser(originalText);
To parse the text, take a look at umbraco's source umbraco.template.ParseInternalLinks() method, and create you own something like this
Getting there... but I would expect Document.AfterCopy += new Document.CopyEventHandler(Document_AfterCopy); to provide me the new resulting copy of the document as "senderDoc" in my handler: public static void Document_AfterCopy(Document senderDoc, CopyEventArgs eArgs)
However, both BeforeCopy and AfterCopy event handlers provide the same document ID (senderDoc.Id) although the copied result has new document IDs (as it should) when I observe it in the document picker. I need these new IDs in order to build a reference table to map subsequent copy operations. Is this a bug or am I misinterpreting something? Another way to get the resulting copied doc ID?
Actually, oddly enough, in the After_Copy the senderDoc.getProperty("bodyText").Value content is acually the new, copied version (with my replacements), so that is working fine, but the senderDoc.Id is still the "old" ID, not the new one. ?
>>And you are sure that you are not saving you replacements on the
original document?
Well, the replacements appear in the new copy in the "other document tree" and the original stays the same, so I guess that's a pretty good indication that all is as intended. Also, the log file shows me that everything went as planned, except for that drasted new document ID that I need and don't have...
My mistake: yes both BeforeCopy and AfterCopy events change the source document of the copy operation. So at least it is consistent. So, I'm almost back to step 1. The event handlers are a nice feature, bug how am I to get at the target ID/content of the copy operation? I am currently trying to figure out your suggestion above, looking here (http://forum.umbraco.org/yaf_postst1619_Relate-copied-items-to-orignal.aspx) for example...
Relational integrity of links in HTML nodes: CMSNode.Move() does right thing, Content.Copy() doesn't...
Hi, we are intensive users of Umbraco 4.0.3 (newest ASP.NET versions, Windows7/Server2008, IIS7)
Essentially, we have this problem, which has not yet been answered:
http://our.umbraco.org/forum/templating/templates-and-document-types/8625-When-copying-a-parent-node,-have-links-refer-to-the-new-children--parent">http://our.umbraco.org/forum/templating/templates-and-document-types/8625-When-copying-a-parent-node,-have-links-refer-to-the-new-children--parent
In our case, the html links (hrefs) in TextDocuments (based on the CWS samples) get updated properly when a content "move" is done, but NOT when a content copy is done. And a "copy then move" doesn't work, since, apparently, the links are somehow messed up in the copy. So, off we went into the sources, for lack of a better solution.
We found that Move and Copy are in two different places and apparently unrelated. Move is in CMSNode.cs, Copy is in Document.cs. The Move command apparently uses XmlDocument classes to migrate the content, the Copy does not. They are very different procedures indeed. So, what we'd like to achieve -- the "embedded href link manipulation" as done by Move() but still do a Copy() -- looks risky for a "non core member" even if we we're willing to change the source.
Our question/request: Are completely missing the point here. Is there another way to achieve this using normal Umbraco XSLT or macros that can be handled by Umbraco mortals, or do we need to have source changes? If source changes necessary: who know how to do this? :-)
Thanks!
Richard
Here's that link again, not sure what went wrong:
http://our.umbraco.org/forum/templating/templates-and-document-types/8625-When-copying-a-parent-node,-have-links-refer-to-the-new-children--parent
R
I think there might be a slight misconception of how things work here. When linking in the richtext editor, umbraco stores the link as {locallink:1234} where 1234 is the node that is being linked to. So let us take an example:
Content
- Home
- - SomePage(id:1001)
- - - ChildPage1(id:1002)
- - OtherPage(id:1003)
Now, if the ChildPage has a link to SomePage, that link will be saved as {locallink:1001}. If we decide to move the SomePage the new structur will look like this:
Content
- Home
- - OtherPage(id:1003)
- - - SomePage(id:1001)
- - - - ChildPage1(id:1002)
In this case the link will be intact, because the target page still has the same id. If we instead copied the page, the structure would now look like this:
Content
- Home
- - SomePage(id:1001)
- - - ChildPage1(id:1002)
- - OtherPage(id:1003)
- - - SomePage(id:1004)
- - - - ChildPage1(id:1005)
Now the SomePage (1004) has gotten an ID that is different from the original page. So the link that is in ChildPage(1005) still points to the page with id 1001. So this is all by design, and not something i believe would be changed in the source.
Maybe if you explain the scenario that you want to use this sort of functionality in, we can identify a design or method that might help you out? :-)
A scenario I'm working with:
I have nodes structured like this for a multilingual site:
Content
-en
--english pages
-fr
--french pages
Within the english pages there are links within the copy that refer to other english pages, what I'd like to achieve is when I add a new language, I copy the entire "en" node under the main "Content" node, I'd like to have a way to make all of the links within the copy of the "en" node i just copied refer to other pages within the COPIED node instead of the original one. Is there a way to achieve this? It's just a bit time consuming and error prone to sift through each new language and make sure all my links are correct.
You could make some code that runs through all the properties looking for {locallink:1234} type tags, and check if the id is in the current site. If not, then check if there is a related node that _is_ in the current site. To make the relation, just remember to check the "Relate to original" when copying the node in the first place. Otherwise, you have no way of knowing which node in the french site to link to instead.
So you think something like ancestor-or-self of whatever node is the main node of the site and then go from there? I guess what I don't understand is how to predict the new id of the copied node, I could definitely just be looking at this the wrong way...
Thanks Morten, Amir. Glad to see some action here, because I was just about to go fishing around in the sources here -- one eyed bandit, so to say! Morten, certainly it is by design, no question there, but we need another (or variant) design :-) Our scenarion is similar to Amir's I expect: we use Umbraco to support several languages of the same site. We chose the option (based on Umbraco forums and docs) to have separate trees, each in a give language. The trees are not related except that they have the same top-level root and similar structure, different lingo:
-Umbraco root
- en
--Home
---TreeLevel1
----TL1a
---TreeLevel2
- de
--Startseite
---BaumLevel1
----TL1a(from EN, now for tranlation to DE)
---BaumLevel2
The "reference" tree is the EN tree. ALL other trees will have exactly the same structure. So once we write TextPage TL1a in the TinyHTML, it has links to Home or Home/TreeLevel2 etc. Our intent: copy the EN version of TL1a into the DE tree at the same location (---BaumLevel1) and figure a way to make the links to Home now point to Startseite (same place/page as Home) ... and then use the Umbraco Translation process to export/import the Tl1a page in this location, translating it from EN to DE. This works with a move, because the IDs are the same, so all realationships are the same. If we could only get the copy to just add a common prefix or postfix to the nodeIDs (eg. localLink:{1001} in EN => localLink:{10000000001001} in DE and localLink:{20000000001001} in FR) so their relationships stay intact, just as if they we're moved ... they will never need to refer to another language tree, only nodes within this language tree. Make sense? Other solutions? Ideas? Clearly, this is not perfect from all perspectives, but it gets us to a point where we can work with translators and content providers in parallel. As for the current status: the EN tree is (full), we are now starting our translatsions ... and thus ran into this problem that didn't turn up in our mini tests where we were more focused onthe translation import/export stuff (which we also had to manip a bit via scripts for external tools, but works now) ...
Thanks!
Richard
I would still say that you could put an eventhandler on your site that parses the copied page for licallinks, and then checks if that id is in the current tree or not. If not, then find a related page that is, or make a qualified guess.
It is the sort of thing that is really nice to already have in place before you start creating content, so might not be so effective if you alread have a lot of unrelated content pages.
Hi Morten, thanks for the suggestion. We do not yet have a lot of dangling links, so we're not too late. I guess I'll need to learn even more about Umbraco. I'm advanced in ASP.NET, and I have written macros and extended the XLST scripts in Umbraco, but no event handlers and parsers ...completely new territory. That's the catch here. Could you give me a little startup help (or pointer, example...) about how to add the event and, above all, how to parse that content for the links and replace them without corrupting something?
Thanks again for your assistance!
Richard
Hi Richard.
Well, the first step would be to create the original site tree.
Whenever something is copied from the original tree to the localized version, remember to check the "relate to original" box. This will make it possible to locate the translated version of a page later.
Now, whenever we copy something, we want our code to start looking for links. EventHandlers to the rescue:
http://our.umbraco.org/wiki/reference/api-cheatsheet/using-applicationbase-to-register-events/event-examples
Here is a list of the events:
http://our.umbraco.org/wiki/reference/api-cheatsheet/using-applicationbase-to-register-events/overview-of-all-events
As you can see, there are BeforeCopy and AfterCopy events. I have not used those, but I am guessing that they will be fired by the node that is being created.
Now, in your eventhandler, you need to get a hold of your content through the Document API. Something like this:
To parse the text, take a look at umbraco's source umbraco.template.ParseInternalLinks() method, and create you own something like this
Look at the Relations API to find the related page to the ID that you get when parsing.
http://our.umbraco.org/wiki/reference/api-cheatsheet/relationtypes-and-relations
Enjoy :-)
Thanks Morten, very cool. I'll get right on it and let you know :-)
Rich
Hi Morten, I'm assuming that I'll need to do this in the Umbraco sources, or is there a place to have the events call-out to so I don't have to?
You don't need to modify the source at all. Look at the wiki articles about events for examples of how to hook into the events.
Thanks for the quick reply: I found this, which explains it all:
http://www.richardsoeteman.net/PermaLink,guid,f470b6cf-40da-4aa9-a0d9-7b984fe9bf59.aspx
very cool indeed.
R
Getting there... but I would expect Document.AfterCopy += new Document.CopyEventHandler(Document_AfterCopy); to provide me the new resulting copy of the document as "senderDoc" in my handler: public static void Document_AfterCopy(Document senderDoc, CopyEventArgs eArgs)
However, both BeforeCopy and AfterCopy event handlers provide the same document ID (senderDoc.Id) although the copied result has new document IDs (as it should) when I observe it in the document picker. I need these new IDs in order to build a reference table to map subsequent copy operations. Is this a bug or am I misinterpreting something? Another way to get the resulting copied doc ID?
Thanks!
Rich
R
Looks like you're right. Would be nice to get the new id in the eventargs.
Possible workaround:
On the AfterCopy event, get the related documents for the original, and find the newest one. Then parse the properties on that document.
Actually, oddly enough, in the After_Copy the senderDoc.getProperty("bodyText").Value content is acually the new, copied version (with my replacements), so that is working fine, but the senderDoc.Id is still the "old" ID, not the new one. ?
And you are sure that you are not saving you replacements on the original document?
>>On the AfterCopy event, get the related documents for the original, and find the newest one. Then parse the properties on that document.
Best way to get the related documents from the original but in the new tree/copy??? Sounds pretty convoluted!
Thanks!
R
>>And you are sure that you are not saving you replacements on the original document?
Well, the replacements appear in the new copy in the "other document tree" and the original stays the same, so I guess that's a pretty good indication that all is as intended. Also, the log file shows me that everything went as planned, except for that drasted new document ID that I need and don't have...
Thanks for you assist on this!
R
My mistake: yes both BeforeCopy and AfterCopy events change the source document of the copy operation. So at least it is consistent. So, I'm almost back to step 1. The event handlers are a nice feature, bug how am I to get at the target ID/content of the copy operation? I am currently trying to figure out your suggestion above, looking here (http://forum.umbraco.org/yaf_postst1619_Relate-copied-items-to-orignal.aspx) for example...
With the following code in my AfterCopy event handler, it looks like I can find the target node, as you said. So I guess this is what was meant.
if (senderDoc.Relations.Length > 0 && senderDoc.Level > 1)
{
System.Diagnostics.Debugger.Launch(); //RHT
foreach (Relation r in senderDoc.Parent.Relations)
{
if (r.RelType.Alias == "relateDocumentOnCopy")
{
Log.Add(LogTypes.Copy, -1, "Copy r.Id: " + r.Id + ", r.Child.Id: " + r.Child.Id + ", Child Level: " + r.Child.Level);
}
}
}
If this is indeed the easiest way, I'll run with this...
is working on a reply...