Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Richard 38 posts 78 karma points
    Apr 30, 2010 @ 21:26
    Richard
    0

    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

     

  • Richard 38 posts 78 karma points
    May 03, 2010 @ 20:16
  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    May 03, 2010 @ 22:18
    Morten Bock
    0

    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? :-)

  • Amir Khan 1282 posts 2739 karma points
    May 04, 2010 @ 17:08
    Amir Khan
    0

    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.

  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    May 04, 2010 @ 20:22
    Morten Bock
    0

    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.

  • Amir Khan 1282 posts 2739 karma points
    May 05, 2010 @ 07:16
    Amir Khan
    0

    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...

  • Richard 38 posts 78 karma points
    May 05, 2010 @ 18:11
    Richard
    0

    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

     

  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    May 06, 2010 @ 18:02
    Morten Bock
    0

    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.

  • Richard 38 posts 78 karma points
    May 07, 2010 @ 09:13
    Richard
    0

    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

  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    May 07, 2010 @ 18:06
    Morten Bock
    0

    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:

    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

    public static string yourTextParser(string pageContents)
    { // Parse internal links MatchCollection tags = Regex.Matches(pageContents, @"href=""[/]?(?:\{|\%7B)localLink:([0-9]+)(?:\}|\%7D)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); foreach (Match tag in tags) if (tag.Groups.Count > 0) { string id = tag.Groups[1].Value; //.Remove(tag.Groups[1].Value.Length - 1, 1); string newLink = yourMethodToFindRelatedPage(int.Parse(id)); pageContents = pageContents.Replace(tag.Value.ToString(), "href=\"{localLink:" + newLink + "}"); } return pageContents; }

    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 :-)

     

  • Richard 38 posts 78 karma points
    May 07, 2010 @ 19:54
    Richard
    0

    Thanks Morten, very cool. I'll get right on it and let you know :-)

    Rich

     

  • Richard 38 posts 78 karma points
    May 08, 2010 @ 16:38
    Richard
    0

    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?

     

  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    May 08, 2010 @ 16:50
    Morten Bock
    0

    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.

  • Richard 38 posts 78 karma points
    May 08, 2010 @ 17:36
    Richard
    0

    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

     

  • Richard 38 posts 78 karma points
    May 08, 2010 @ 20:09
    Richard
    0

    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

  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    May 08, 2010 @ 20:33
    Morten Bock
    0

    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.

  • Richard 38 posts 78 karma points
    May 08, 2010 @ 20:36
    Richard
    0

    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. ?

     

  • Morten Bock 1867 posts 2140 karma points MVP 2x admin c-trib
    May 09, 2010 @ 12:28
    Morten Bock
    0

    And you are sure that you are not saving you replacements on the original document?

  • Richard 38 posts 78 karma points
    May 09, 2010 @ 13:29
    Richard
    0

    >>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

  • Richard 38 posts 78 karma points
    May 09, 2010 @ 13:32
    Richard
    0

    >>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

  • Richard 38 posts 78 karma points
    May 09, 2010 @ 19:33
    Richard
    0

    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...

     

     

  • Richard 38 posts 78 karma points
    May 09, 2010 @ 23:24
    Richard
    0

    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...

     

Please Sign in or register to post replies

Write your reply to:

Draft