Appologies if this has already been covered, but I couldnt find it anywhere.
I am wondering if there are any ways to add events during page render? Specifically I want to extend the ParseInternalLinks method in templates.cs, but I would like to do this in such a way that I can package it up as as a plugin.
The reason I wish to do this, is I would like to create a soloution whereby media is managed in a similar way to links. I.E. when a link is created a "locallink" is inserted. I would would like to extend this so that "medialink".
If anybody has manged to do this / has any tips / can point me to a plugin that does this already I would very much appreciate it!
I don't know of any plugins doing this, but if you want to hook-up events, there you have the list of all events available:http://our.umbraco.org/wiki/reference/api-cheatsheet/using-applicationbase-to-register-events/overview-of-all-events.
Top job that you are actually looking into this. It's great to see people poking around the codebase :)
With that said, for me, this is more like a failing of the core and so I would probably suggest that you have a look at forking the codebase and instead modiying the core to support this and submit it back as a pull request.
I know I've heard other raise this issue, so it would be awesome if you managed to create a fix.
I know of another developer who was looking into creating a "localMedia" code for TinyMCE also, not sure how far he got with it though. Could try pinging Mads on Twitter if you're interested?
If you wanted something that will work with v4.7.x, (e.g. now) you could take a look at my Shortcodes package - or if that doesn't fit the bill, then feel free to disect the source-code to see how it works. (Essentially it's a HttpModule that adds a Response Filter to parse the HTML output for shortcodes - effectively the same as "localLink" does, but more extensible)
Unfortuantly there are no events that will trigger when I'm after, but thanks for the link, as you've just pointed me to some events that will be handy for future projects!
Scary stuff playing with the core, but I think that would be my best bet. Are there any hard and fast rules about getting my code into the next release?
Can I pick your brain a little on what the best approach is please?
Should I develop "events" into the core, and create a "medialink" plugin, or just build the "medialink" option streight into the core?
If I did the latter, do you think adding an option into umbracoSettings.config would be the best bet to allow people to turn on/off the "medialink" code? This way we wont break anybodys build during upgrade.
I havent really hacked away at the core yet, so I dont even know if my ideas are possible, but I'm keen to give it a go!
For me, it would probably make more sense to just bake it in, rather than making a plugin, but I do like your idea of creating a switch to be able to turn it back to the legacy way of working.
If you create a fork of the repo and just have a poke around, you can rest assured that anything you do won't break the main code base untill it's submitted as a pull request, and even when it is, we will do testing on it anyway before it's merged in. So just give it a go.
No no, you should be ok, you need to switch branch to the 4.8 branch. We are working on splitting out the old v5 code to prevent this type of confusion.
Ok, a few pointers and notes from the solution I ended up with:
After playing around a little in the umbraco and umbraco_client folders, I stumpled upon a file called insertLink.aspx, which resides in umbraco/plugins/tinymce3. At the bottom of that file there are two umbraco:TreeControl controls of interest. One has an App-attribute with the value "content" and one has the value "media". Both controls have a DialogMode-attribute. The content control has a value of "locallink" and the media control has a value of "fulllink". So, change media to "locallink" and get beer? NO ! :)
The screenshot below illustrates the difference in behavior for content and media when using locallink in the umbraco:TreeControl.
Notice the difference in the html. One could probably live with this and just try to work with the href and assume that it's a media item if it parses to a number. Yet, I do not think this is an ideal solution since
1. it's not very user friendly to put an id in the url field 2. assuming all url's which parses to a number could end up causing you pain.
Now, the correct approach, as stated above, would probably be to fork the source, fix it in the core and submit it back. I haven't done this before though, and my time was short, so I fixed it as a workaround instead.
Time for some code:
In the insertLink.aspx there are two javascript functions called dialogHandler and validateUmbracoLink. You can check the original code in your Umbraco installation and I gist'ed my custom versions of these functions here.
The dialogHandler function will split the href of the chosen node/media and insert the different parts in the correct fields. Since the locallink href for media is just an id, it wont populate the url and title fields for us yet, to do this, we need to call a custom web service to get the correct href for Umbraco to use.
The validateUmbracoLink function will transform the locallink from the html to the correct url and title fields in the dialog when you edit the existing link. To do this it will call a (legacy...?) web service to get the nice url for the content link, however, we can't do that with media, so some custom web service call is needed.
I gist'ed the web services here, or, actually they are just mvc actions in a controller, but they could just as well be a good old asmx or something else.
Now, for simplicity I decided to route all calls to the dialogHandler through my web service exiting fast if the supplied id string do not parse to a number. If we cant parse the id to a number, its a content link and we dont need to do anything special with it. Now that we have an id of a media item we need to load that media item. In this case, I use LuceneNode which runs on a Lucene index and wraps the functionality of dynamic node. You could use whatever here, new Media(id), Examine or what ever. The important thing is that we need our media item.
From my LuceneNode I create a new MediaItem which again is just a wrapper class to get easy access to the name of the media item and the path of the file. Lastly I return a string that matches the content href in the dialog. It contains three parts seperated by pipes (|). To break it up:
1. The first part is inserted in the html. 2. The second part is inserted in the url field. 3. The third part is inserted in the title field.
Now, when we return this string we can let the existing Umbraco code do the rest of the work for us.
The validateUmbracoLink and the GetTinyMceMediaStringFromLink functions are used to keep a reference to current link and populate the url and title fields when editing an existing link. I hope they are self explanatory, if not, let me know.
Ok, so far so good. We now have localmedia links in our html, but we still need to parse them into real links when rendering a page. To do this I use HtmlAgilityPack to parse all the html in the TinyMCE field. Again, here is a gist to show the code.
With HtmlAgilityPack I load up a html document with the text from my TinyMce field. In the document I select all a tags and run the href value through my localmedia regex. If I get a match I can get the id for the media item. Again I use my LuceneNode and MediaItem to get the media item and then the file path which I put back into the href value. Lastly, if any links was altered I replace the original string with the new html and return it as so.
Now, be aware, that for me, this is a perfectly viable solution since I can get media items from my Lucene index which is wicked fast. You might run into some performance issues though if you are simply using new Media(id), thus hitting the database for every single localmedia link, and you have a lot of them on the page. I wrote about this potential performance issue in another thread.
Now, this approach is by no means perfect, but its what I was able to pull together in the time I had available. Hopefully it can point you in the right direction. Let me know if you got further questions or anything else.
Just thinking ... wouldn't it be great if you could had a "content-processing pipeliene" you could register your HtmlAgilityPack-/whatever-based logic to alter the content without touching the TinyMCE control used in umbraco?
BTW: Mads, you use Lucene for "caching" the media items?
Yes, we use Lucene as a backing storage between the Umbraco database and the frontend, so everything that is in the database is also in the Lucene index, including media items. We then query the Lucene index throug our own little API. If you aren't familiar with Lucene, Examine is a great option, I have referenced Shannons post about using media items in Examine a few times: http://shazwazza.com/post/Ultra-fast-media-performance-in-Umbraco.aspx
Extend / Override template.cs
Hi Guys,
(Umbraco v4.7.x)
Appologies if this has already been covered, but I couldnt find it anywhere.
I am wondering if there are any ways to add events during page render? Specifically I want to extend the ParseInternalLinks method in templates.cs, but I would like to do this in such a way that I can package it up as as a plugin.
The reason I wish to do this, is I would like to create a soloution whereby media is managed in a similar way to links. I.E. when a link is created a "locallink" is inserted. I would would like to extend this so that "medialink".
If anybody has manged to do this / has any tips / can point me to a plugin that does this already I would very much appreciate it!
Thanks in advance,
Matt Watson
Hi Matthew,
I don't know of any plugins doing this, but if you want to hook-up events, there you have the list of all events available:http://our.umbraco.org/wiki/reference/api-cheatsheet/using-applicationbase-to-register-events/overview-of-all-events.
Basically you need to create a class that inherits from ApplicationBase, and then in the constructor you hook up whatever event you want. You can find an example here : http://our.umbraco.org/forum/developers/extending-umbraco/33002-Executing-some-code-after-a-member-has-been-created
Hope this helps.
Cheers,
Michael.
Hey Matt,
Top job that you are actually looking into this. It's great to see people poking around the codebase :)
With that said, for me, this is more like a failing of the core and so I would probably suggest that you have a look at forking the codebase and instead modiying the core to support this and submit it back as a pull request.
I know I've heard other raise this issue, so it would be awesome if you managed to create a fix.
Matt
Hi Matt (original poster),
I know of another developer who was looking into creating a "localMedia" code for TinyMCE also, not sure how far he got with it though. Could try pinging Mads on Twitter if you're interested?
If you wanted something that will work with v4.7.x, (e.g. now) you could take a look at my Shortcodes package - or if that doesn't fit the bill, then feel free to disect the source-code to see how it works. (Essentially it's a HttpModule that adds a Response Filter to parse the HTML output for shortcodes - effectively the same as "localLink" does, but more extensible)
Good luck and let us know how you get on.
Cheers, Lee.
Thanks Michael,
Unfortuantly there are no events that will trigger when I'm after, but thanks for the link, as you've just pointed me to some events that will be handy for future projects!
Matt,
Scary stuff playing with the core, but I think that would be my best bet. Are there any hard and fast rules about getting my code into the next release?
Can I pick your brain a little on what the best approach is please?
Should I develop "events" into the core, and create a "medialink" plugin, or just build the "medialink" option streight into the core?
If I did the latter, do you think adding an option into umbracoSettings.config would be the best bet to allow people to turn on/off the "medialink" code? This way we wont break anybodys build during upgrade.
I havent really hacked away at the core yet, so I dont even know if my ideas are possible, but I'm keen to give it a go!
Lee,
Thank you for the info, I will definately give Mads a tweet as he may have some of the answers I need :)
I'll take a look at the Shortcodes package, it looks very usful and may give me a workaround for a project I am working on at the moment!
Thanks everyone! I love the Umbraco community!
Matt Watson
Hey Matt,
Check out the documentation that's recently been put up regarding contributing: http://our.umbraco.org/contribute/guidelines-for-core-contribution
For me, it would probably make more sense to just bake it in, rather than making a plugin, but I do like your idea of creating a switch to be able to turn it back to the legacy way of working.
If you create a fork of the repo and just have a poke around, you can rest assured that anything you do won't break the main code base untill it's submitted as a pull request, and even when it is, we will do testing on it anyway before it's merged in. So just give it a go.
Matt
Thanks for that Matt!
I will officially be contributing to the core (or at least attempting to)! I'll let you know how I get on.
Matt Watson
Awesome!
You know where I am if you have any questions (quite literally :).
Matt
Right,
My fork is here: http://umbraco.codeplex.com/SourceControl/network/forks/mwtsn/mediaLink, wish me luck ;)
Dont expect much to happen until after the 25/07/2012 as I'm on leave, but at least I've made a start.
Matt Watson
Hmmm,
Seems that was a fork of v5 :(
I'll try again!
No no, you should be ok, you need to switch branch to the 4.8 branch. We are working on splitting out the old v5 code to prevent this type of confusion.
Matt
Cheers Matt :)
Feel like a n00b right now ;)
I've manged to find this guide, which should help http://our.umbraco.org/wiki/how-tos/how-to-contribute-to-umbraco/develop-and-commit/creating-a-fork
Shame I've just deleted and recreated the fork, but at least I'm learning!
I plan to blog my progress to help future n00bs, so I'll share that link when I create it.
Matt Watson
Lets arrange a meetup and I'll be happy to give you a crash course. Should make for a nice blog post.
DM Twitter me with a time you are free and we'll get together.
Matt
Ok, a few pointers and notes from the solution I ended up with:
After playing around a little in the umbraco and umbraco_client folders, I stumpled upon a file called insertLink.aspx, which resides in umbraco/plugins/tinymce3. At the bottom of that file there are two umbraco:TreeControl controls of interest. One has an App-attribute with the value "content" and one has the value "media". Both controls have a DialogMode-attribute. The content control has a value of "locallink" and the media control has a value of "fulllink". So, change media to "locallink" and get beer? NO ! :)
The screenshot below illustrates the difference in behavior for content and media when using locallink in the umbraco:TreeControl.
Notice the difference in the html. One could probably live with this and just try to work with the href and assume that it's a media item if it parses to a number. Yet, I do not think this is an ideal solution since
1. it's not very user friendly to put an id in the url field
2. assuming all url's which parses to a number could end up causing you pain.
Now, the correct approach, as stated above, would probably be to fork the source, fix it in the core and submit it back. I haven't done this before though, and my time was short, so I fixed it as a workaround instead.
Time for some code:
In the insertLink.aspx there are two javascript functions called dialogHandler and validateUmbracoLink. You can check the original code in your Umbraco installation and I gist'ed my custom versions of these functions here.
The dialogHandler function will split the href of the chosen node/media and insert the different parts in the correct fields. Since the locallink href for media is just an id, it wont populate the url and title fields for us yet, to do this, we need to call a custom web service to get the correct href for Umbraco to use.
The validateUmbracoLink function will transform the locallink from the html to the correct url and title fields in the dialog when you edit the existing link. To do this it will call a (legacy...?) web service to get the nice url for the content link, however, we can't do that with media, so some custom web service call is needed.
I gist'ed the web services here, or, actually they are just mvc actions in a controller, but they could just as well be a good old asmx or something else.
Now, for simplicity I decided to route all calls to the dialogHandler through my web service exiting fast if the supplied id string do not parse to a number. If we cant parse the id to a number, its a content link and we dont need to do anything special with it. Now that we have an id of a media item we need to load that media item. In this case, I use LuceneNode which runs on a Lucene index and wraps the functionality of dynamic node. You could use whatever here, new Media(id), Examine or what ever. The important thing is that we need our media item.
From my LuceneNode I create a new MediaItem which again is just a wrapper class to get easy access to the name of the media item and the path of the file. Lastly I return a string that matches the content href in the dialog. It contains three parts seperated by pipes (|).
To break it up:
1. The first part is inserted in the html.
2. The second part is inserted in the url field.
3. The third part is inserted in the title field.
Now, when we return this string we can let the existing Umbraco code do the rest of the work for us.
The validateUmbracoLink and the GetTinyMceMediaStringFromLink functions are used to keep a reference to current link and populate the url and title fields when editing an existing link. I hope they are self explanatory, if not, let me know.
Ok, so far so good. We now have localmedia links in our html, but we still need to parse them into real links when rendering a page. To do this I use HtmlAgilityPack to parse all the html in the TinyMCE field. Again, here is a gist to show the code.
With HtmlAgilityPack I load up a html document with the text from my TinyMce field. In the document I select all a tags and run the href value through my localmedia regex. If I get a match I can get the id for the media item. Again I use my LuceneNode and MediaItem to get the media item and then the file path which I put back into the href value. Lastly, if any links was altered I replace the original string with the new html and return it as so.
Now, be aware, that for me, this is a perfectly viable solution since I can get media items from my Lucene index which is wicked fast. You might run into some performance issues though if you are simply using new Media(id), thus hitting the database for every single localmedia link, and you have a lot of them on the page. I wrote about this potential performance issue in another thread.
Now, this approach is by no means perfect, but its what I was able to pull together in the time I had available. Hopefully it can point you in the right direction. Let me know if you got further questions or anything else.
Kind regards
Mads
Thank you Mads for an absoloutly awsome reply! Some great pointers of what I need to tackle with regards to the TinyMCE part of the soloution!
Hopefully I will get it all bundled up back into the core :)
Matt Watson
Just thinking ... wouldn't it be great if you could had a "content-processing pipeliene" you could register your HtmlAgilityPack-/whatever-based logic to alter the content without touching the TinyMCE control used in umbraco?
BTW: Mads, you use Lucene for "caching" the media items?
André
Hey André
Yes, we use Lucene as a backing storage between the Umbraco database and the frontend, so everything that is in the database is also in the Lucene index, including media items. We then query the Lucene index throug our own little API. If you aren't familiar with Lucene, Examine is a great option, I have referenced Shannons post about using media items in Examine a few times: http://shazwazza.com/post/Ultra-fast-media-performance-in-Umbraco.aspx
Kind regards
Mads
is working on a reply...