Zim

running a custom script when a page is created

Asked by Zoltán Vörös

Dear All,

I was wondering whether it would be possible to run a custom script when a page is created. I know that one can use templates, but templates support only 4 functions, and in fact, only one of them (strftime) is a standard python function. I believe it would be useful to let the user run whatever script they want. The particular example I have in mind is that I use calendar pages for documenting experiments, and I would like to create a folder for that particular date. I can create a link (although, I cannot use lowercase letters for months' names...), but I still have to create the folder in an external application. (Another option is to make a custom tool for this, but that is not very elegant, and it would still suffer from the limitations of strftime.) Could something like this be added to the template format:

[% IF expr %]
 nothing will run here, this is only a text...
[% ELSIF expr %]
[% RUN myscript.py %]
myscript.py has run
[% END %]

I would suggest that the script runs before everything else, so its output (if any) could be inserted into the page just created.

If I got a pointer, I could try to implement this myself.
Thanks,
Zoltán

Question information

Language:
English Edit question
Status:
Solved
For:
Zim Edit question
Assignee:
No assignee Edit question
Solved by:
Zoltán Vörös
Solved:
Last query:
Last reply:
Revision history for this message
Jaap Karssenberg (jaap.karssenberg) said :
#1

What you are asking is currently not possible, and I would not like to
add this to the template structure.

But with the "custom tools" function maybe you can solve it in a different way.

E.g. if you want to create a folder for the current page you would
create a script that does that given the page name as a argument. Next
you configure the custom tool to call that script.

This way at least you can have a toolbar button "create experiment
folder". You can even make the script open the folder directly. So you
push on the button, the folder gets created and a opened in one
action.

Revision history for this message
Zoltán Vörös (zvoros) said :
#2

Thanks for the answer! The method that you suggest is what I am doing now, but I wanted to change that. The problem is that I would like to add a link to the folder in the file itself, and not as a separate tool. (For some reason, at least, on windows, one cannot link a custom image to custom tools, so if you already have 5 identical buttons, then it becomes a bit confusing. But that is a different issue.) As I pointed out above, the options for strftime are quite limited, so even if I don't want to create the folder, there are problems with the formatting. I can't create a link to mar2012/mar07.

But is there a specific reason, why a script cannot be run? I have looked into templates.py, and it seems to me that one could deal with this as with the 'IF' case or similar. What I had in mind was to take whatever comes after RUN, and then pass it to os.system(). I don't see why this would be impossible, or what kind of ramifications there would be.

Best,
Zoltán

Revision history for this message
Jaap Karssenberg (jaap.karssenberg) said :
#3

On Wed, Mar 7, 2012 at 1:30 PM, Zoltán Vörös
<email address hidden> wrote:
> Question #189945 on Zim changed:
> https://answers.launchpad.net/zim/+question/189945
>
> Zoltán Vörös posted a new comment:
> Thanks for the answer! The method that you suggest is what I am doing
> now, but I wanted to change that. The problem is that I would like to
> add a link to the folder in the file itself, and not as a separate tool.
> (For some reason, at least, on windows, one cannot link a custom image
> to custom tools, so if you already have 5 identical buttons, then it
> becomes a bit confusing. But that is a different issue.) As I pointed
> out above, the options for strftime are  quite limited, so even if I
> don't want to create the folder, there are problems with the formatting.
> I can't create a link to mar2012/mar07.

You can modify the page from the custom tool and add the link as well.
Then you are not limited by the template language.

For the icon for custom tools, did you file a bug report ?

> But is there a specific reason, why a script cannot be run? I have
> looked into templates.py, and it seems to me that one could deal with
> this as with the 'IF' case or similar. What I had in mind was to take
> whatever comes after RUN, and then pass it to os.system(). I don't see
> why this would be impossible, or what kind of ramifications there would
> be.

It is just bad design, because you use a template to run something
that has nothing to do with templating. Also I do not want to allow
arbitrary code execution in templates. In my opinion a template should
be safe and only do actual output formatting.

-- Jaap

Revision history for this message
Zoltán Vörös (zvoros) said :
#4

> You can modify the page from the custom tool and add the link as well.
> Then you are not limited by the template language.

Basically, that is what I am doing now. However, this also allows the user to tamper with the page via a script even long after it was created. The upshot is that the script has to do some sort of parsing, while if the script is run only at the time of creation, then one has a blank page to start with, so no parsing is required. I think making the custom tool failsafe is quite tricky, and in fact, failure might result in loss of data in that case, for one can simply wipe out the page completely, or return garbage. On the other hand, there is not much that can go wrong with a blank page...

> For the icon for custom tools, did you file a bug report ?

No, not yet. I will do that soon.

> Also I do not want to allow arbitrary code execution in templates. In my opinion a
> template should be safe and only do actual output formatting.

While I understand the point here, I don't completely agree with it: one problem I have already mentioned, I cannot insert a string (let along create the folder) "Data can be found in data/mar2012/mar07", because the formatting options of strftime are rather limited. Beyond that, I can create a syntactically valid template that still "fails". But you check for failures when you process the template in templates.py. An external script could be checked in the same way: one could demand that the script return a boolean, and a string. If the boolean is true, the script is inserted, otherwise it is ignored.

I see that I can't really convince you why this option would be useful, but I still feel that it is a huge loss. Allowing the user to simply run scripts would make zim much more customisable. And again, I don't quite see the conceptual problem, because one can already work on the text of the page using custom tools. But I just don't want to have a custom tool for every type of page (in that case, one could even run the wrong script on a page), while if the script is run at the time of creation, there is no such need.

Best,
Zoltán

Revision history for this message
Jaap Karssenberg (jaap.karssenberg) said :
#5

On Wed, Mar 7, 2012 at 9:05 PM, Zoltán Vörös > While I understand the
point here, I don't completely agree with it: one
> problem I have already mentioned, I cannot insert a string (let along
> create the folder) "Data can be found in data/mar2012/mar07", because
> the formatting options of strftime are rather limited.

Have a look at http://docs.python.org/library/datetime.html#strftime-strptime-behavior
. Looks like you need the format "data/%b%Y/%b%d". Any issue with that
?

> Beyond that, I
> can create a syntactically valid template that still "fails". But you
> check for failures when you process the template in templates.py. An
> external script could be checked in the same way: one could demand that
> the script return a boolean, and a string. If the boolean is true, the
> script is inserted, otherwise it is ignored.

I not talking about safety in terms of failure. I'm talking of safety
as in security. Templates should be safe in the sense that when you
get them from unknown source they should not contain arbitrary code.
That is why functions are sandboxed in the template.

Btw. you can always write a plugin to add a function to the template.
Requires a bit more work on your end, but it is really not that
complicated. Just take a boilerplate plugin and register for the
signal when a template is processed. At this time you can insert a new
function in the template dict.

-- Jaap

Revision history for this message
Zoltán Vörös (zvoros) said :
#6

> Have a look at http://docs.python.org/library/datetime.html#strftime-strptime-behavior
> . Looks like you need the format "data/%b%Y/%b%d". Any issue with that?

This would create the string 'data/Mar2012/Mar07', and not 'data/mar2012/mar07'. This is not an issue, if I only want a string, but if I want to create a link, then there is a problem. Strangely, the link won't work even on windows, which I don't quite understand, because paths on windows are not case sensitive.

strftime does not have a format for lowercase month names. I guess, the reason is that one could use the tolower() method on strings, but that does not work in zim.

> I not talking about safety in terms of failure. I'm talking of safety
> as in security. Templates should be safe in the sense that when you
> get them from unknown source they should not contain arbitrary code.
> That is why functions are sandboxed in the template.

I think this is a fair point, but this also incurs limitations.

> Btw. you can always write a plugin to add a function to the template.
> Requires a bit more work on your end, but it is really not that
> complicated. Just take a boilerplate plugin and register for the
> signal when a template is processed. At this time you can insert a new
> function in the template dict.

More work is not an issue as I indicated at the beginning. The question is whether this can be done in a "portable" way, i.e., without changing the source code of zim. I think it wouldn't make too much sense to do anything that works only on a particular installation on a particular computer.

Can I find any information on how I would implement this?
Best,
Zoltán

Revision history for this message
Mariano Draghi (chaghi) said :
#7

Hi Zoltán,

I uploaded a very simple plugin as a proof of concept of the solution proposed by Jaap, i.e., add any extra functions using a plugin.
The example plugin adds the function "lower", and it just returns str(whatever-it-gets).lower()
An example Template using this would be:

======= [% page.basename %] =======
[% SET date = strftime("data/%b%Y/%b%d") -%]
This is a link with the needed format: [[[% lower(date) %]]]

Please, take a look at the plugin, and feel free to extend it. You can add as many functions as you like adding them in the Template's dict.

You can grab the example plugin from here: http://ubuntuone.com/4BIuzWeBy8FDgCUBDLAS5Q
Just drop it in your plugins folder, and enable it using the Preferences dialog.

Best regards,
Mariano

Revision history for this message
Mariano Draghi (chaghi) said :
#8

Sorry, the download link would be http://ubuntuone.com/7T1bQl7QhtaTsCnPmdVfew

Best regards,
Mariano

Revision history for this message
Zoltán Vörös (zvoros) said :
#9

Hi Mariano,

Many thanks for the plugin! It's fantastic.
Best,

Zoltán