I was laborating with IronPython 2.6 in Umbraco 4.5.1. It is indeed a nice extension to Umbraco, it's really a quick way to get some macro's done.
Looking at some old samples by Immo Wache I tried getting macro parameters via a session variable called 'args'. That's something I havent seen documented and perhaps it's no longer supported. But - how can one get the macro parameters?
I have this code for a navigation-ul:
from umbraco.presentation.nodeFactory import Node from umbraco import library
import clr clr.AddReference("System.Web")
from System.Web import * session = HttpContext.Current.Session
#I cannot figure out how to get the nodeid from a macro parameter
#args=session['args'] #nodeid=args['nodeid']
rootNodeId = int(1046)
result = "<ul>"
for childNode in Node(rootNodeId).Children: if childNode.Id==currentPage.Id: result += "<li class='selected'><a href='" + library.NiceUrl(childNode.Id) + "'>" + childNode.Name + "</a></li>" else: result += "<li><a href='" + library.NiceUrl(childNode.Id) + "'>" + childNode.Name + "</a></li>"
Updated code with old friend umbracoNaviHide if someone find it useful:
from umbraco.presentation.nodeFactory import Node from umbraco import library rootNodeId = int(1046)
result = "<ul>"
for childNode in Node(rootNodeId).Children: if childNode.GetProperty("umbracoNaviHide") is None or childNode.GetProperty("umbracoNaviHide").Value=="0": if childNode.Id==currentPage.Id: result += "<li class='selected'><a href='" + library.NiceUrl(childNode.Id) + "'>" + childNode.Name + "</a></li>" else: result += "<li><a href='" + library.NiceUrl(childNode.Id) + "'>" + childNode.Name + "</a></li>"
from umbraco.presentation.nodeFactory import Node from umbraco import library
rootNodeId = 0
result = "<ul>"
# Check if the macro attribute exists - saving will cause exception if we dont if globals().has_key("RootNodeId"): rootNodeId = int(RootNodeId) else: result = "Please add macro parameter 'RootNodeId'"
for childNode in Node(rootNodeId).Children: if childNode.GetProperty("umbracoNaviHide") is None or childNode.GetProperty("umbracoNaviHide").Value=="0": if childNode.Id==currentPage.Id: result += "<li class='selected'><a href='" + library.NiceUrl(childNode.Id) + "'>" + childNode.Name + "</a></li>" else: result += "<li><a href='" + library.NiceUrl(childNode.Id) + "'>" + childNode.Name + "</a></li>"
Slace, yes, the Templates suggest "set the node id you would like to fetch pages
from here you can also set it as a
macro property with the alias 'nodeId' instead"
I tried that and got errors when saving. From there I gave up that path, thought it was wrong. However looking at the source I found out the attributes are indeed added from macro property names and nodeId works - if one just checks "skip testing (ignore errors)"
To be able to save without error messages I added a test for has_key, and it works fine.
Just wanted to add another cool thing that's easily done, create a .Net - class file and save it in App_Code, and you will be able to use it from IronPython:
Ah very nice I have been wrestling Python and Umbraco for a few days now too.
For passing the macro parameters the name seems to work fine for me. You can use
print globals()
to see what's available. I also noticed you don't need to do currentPage.getProperty('x') but that you can just do x.
An other snippet I use to debug is:
import sys
try:
#Write code here
pass
except:
exctype, value = sys.exc_info()[:2]
print exctype, value
This will print errors to the page so you get a little more info about what's going wrong.
I also notice that you concatenate a lot of strings but it's faster to use the format method on string. Also for html I find it easier to use multi line strings.
You don't have to use named parameters you can just use {0}, {1}, ... but then the order matters like in C#.
Also to traverse a tree I got this snippet I use.
top = Node(-1)
def traverse(list):
for child in list:
#traverse.level += 1
traverse(child.Children)
#traverse.level -= 1
traverse.level = 0
traverse(top.Children)
If you add some conditionals you can for example look for a node of a certain type.
def allChildrenFromNodeByType(parent, type):
foundKids = []
def traverse(list):
for child in list:
if type == child.NodeTypeAlias: foundKids.append(child)
traverse(child.Children)
traverse(parent.Children)
return foundKids
Not sure if using Media is a good idea since it's in the businesslogic assemble which if I remember correctly does lots of database calls. But I have this macro to return all the images of a certain folder media node.
import clr
clr.AddReference('cms')
clr.AddReference('businesslogic')
from umbraco.cms.businesslogic.media import Media
mediaId = MacroParameter
for child in Media(mediaId).Children:
url = child.getProperty("umbracoFile").Value.ToString()
That's about it I hope you don't mind me posting a few examples here. Was looking how to get the HttpContext myself which you did in your example so thought I'd share my snippets too.
I added your suggested string templates, makes the code look better.
I experimented a bit more, especially to sort my child nodes, and found out one way to do it, and also a kind of cool way to filter the list:
from umbraco.presentation.nodeFactory import Node from umbraco import library
rootNodeId = 0
result = "<ul>"
# Check if the macro parameter exists - saving will cause exception if we dont if globals().has_key("RootNodeId"): rootNodeId = int(RootNodeId) else: result = "Please add macro parameter 'RootNodeId'" # create a python list of children - otherwise we cannot use .sort later NodeList = list(Node(rootNodeId).Children)
# filter out only the ones we like with list comprehensions NodeList = [c for c in NodeList if c.GetProperty("umbracoNaviHide") is None or c.GetProperty("umbracoNaviHide").Value =="0"]
# sort the list on Name NodeList.sort(key=lambda f: f.Name)
# iterate over the list items for childNode in NodeList: if childNode.Id==currentPage.Id: result += liSelected.format(url=library.NiceUrl(childNode.Id), name=childNode.Name) else: result += li.format(url=library.NiceUrl(childNode.Id), name=childNode.Name)
I am starting up a new Umbraco-project and have spent the day to experiment with IronPython to see if I can manage to use most py instead of xslt and c#. And the conclusion is I will go for py for almost everything, except for some heavier functionality that suits compiled c# better. The project will be built in an experimental manner and I find that a scripting language like IronPython will suit it great. Good to hear that more Umbraco-people are into IronPython aswell.
It will show . (dot) so that would make you think that the python folder is part of the Python Path. But if you try to import an other file or package you get the 'No module with that name' error.
To make it work I do
import sys
import clr
clr.AddReference('System.Web')
from System.Web.HttpContext import Current
sys.path.append(Current.Server.MapPath('~/python'))
I've been struggling with importing my own modules for hours, and was about to dig into umbraco source. In the previous version of Umbraco a print sys.path gives
but like you said, in current version we only have
['.']
and I tried adding the path by entering the full path, with no luck, but using MapPath as you did works, many thanks! However I agree the paths should be added by umbraco by default. 6 lines to import a .py-file is a bit ugly.
Did you flip around the slashes when hardcoding the path? Need to have forward slashes. Lots of imports aint that rare in Python though but not having to do it is of course better.
Was thinking with the standard libraries I could maybe implement one of the template engines you have in Python. That way I could clean up the python macros from all the html markup.
The one I know is the Django one which is also used in Google's App Engine. There is also Jinga2, genshin, mako and cheetah. The Django templates and Jinga2 use the same syntax if I am correct but I am not sure they port to IronRuby. I read that the Cheetah works in IronRuby but their site was down before so I didn't test it.
I guess now we have access to Python we can do some crazy stuff ^^.
If you want to unittest your macros you can do it like this. Thought I wouldn't have to import Python since I was using IronPython Interactive but it didn't find unittest.py. Not sure in which scope that shell runs.
"""
To run the tests in Visual Studio use the shortcut shift + alt + f5
"""
import sys
sys.path.append('C:/Python27/Lib')
import unittest
class TestClass(unittest.TestCase):
def testTrue(self):
assert True
def testFalse(self):
assert False
def main():
unittest.main()
if __name__ == '__main__':
main()
Like your traverse example, here's one to print a tree from a defined node:
from umbraco.presentation.nodeFactory import Node from umbraco import library
if globals().has_key("RootNodeId"): rootNode=Node(int(RootNodeId)) else: rootNode=None # Please add macro parameter 'RootNodeId' or set rootNode to currentPage
def listChildrenRecursively(fromNode): liItems="" for c in fromNode.Children: liItems+=liHtml.format(url=library.NiceUrl(c.Id),name=c.Name) liItems+=listChildrenRecursively(c) if liItems!="": return ulHtml.format(items=liItems) else: return ""
if not(rootNode is None): print listChildrenRecursively(rootNode)
from umbraco.presentation.nodeFactory import Node from umbraco import library
# returns a breadcrumb for a particular node
def getPath(atNodeId, fromLevel, nodeHtml, nodeHtmlCurrentPage): nodeIds = Node(atNodeId).Path.Split(",") path="" currentNodeLevel=0 for n in nodeIds: node = Node(int(n)) if currentNodeLevel>=fromLevel: if not (node is None) and not (node.Name is None): if int(n)==atNodeId: path+=nodeHtmlCurrentPage.format(name=node.Name, url=library.NiceUrl(node.Id)) else: path+=nodeHtml.format(name=node.Name, url=library.NiceUrl(node.Id)) currentNodeLevel+=1
I'm successively creating a python module with some common functions I often need from my python macro scripts. I call it HelperFunctions.py. There I define functions like dynamicHeader which returns property header if it exists and is not "", otherwise returns node name. Another example is getImageHtmlFromMedia(nodeImageProperty, imageHtmlTemplate) which returns a image src with nice format.
This module I include in the python macros I need, like
import HelperFunctions
(However because of the discussed path
problem one has to append the ~/python path as Pickles described above)
I use the common functions as:
print HelperFunctions.dynamicHeader(node)
In HelperFunctions I am also able to define global variables or I could use another .py for that, GlobalVariables.
A new cool thing is that in Umbraco 4.5+ one is able to create subfolders under the Python-folder and they display nicely in the admin ui Scripting files-tree. Subfolders makes it easy to maintain some kind of structure even with lots of python-files. Yes, the subfolder-files are also selectable from the Macro python-file dropdown.
(It's not possible to create the subfolders directly in Umbraco ui, one has to do it in the file system. Why is the folder called Python btw, when it stores the ruby-files
aswell?).
I'm very happy experiencing the ease of learning to code macros with IronPython compared to all hours I've spent struggling with Xslt. (Don't tell anyone) :-)
# Helper function to check if the node should be hidden def umbracoNaviHide(node): if node.GetProperty("umbracoNaviHide") is None or node.GetProperty("umbracoNaviHide").Value=="0": return False else: return True
I started Umbraco a week ago and I was really not looking forward to learning XSLT. Since I can use python I can prolly end the project a week earlier which is great.
I see you type is None alot or is Not None. In python False, '' (empty string), 0 and None all will return False when compared.
if something: pass
So if something is None it will return false. If you want to check if it's None you can do:
if not something: pass
There are a few pitfalls though. A none empty string will return True.
if '0': pass
That will actually return True instead of False. I had this in one of my macros since Umbraco gives booleans values as a stringed 1 or 0. So if you check those it will always return True.
One awesome thing you can also do in Python to scare people is False = True. This will actually the False label return True.
If you wanna sit back and learn python from a great tutor there is the Python Class from Google. It's a real life 2 day course recorded with excercises and documentation which you can watch on youtube.
if not node.GetProperty("umbracoNaviHide")ornode.GetProperty("umbracoNaviHide").Value=="0"
However I like "is None" because it always gives me a comfortable Boolean, and I think it makes it very clear what I'm testing: if a property exists. Right?
Yep, it's always important to keep your code readable/understandable. In Python you could take any code and reduce it so much nobody will every be able to read it.
An input from @perploughansen about creating folder directly from admin ui "actually if you write "folder/filename" in the
xslt create dialog it will create the folder for it :)" Cool!
Just wanted to say that I'm really impressed with how you guys are using IronPython with Umbraco. Python has been available for such a long time, and up until now I haven't heard of anyone actually using it!
Keep up the good work, and let us all know how you get on with future IronPython code snippets.
Pickles - you know of good xml/xhtml-modules? One thing I'd like is NiceXhtmlIndent so the produced markup is nicely indented. Another is to construct tags in a neat way, adding attributes and innerhtml. str.format() is really good but I thought probably there's something even better.
IronPython lab
Hi!
I was laborating with IronPython 2.6 in Umbraco 4.5.1. It is indeed a nice extension to Umbraco, it's really a quick way to get some macro's done.
Looking at some old samples by Immo Wache I tried getting macro parameters via a session variable called 'args'. That's something I havent seen documented and perhaps it's no longer supported. But - how can one get the macro parameters?
I have this code for a navigation-ul:
Updated code with old friend umbracoNaviHide if someone find it useful:
Don't any of the built-in Python and Ruby templates have examples of passing in the arguments?
This works fine:
Slace, yes, the Templates suggest "set the node id you would like to fetch pages from here you can also set it as a macro property with the alias 'nodeId' instead"
I tried that and got errors when saving. From there I gave up that path, thought it was wrong. However looking at the source I found out the attributes are indeed added from macro property names and nodeId works - if one just checks "skip testing (ignore errors)"
To be able to save without error messages I added a test for has_key, and it works fine.
Just wanted to add another cool thing that's easily done, create a .Net - class file and save it in App_Code, and you will be able to use it from IronPython:
Ah very nice I have been wrestling Python and Umbraco for a few days now too.
For passing the macro parameters the name seems to work fine for me. You can use
to see what's available. I also noticed you don't need to do currentPage.getProperty('x') but that you can just do x.
An other snippet I use to debug is:
This will print errors to the page so you get a little more info about what's going wrong.
I also notice that you concatenate a lot of strings but it's faster to use the format method on string. Also for html I find it easier to use multi line strings.
You don't have to use named parameters you can just use {0}, {1}, ... but then the order matters like in C#.
Also to traverse a tree I got this snippet I use.
If you add some conditionals you can for example look for a node of a certain type.
Not sure if using Media is a good idea since it's in the businesslogic assemble which if I remember correctly does lots of database calls. But I have this macro to return all the images of a certain folder media node.
That's about it I hope you don't mind me posting a few examples here. Was looking how to get the HttpContext myself which you did in your example so thought I'd share my snippets too.
Ah, nice, thanks alot!
I added your suggested string templates, makes the code look better.
I experimented a bit more, especially to sort my child nodes, and found out one way to do it, and also a kind of cool way to filter the list:
Yeah, list comprehension are really nice in Python. They are suppose to be faster than normal for loops also.
I am starting up a new Umbraco-project and have spent the day to experiment with IronPython to see if I can manage to use most py instead of xslt and c#. And the conclusion is I will go for py for almost everything, except for some heavier functionality that suits compiled c# better. The project will be built in an experimental manner and I find that a scripting language like IronPython will suit it great. Good to hear that more Umbraco-people are into IronPython aswell.
Regards
Jonas
Any idea if you can import other standard Python libraries? Like the os module would be good.
What is also strange is that I can't import other python files unless i include the python folder into the Python Path.
If you look at the Umbraco source:
You would think it's included.
Even if you do:
It will show . (dot) so that would make you think that the python folder is part of the Python Path. But if you try to import an other file or package you get the 'No module with that name' error.
To make it work I do
I've been struggling with importing my own modules for hours, and was about to dig into umbraco source. In the previous version of Umbraco a print sys.path gives
['C:\\inetpub\\www.mysite.com\\bin', 'C:\\inetpub\\www.mysite.com\\python']
but like you said, in current version we only have
['.']
and I tried adding the path by entering the full path, with no luck, but using MapPath as you did works, many thanks! However I agree the paths should be added by umbraco by default. 6 lines to import a .py-file is a bit ugly.
Standard Python libraries I havent tried yet.
Did you flip around the slashes when hardcoding the path? Need to have forward slashes. Lots of imports aint that rare in Python though but not having to do it is of course better.
Was thinking with the standard libraries I could maybe implement one of the template engines you have in Python. That way I could clean up the python macros from all the html markup.
Tested to import standard library os successfully.
Copied 26 megs of Python 2.6 lib-files first...
Interested in the template engines. Any good places to start reading about them?
The one I know is the Django one which is also used in Google's App Engine. There is also Jinga2, genshin, mako and cheetah. The Django templates and Jinga2 use the same syntax if I am correct but I am not sure they port to IronRuby. I read that the Cheetah works in IronRuby but their site was down before so I didn't test it.
I guess now we have access to Python we can do some crazy stuff ^^.
If you want to unittest your macros you can do it like this. Thought I wouldn't have to import Python since I was using IronPython Interactive but it didn't find unittest.py. Not sure in which scope that shell runs.
Like your traverse example, here's one to print a tree from a defined node:
Breadcrumb
Making new content (entering the realms of stuff that most often is better placed in a regular codebehind c#)
A bit about structure.
I'm successively creating a python module with some common functions I often need from my python macro scripts. I call it HelperFunctions.py. There I define functions like dynamicHeader which returns property header if it exists and is not "", otherwise returns node name. Another example is getImageHtmlFromMedia(nodeImageProperty, imageHtmlTemplate) which returns a image src with nice format.
This module I include in the python macros I need, like
import HelperFunctions
(However because of the discussed path problem one has to append the ~/python path as Pickles described above)
I use the common functions as:
print HelperFunctions.dynamicHeader(node)
In HelperFunctions I am also able to define global variables or I could use another .py for that, GlobalVariables.
A new cool thing is that in Umbraco 4.5+ one is able to create subfolders under the Python-folder and they display nicely in the admin ui Scripting files-tree. Subfolders makes it easy to maintain some kind of structure even with lots of python-files. Yes, the subfolder-files are also selectable from the Macro python-file dropdown.
(It's not possible to create the subfolders directly in Umbraco ui, one has to do it in the file system. Why is the folder called Python btw, when it stores the ruby-files aswell?).
Not sure if you know it but you can create python packages as wel.
In my python folder I got a folder:
-Helpers
--__init__.py (empty py file to create package)
--traversing.py
--templates.py
That way I can drop all my python helpers into my Umbraco projects. When I am done with my current project I try and share my whole folder.
Great!
I'm very happy experiencing the ease of learning to code macros with IronPython compared to all hours I've spent struggling with Xslt. (Don't tell anyone) :-)
I started Umbraco a week ago and I was really not looking forward to learning XSLT. Since I can use python I can prolly end the project a week earlier which is great.
I see you type is None alot or is Not None. In python False, '' (empty string), 0 and None all will return False when compared.
So if something is None it will return false. If you want to check if it's None you can do:
There are a few pitfalls though. A none empty string will return True.
That will actually return True instead of False. I had this in one of my macros since Umbraco gives booleans values as a stringed 1 or 0. So if you check those it will always return True.
One awesome thing you can also do in Python to scare people is False = True. This will actually the False label return True.
If you wanna sit back and learn python from a great tutor there is the Python Class from Google. It's a real life 2 day course recorded with excercises and documentation which you can watch on youtube.
Good to know, so I could use this instead?
However I like "is None" because it always gives me a comfortable Boolean, and I think it makes it very clear what I'm testing: if a property exists. Right?
Yep, it's always important to keep your code readable/understandable. In Python you could take any code and reduce it so much nobody will every be able to read it.
An input from @perploughansen about creating folder directly from admin ui "actually if you write "folder/filename" in the xslt create dialog it will create the folder for it :)" Cool!
Hey Jonas/Pickels,
Just wanted to say that I'm really impressed with how you guys are using IronPython with Umbraco. Python has been available for such a long time, and up until now I haven't heard of anyone actually using it!
Keep up the good work, and let us all know how you get on with future IronPython code snippets.
Cheers, Lee.
Thanks Lee, yeah we like py :)
Pickles - you know of good xml/xhtml-modules? One thing I'd like is NiceXhtmlIndent so the produced markup is nicely indented. Another is to construct tags in a neat way, adding attributes and innerhtml. str.format() is really good but I thought probably there's something even better.
Beautiful Soup but you also have the HTML Agile Pack in C#.
http://stackoverflow.com/questions/118654/iron-python-beautiful-soup-win32-app
Thanks, and there's ofcourse lots in standard .net fw we can use:
I've added two issues about this on codeplex, if you like to vote on them:
http://umbraco.codeplex.com/workitem/28473 (for the path-thing)
and one for umbraco.library.pythonexecutefile that also doesnt work
http://umbraco.codeplex.com/workitem/28471
This is the best thread on this entire forum!!
you guys are amazing - you've directly solved at least three issues I'm having!
is working on a reply...