Copied to clipboard

Flag this post as spam?

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


  • Jonas Eriksson 930 posts 1825 karma points
    Aug 03, 2010 @ 14:14
    Jonas Eriksson
    0

    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:

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

      
    result += "</ul>"
      
    print result
  • Jonas Eriksson 930 posts 1825 karma points
    Aug 03, 2010 @ 15:29
    Jonas Eriksson
    0

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

      
    result += "</ul>"
      
    print result
  • Aaron Powell 1708 posts 3046 karma points c-trib
    Aug 03, 2010 @ 15:52
    Aaron Powell
    0

    Don't any of the built-in Python and Ruby templates have examples of passing in the arguments?

  • Jonas Eriksson 930 posts 1825 karma points
    Aug 03, 2010 @ 16:41
    Jonas Eriksson
    0

    This works fine:

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

      
    result += "</ul>"
      
    print result
  • Jonas Eriksson 930 posts 1825 karma points
    Aug 03, 2010 @ 16:44
    Jonas Eriksson
    0

    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.

  • Jonas Eriksson 930 posts 1825 karma points
    Aug 03, 2010 @ 17:55
    Jonas Eriksson
    0

    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:

    import clr
    clr.AddReferenceByName("App_Code")

    import MyAppCodeClass
    print MyAppCodeClass.HelloWorld()
  • Pickels 75 posts 108 karma points
    Aug 03, 2010 @ 19:29
    Pickels
    2

    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.

    tmpl = """
                <li>
                    <a href="{url}" title="{name}">
                        <img src="{thumbnail}" alt="{name}" />
                        {name}
                    </a>
                </li>
            """
    
    print tmpl.format( thumbnail=thumbnail, url=url, name=name

    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.

     

  • Jonas Eriksson 930 posts 1825 karma points
    Aug 03, 2010 @ 20:26
    Jonas Eriksson
    0

    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:

    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)

    # create string templates
    liSelected="<li class='selected'><a href='{url}'>{name}</a></li>"
    li="<li><a href='{url}'>{name}</a></li>"

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

      
    result += "</ul>"
      
    print result
  • Pickels 75 posts 108 karma points
    Aug 03, 2010 @ 20:32
    Pickels
    0

    Yeah, list comprehension are really nice in Python. They are suppose to be faster than normal for loops also. 

  • Jonas Eriksson 930 posts 1825 karma points
    Aug 03, 2010 @ 20:55
    Jonas Eriksson
    1

    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

  • Pickels 75 posts 108 karma points
    Aug 04, 2010 @ 18:21
    Pickels
    0

    Any idea if you can import other standard Python libraries? Like the os module would be good. 

  • Pickels 75 posts 108 karma points
    Aug 04, 2010 @ 18:56
    Pickels
    1

    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:

       // Add umbracos python folder to python's path
                path = System.Web.HttpContext.Current.Server.MapPath(GlobalSettings.Path + "\\..\\python");
                Engine.AddToPath(path);

    You would think it's included.

    Even if you do:

    import sys
    from line in sys.path:
         print line

    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'))
  • Jonas Eriksson 930 posts 1825 karma points
    Aug 04, 2010 @ 19:10
    Jonas Eriksson
    0

    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.

  • Pickels 75 posts 108 karma points
    Aug 04, 2010 @ 19:48
    Pickels
    0

    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.

  • Jonas Eriksson 930 posts 1825 karma points
    Aug 04, 2010 @ 20:29
    Jonas Eriksson
    0

    Tested to import standard library os successfully.

    sys.path.append(Current.Server.MapPath('~/python/lib'))

    import os
    for file in os.listdir("c:/"):
        print file

    Copied 26 megs of Python 2.6 lib-files first...

    Interested in the template engines. Any good places to start reading about them?

  • Pickels 75 posts 108 karma points
    Aug 04, 2010 @ 20:44
    Pickels
    1

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

  • Pickels 75 posts 108 karma points
    Aug 04, 2010 @ 21:37
    Pickels
    0

    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()
  • Jonas Eriksson 930 posts 1825 karma points
    Aug 05, 2010 @ 06:23
    Jonas Eriksson
    0

    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

    ulHtml = "<ul>{items}</ul>"
    liHtml = "<li><a href='{url}'>{name}</a></li>"

    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)

     

  • Jonas Eriksson 930 posts 1825 karma points
    Aug 05, 2010 @ 07:40
    Jonas Eriksson
    1

    Breadcrumb

    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

      return path

    breadCrumbHtml = "<a href='/'>Home</a>{path}"
    breadCrumbNodeHtml = " / <a href='{url}'>{name}</a>"
    breadCrumbNodeHtmlCurrentPage = " / {name}"

    breadCrumb = breadCrumbHtml.format(path=getPath(currentPage.Id, 2, breadCrumbNodeHtml, breadCrumbNodeHtmlCurrentPage ))

    print breadCrumb
  • Jonas Eriksson 930 posts 1825 karma points
    Aug 05, 2010 @ 08:51
    Jonas Eriksson
    1

    Making new content (entering the realms of stuff that most often is better placed in a regular codebehind c#)

    import clr
    clr.AddReference('cms')
    clr.AddReference('businesslogic')

    from umbraco.BusinessLogic import *
    from umbraco.cms.businesslogic.web import DocumentType, Document
    from umbraco import library

    # sample documenttype and node

    dt = DocumentType.GetByAlias("myDocumentType")
    author = User(0)
    atNodeId=9999

    doc = Document.MakeNew("New node", dt, author, atNodeId)

    doc.getProperty("header").Value = "Sample header"
    doc.getProperty("bodyText").Value = "Sample body text"

    doc.Publish(author)
    library.UpdateDocumentCache(doc.Id)

    print "Added document"
  • Jonas Eriksson 930 posts 1825 karma points
    Aug 05, 2010 @ 10:11
    Jonas Eriksson
    0

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

  • Pickels 75 posts 108 karma points
    Aug 05, 2010 @ 10:27
    Pickels
    1

    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.

  • Jonas Eriksson 930 posts 1825 karma points
    Aug 05, 2010 @ 10:40
    Jonas Eriksson
    0

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

    # 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
  • Pickels 75 posts 108 karma points
    Aug 05, 2010 @ 11:00
    Pickels
    1

    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. 

  • Jonas Eriksson 930 posts 1825 karma points
    Aug 05, 2010 @ 11:54
    Jonas Eriksson
    0

    Good to know, so I could use this instead?

    if not node.GetProperty("umbracoNaviHide") or node.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?

  • Pickels 75 posts 108 karma points
    Aug 05, 2010 @ 11:57
    Pickels
    0

    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.

  • Jonas Eriksson 930 posts 1825 karma points
    Aug 05, 2010 @ 13:11
    Jonas Eriksson
    0

    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!

  • Lee Kelleher 4020 posts 15802 karma points MVP 13x admin c-trib
    Aug 05, 2010 @ 19:49
    Lee Kelleher
    0

    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.

  • Jonas Eriksson 930 posts 1825 karma points
    Aug 06, 2010 @ 10:30
    Jonas Eriksson
    0

    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.

  • Pickels 75 posts 108 karma points
    Aug 06, 2010 @ 10:36
    Pickels
    0

    Beautiful Soup but you also have the HTML Agile Pack in C#. 

    http://stackoverflow.com/questions/118654/iron-python-beautiful-soup-win32-app

  • Jonas Eriksson 930 posts 1825 karma points
    Aug 06, 2010 @ 10:55
    Jonas Eriksson
    0

    Thanks, and there's ofcourse lots in standard .net fw we can use:

    import clr
    clr.AddReference('System.Xml.Linq')
    from System.Xml import Linq

    # parse a xml string to have it indented nicely
    theString = "<ul><li>blah</li><li>node</li></ul>";
    element = Linq.XDocument.Parse(theString);

    print element
  • Jonas Eriksson 930 posts 1825 karma points
    Aug 09, 2010 @ 13:26
    Jonas Eriksson
    0

    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

  • montana 42 posts 63 karma points
    Oct 29, 2010 @ 18:47
    montana
    0

    This is the best thread on this entire forum!!

    you guys are amazing - you've directly solved at least three issues I'm having!

Please Sign in or register to post replies

Write your reply to:

Draft