Zim

Merge lp:~grahamrow/zim/zim-mendeley into lp:~jaap.karssenberg/zim/pyzim-refactor

Proposed by Graham Rowlands on 2014-01-21
Status: Approved
Approved by: Jaap Karssenberg on 2014-01-22
Approved revision: 680
Proposed branch: lp:~grahamrow/zim/zim-mendeley
Merge into: lp:~jaap.karssenberg/zim/pyzim-refactor
Diff against target: 139827 lines (+40149/-28975)
179 files modified
CHANGELOG.txt (+26/-0)
HACKING/Coding_Style_&_Guidelines.txt (+6/-12)
HACKING/Compatibility.txt (+0/-21)
HACKING/Config_Files.txt (+0/-30)
HACKING/Development_Tools.txt (+0/-48)
HACKING/Formats_modules.txt (+0/-5)
HACKING/Gtk_and_GObject_and_signals.txt (+22/-0)
HACKING/HACKING.txt (+29/-0)
HACKING/Ideas.txt (+0/-108)
HACKING/Ideas/Attachments.txt (+0/-23)
HACKING/Ideas/Database_and_Album.txt (+0/-77)
HACKING/Ideas/Export_OpenOffice.txt (+0/-15)
HACKING/Ideas/Forms.txt (+0/-14)
HACKING/Ideas/Inline_Latex.txt (+0/-44)
HACKING/Ideas/Parse_Tree.txt (+0/-13)
HACKING/Ideas/Profiles.txt (+0/-12)
HACKING/Ideas/SavedSearch.txt (+0/-30)
HACKING/Ideas/Search.txt (+0/-45)
HACKING/Ideas/Separate_Window.txt (+0/-15)
HACKING/Ideas/Spell_Checking.txt (+0/-27)
HACKING/Ideas/Storage.txt (+0/-24)
HACKING/Ideas/Synchronization.txt (+0/-23)
HACKING/Ideas/Tables.txt (+0/-71)
HACKING/Ideas/Tags.txt (+0/-29)
HACKING/Ideas/Task_List.txt (+0/-119)
HACKING/Ideas/Templates.txt (+0/-14)
HACKING/Ideas/Transclusion.txt (+0/-46)
HACKING/LIMITATIONS.txt (+8/-0)
HACKING/Limitations.txt (+0/-7)
HACKING/Parse_Tree.txt (+0/-84)
HACKING/Signals.txt (+0/-17)
HACKING/Start.txt (+0/-27)
HACKING/Store_modules.txt (+0/-5)
HACKING/Tasks.txt (+0/-52)
HACKING/Usage/Document_Editing_and_Creative_Writing.txt (+0/-15)
HACKING/Working_with_Bazaar.txt (+24/-0)
HACKING/Writing_plugins.txt (+6/-35)
HACKING/notebook.zim (+10/-6)
data/manual/FAQ.txt (+1/-1)
data/manual/Help/Commandline_Options.txt (+9/-9)
data/manual/Help/Config_Files.txt (+2/-0)
data/manual/Help/Custom_Tools.txt (+6/-4)
data/manual/Help/Export.txt (+4/-0)
data/manual/Help/Links.txt (+1/-1)
data/manual/Help/Templates.txt (+244/-88)
data/manual/Plugins.txt (+2/-1)
data/manual/Plugins/Ditaa_Editor.txt (+4/-4)
data/manual/Plugins/Mendeley_Citations.txt (+17/-0)
data/templates/html/Default.html (+83/-84)
data/templates/html/Default_with_index.html (+107/-82)
data/templates/html/Presentation.html (+12/-14)
data/templates/html/Print.html (+31/-29)
data/templates/html/SlideShow_(S5).html (+6/-4)
data/templates/html/ZeroFiveEight.html (+15/-16)
data/templates/latex/Article.tex (+8/-2)
data/templates/latex/Part.tex (+6/-3)
data/templates/latex/Report.tex (+4/-2)
data/templates/markdown/Default.markdown (+2/-0)
data/templates/rst/Default.rst (+2/-0)
debian/changelog (+6/-0)
tests/applications.py (+3/-0)
tests/calendar.py (+15/-14)
tests/data/TestTemplate.html (+90/-0)
tests/data/formats/export.html (+1/-1)
tests/data/formats/export.markdown (+1/-1)
tests/data/formats/export.rst (+1/-1)
tests/data/formats/export.tex (+1/-1)
tests/data/templates/html/Default.html (+75/-71)
tests/export.py (+651/-144)
tests/formats.py (+10/-54)
tests/gui.py (+2/-2)
tests/parsing.py (+93/-50)
tests/tags.py (+1/-1)
tests/templates.py (+688/-281)
tests/utils.py (+25/-0)
tests/versioncontrol.py (+2/-2)
tests/www.py (+1/-1)
tools/build_website.sh (+2/-0)
translations/ca.po (+942/-797)
translations/cs.po (+966/-819)
translations/da.po (+990/-841)
translations/de.po (+964/-815)
translations/el.po (+1025/-867)
translations/en_GB.po (+1007/-870)
translations/es.po (+966/-829)
translations/et.po (+1035/-891)
translations/fi.po (+3551/-0)
translations/fr.po (+972/-824)
translations/gl.po (+1013/-864)
translations/he.po (+947/-808)
translations/hu.po (+1005/-864)
translations/it.po (+963/-824)
translations/ja.po (+974/-831)
translations/ko.po (+959/-813)
translations/nb.po (+944/-801)
translations/nl.po (+948/-809)
translations/pl.po (+954/-817)
translations/pt_BR.po (+1002/-859)
translations/ro.po (+941/-807)
translations/ru.po (+956/-822)
translations/sk.po (+946/-802)
translations/sl.po (+951/-815)
translations/sr.po (+944/-802)
translations/sv.po (+974/-827)
translations/tr.po (+1032/-879)
translations/uk.po (+950/-805)
translations/zh_CN.po (+952/-809)
translations/zh_TW.po (+949/-805)
translations/zim.pot (+936/-792)
website/pages/downloads.txt (+1/-1)
website/template.html (+3/-0)
zim/__init__.py (+3/-3)
zim/applications.py (+21/-6)
zim/command.py (+8/-0)
zim/config/basedirs.py (+2/-1)
zim/errors.py (+3/-1)
zim/export/__init__.py (+90/-0)
zim/export/__main__.py (+60/-0)
zim/export/exporters/__init__.py (+37/-0)
zim/export/exporters/files.py (+195/-0)
zim/export/exporters/mhtml.py (+122/-0)
zim/export/layouts.py (+211/-0)
zim/export/linker.py (+273/-0)
zim/export/selections.py (+86/-0)
zim/export/template.py (+486/-0)
zim/exporter.py (+0/-329)
zim/formats/__init__.py (+73/-116)
zim/formats/__main__.py (+5/-7)
zim/formats/html.py (+11/-5)
zim/formats/latex.py (+2/-2)
zim/formats/markdown.py (+1/-1)
zim/formats/plain.py (+1/-0)
zim/formats/rst.py (+1/-1)
zim/formats/wiki.py (+29/-7)
zim/fs.py (+18/-7)
zim/gui/__init__.py (+15/-4)
zim/gui/applications.py (+9/-1)
zim/gui/clipboard.py (+4/-13)
zim/gui/customtools.py (+4/-0)
zim/gui/exportdialog.py (+274/-70)
zim/gui/pageview.py (+6/-4)
zim/gui/widgets.py (+33/-1)
zim/inc/MendeleyDesktopAPI.py (+357/-0)
zim/inc/MendeleyHttpClient.py (+228/-0)
zim/inc/xdot.py (+2/-1)
zim/main.py (+108/-51)
zim/notebook.py (+32/-8)
zim/objectmanager.py (+4/-3)
zim/parser.py (+162/-47)
zim/plugins/__init__.py (+291/-76)
zim/plugins/base/imagegenerator.py (+1/-10)
zim/plugins/calendar.py (+34/-30)
zim/plugins/diagrameditor.py (+3/-3)
zim/plugins/ditaaeditor.py (+3/-3)
zim/plugins/equationeditor.py (+13/-15)
zim/plugins/gnu_r_ploteditor.py (+8/-10)
zim/plugins/gnuplot_ploteditor.py (+10/-10)
zim/plugins/insertsymbol.py (+1/-1)
zim/plugins/linesorter.py (+1/-1)
zim/plugins/linkmap.py (+1/-1)
zim/plugins/mendeley.py (+192/-0)
zim/plugins/printtobrowser.py (+19/-4)
zim/plugins/quicknote.py (+13/-11)
zim/plugins/scoreeditor.py (+13/-12)
zim/plugins/sequencediagrameditor.py (+3/-3)
zim/plugins/sourceview.py (+7/-0)
zim/plugins/tags.py (+4/-1)
zim/plugins/versioncontrol/__init__.py (+1/-1)
zim/plugins/versioncontrol/git.py (+2/-2)
zim/plugins/versioncontrol/hg.py (+20/-57)
zim/templates.py (+0/-1086)
zim/templates/__init__.py (+180/-0)
zim/templates/expression.py (+447/-0)
zim/templates/expressionparser.py (+185/-0)
zim/templates/functions.py (+71/-0)
zim/templates/parser.py (+310/-0)
zim/templates/processor.py (+213/-0)
zim/utils.py (+45/-0)
zim/www.py (+55/-59)
To merge this branch: bzr merge lp:~grahamrow/zim/zim-mendeley
Reviewer Review Type Date Requested Status
Jaap Karssenberg 2014-01-21 Pending
Review via email: mp+202492@code.launchpad.net

Description of the change

Mendeley is a free cross-platform desktop reference and paper management program (http://www.mendeley.com/). This plugin allows you to insert mendeley citations that link directly to the Mendeley desktop application or to a DOI URL. This is accomplished by interfacing with the Mendeley application, which must be open for the plugin to function. Any reference style from http://www.zotero.org/styles/ may be chosen for the citations or bibliography, though numerical styles do not function properly at present.

The behavior and configuration are as follows:
- Inserts formatted citations (as simple Zim "link" objects) with links to either a users own Mendeley library (via the local mendeley:// protocol) or to a more universal DOI redirect (via http://dx.doi.org). In either case the unique Mendeley UUID is included in the URL such that the plugin can subsequently identify Mendeley links in order to construct a bibliography.
- The backend is a slightly modified version of the code used in Mendeley's own OpenOffice plugin (https://github.com/Mendeley/openoffice-plugin). It requires Mendeley be open for operation.

Preferences include:
1. Whether to insert dx.doi.org or mendeley:// links
2. What the citation format is to be
3. What the bibliography format is to be

Known limitations:
1. Numerical citation indices such as [1], [1,2], or [1-3] cannot be used. One reason is that this plugin does not keep track of previously inserted citations. Another reason is that range formatting such as [1-3] does not allow for the insertion of links corresponding to each paper in the range.
2. A unittest has not yet been implemented.

To post a comment you must log in.
Jaap Karssenberg (jaap.karssenberg) wrote :

Thanks ! Plugin looks good, no technical objections to merging it. I'm sure more people will want to used this.

Just hesitation a bit if it makes sense to have this in the standard distribution. Thinking that this might be a good candidate to start distributing plugins separately - e.g. as python .egg files or just .zip files the user can drop into place.

Or maybe I should just merge it now and take it out later again when I have such a distribution mechanism setup ...

lp:~grahamrow/zim/zim-mendeley updated on 2014-08-19
681. By Graham Rowlands on 2014-03-20

Merged into pyzim-refactor. A bit late now...

682. By Graham Rowlands on 2014-03-20

Merged with main zim branch

683. By Graham Rowlands on 2014-06-22

Pulled latest lp:zim and relocated Mendeley API code back to inc/ since it is vanilla code from Mendeley. This fixed an issue where the plugin manager thought the plugins/mendeleyAPI directory was itself a plugin, causing an error message to be generated when closing the preferences dialog

684. By Graham Rowlands on 2014-08-19

Pulled in Zim 0.61 code.

Unmerged revisions

684. By Graham Rowlands on 2014-08-19

Pulled in Zim 0.61 code.

683. By Graham Rowlands on 2014-06-22

Pulled latest lp:zim and relocated Mendeley API code back to inc/ since it is vanilla code from Mendeley. This fixed an issue where the plugin manager thought the plugins/mendeleyAPI directory was itself a plugin, causing an error message to be generated when closing the preferences dialog

682. By Graham Rowlands on 2014-03-20

Merged with main zim branch

681. By Graham Rowlands on 2014-03-20

Merged into pyzim-refactor. A bit late now...

680. By Graham Rowlands on 2014-01-21

Relocated Mendeley API code to plugins/mendeleyAPI. Added bibliography capability.

679. By Graham Rowlands on 2014-01-20

Updated reg. exps. for converting bib html to zim formatting.

678. By Graham Rowlands on 2014-01-20

Rebased to pyzim-refactor. Added rudimentary bibliography support.

677. By Graham Rowlands on 2014-01-20

Reverted inavertent changes to notebook.zim and updated documentation.

676. By Graham Rowlands on 2014-01-20

Initial commit of plugin

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CHANGELOG.txt'
2--- CHANGELOG.txt 2013-04-30 17:27:20 +0000
3+++ CHANGELOG.txt 2014-08-19 17:10:33 +0000
4@@ -4,6 +4,32 @@
5 This branch is the Python rewrite and starts with version 0.42.
6 Earlier version numbers for zim correspond to the Perl branch.
7
8+=== 0.61 - Thu 31 Jul 2014 ===
9+* Full refactoring of code for parsing and processing wiki syntax
10+ making parser easier to extend and document interface more scalable
11+* Full refactoring of code for plugin framework making plugins more
12+ flexible by defining decorators for specific application objects
13+* Full refactoring of code for exporting pages from zim
14+ - Now supports MHTML export format
15+ - Supports exporting multiple pages to a single file
16+ - Supports recursive export of a page and all it's sub-pages
17+ - Templates now support many more instructions and expressions
18+* Full refactoring of the code for parsing commandline commands and
19+ initializing the application
20+* New config manager code to make parsing and handling of config files
21+ more robust
22+* Merged new plugin for editing sequence diagrams by Greg Warner
23+* Improved the ToC plugin with floating widget
24+* Fixed unicode issue when calling external applications, and in
25+ particular for the hg and git commands
26+* Fixed support for unicode CamelCase word detection
27+* Fixed bug on windows with unicode user names in background process
28+ connection
29+* Changed "tags" plugin to show full page paths in the pre-tag view
30+* Added option for custom commands to replace the current selection
31+* Added keybindings for XF86Back and XF86Forward
32+* Many small fixes & patches from various persons that I forgot about *sorry*
33+* Added Finnish translation
34
35 === 0.60 - Tue 30 Apr 2013 ===
36 * In this release the required python version is changed from 2.5 to 2.6 !
37
38=== modified file 'HACKING/Coding_Style_&_Guidelines.txt'
39--- HACKING/Coding_Style_&_Guidelines.txt 2011-12-11 18:35:35 +0000
40+++ HACKING/Coding_Style_&_Guidelines.txt 2014-08-19 17:10:33 +0000
41@@ -1,21 +1,22 @@
42 Content-Type: text/x-zim-wiki
43 Wiki-Format: zim 0.4
44-Creation-Date: 2011-07-04T13:58:33+02:00
45+Creation-Date: 2014-07-31T21:06:39+02:00
46
47 ====== Coding Style & Guidelines ======
48+Created Thursday 31 July 2014
49
50 See the python style guide for best practices. Some items to keep in mind:
51
52-* GUI classes are only allowed to construct widgets and wire signals. Any actual manipulation of pages, notebooks etc. should go elsewhere.
53-* Signal handlers have a method name starting with "do_"
54+* GUI classes are only allowed to construct widgets and wire signals. Any actual manipulation of pages, notebooks etc. should go elsewhere.
55+* Signal handlers have a method name starting with "do_"
56 * Only use "assert" for checks that could be removed when code is stable, these statements could be optimized away
57 * Do not rely on ''__doc__'' on run time, this data could be optimized away
58 * Have a look e.g. at zim.parsing and zim.gui.widgets for common code snippets
59
60 **Other general guidelines**
61-* Writing test cases is good, full test coverage is better. Run "./test.py --cover" to get a coverage report.
62+* Writing test cases is good, full test coverage is better. Run "./test.py --cover" to get a coverage report.
63 * Wait with loading modules and constructing widgets untill they are really needed, this should keep startup speed reasonable
64-* Try to do slow operations that could be done asynchronous using the idle event, e.g loading the side pane index, or a list with search results, or even running an external command to check in a new version of a file.
65+* Try to do slow operations that could be done asynchronous using the idle event or a thread, e.g loading the side pane index, or a list with search results, or even running an external command to check in a new version of a file.
66
67 **Source code formatting**
68 * I use TABs (not spaces) with a tabstop set to the equivalent of 4 spaces, (most) lines should fit within 80 character with this setting.
69@@ -29,10 +30,3 @@
70 @emits: signal
71 @implementation: must implement / optional for sub-classes
72 '''
73-
74-
75-===== Gtk and GObject usage =====
76-
77-Zim is build on top of pygtk and pygobject. However, the classes outside the 'gui' namespace are not allowed to use the gtk libraries, and should work when only gobject is available. This potentially allows zim to run without a graphical interface, also it gives a strict boundry between UI and data layers in the class hierarchy. The exception are modules in the 'plugin' namespace, which can of course package their own UI components. However, these should check on initialization if we are running in graphical mode or not.
78-
79-Zim should be able to run at systems with at least Gtk+ version 2.6, however up to date systems can be expected to have at least Gtk+ version 2.20 or newer. Therefore Gtk+ 2.20 is required to use all functionality. This means that you can use features not available for Gtk < 2.20 but you should wrap them in a block that checks the Gtk version first. Critical functions that can not be disabled should not rely on Gtk > 2.6. Note that the Gtk API documentation specifies what version a function was introduced (if not specified it can be assumed to be present in all 2.x versions).
80
81=== removed file 'HACKING/Compatibility.txt'
82--- HACKING/Compatibility.txt 2010-01-19 18:41:47 +0000
83+++ HACKING/Compatibility.txt 1970-01-01 00:00:00 +0000
84@@ -1,21 +0,0 @@
85-Content-Type: text/x-zim-wiki
86-Wiki-Format: zim 0.4
87-Creation-Date: Sat, 04 Apr 2009 10:53:24 +0200
88-
89-====== Compatibility ======
90-
91-
92-===== Changes from zim < 0.42 =====
93-We depend on Gtk+ >= 2.6 instead of >= 2.4
94-
95-=== Config files ===
96-* ...
97-* Backward compatibility with files like notebook.list / repositories.list ?
98-* Removed profile options - use different XDG dirs if needed
99-
100-=== Templates ===
101-* Templates now use "[%-" and "-%]" to remove linebreaks around tags
102-
103-=== File links ===
104-* Dropped backward support for linking images in same namespace as dir with "./" now only works with "../" while "./" is reserved for attachments.
105-* Made paths starting with '/' always refer to the document root. Absolute paths now always need the file:// syntax. (Only in the case where a notebook really doesn't have a directory will the document root default to the filesystem root.)
106
107=== removed file 'HACKING/Config_Files.txt'
108--- HACKING/Config_Files.txt 2011-08-06 17:18:57 +0000
109+++ HACKING/Config_Files.txt 1970-01-01 00:00:00 +0000
110@@ -1,30 +0,0 @@
111-Content-Type: text/x-zim-wiki
112-Wiki-Format: zim 0.4
113-Creation-Date: Thu, 09 Apr 2009 21:23:30 +0200
114-
115-====== Config Files ======
116-
117-Zim uses several config files for different kinds of data:
118-
119-**XDG_CONFIG_HOME/zim/preferences.conf**
120-This file is used for all kinds of settings that are constant across notebooks. Mainly contains the contents of the "preferences" dialog and some hidden settings. Can be accessed in the code as ... FIXME
121-
122-If this file does not exist a default will be looked for first in the XDG_CONFIG_DIRS and then in the XDG_DATA_DIRS.
123-
124-**NOTEBOOK/notebook.zim**
125-Used to save notebook specific settings. This file has the same format as the other ".conf" files, but the extension ".zim" is used so we can have our own mimetype (application/x-zim-notebook). When we configure zim as the default application for this mimetype we get to open notebooks by clicking on this file in the notebook. Can be accessed in the code as ... FIXME
126-
127-**NOTEBOOK/.zim/state.conf**
128-Used for state parameters that are specific per notebook but do not belong to the notebook configuration. E.g. the window size, the last template used for export etc. We want to keep these out of notebook.zim because when the notebook is for example under version control, we do not want notebook.conf to change after every usage of zim. Can be accessed in the code as ... FIXME
129-
130-The files in NOTEBOOK/.zim/ can also be saved in XDG_CACHE/notebook_path/ when the notebook is read-only.
131-
132-**XDG_CONFIG_HOME/zim/accelmap**
133-Specifies the key bindings for menu items if these are customized. Format is a scheme variant used byt gtk. can be loaded and saved by ''gtk.accel_map_load()'' and ''gtk.accel_map_save()'' respectively.
134-
135-If this file does not exist a default will be looked for first in the XDG_CONFIG_DIRS and then in the XDG_DATA_DIRS.
136-
137-**XDG_CONFIG_HOME/zim/style.conf**
138-Specifies the look of the various text styles in the GUI.
139-
140-If this file does not exist a default will be looked for first in the XDG_CONFIG_DIRS and then in the XDG_DATA_DIRS.
141
142=== removed file 'HACKING/Development_Tools.txt'
143--- HACKING/Development_Tools.txt 2011-08-06 17:18:57 +0000
144+++ HACKING/Development_Tools.txt 1970-01-01 00:00:00 +0000
145@@ -1,48 +0,0 @@
146-Content-Type: text/x-zim-wiki
147-Wiki-Format: zim 0.4
148-Creation-Date: 2011-07-12T19:09:49+02:00
149-
150-====== Development tools ======
151-
152-We use the following resources to communicate on zim development"
153-
154-**Bug tracker:**
155-https://bugs.launchpad.net/zim
156-Also for feature requests
157-
158-**Development wiki:**
159-http://www.zim-wiki.org/wiki/
160-Has e.g. the planning for some of the developers
161-
162-**Mailing list:**
163-https://launchpad.net/~zim-wiki
164-For all your questions
165-
166-
167-===== Working with Bazaar =====
168-The zim code is kept under version control using the bazaar version control system. See the website for documentation on using this system: http://bazaar.canonical.com/en/
169-
170-The quick course, to get a copy of the zim code:
171-
172- '''
173- bzr branch lp:zim
174- '''
175-
176-To check in some changes (don't forget to give some meaningful changelog)
177-
178- '''
179- bzr add
180- bzr commit
181- '''
182-
183-
184-To create a patch that can be mailed:
185-
186- '''
187- bzr send -o some-description.patch
188- '''
189-
190-Alternatively you can publish your branch directly on the launchpad website and file a merge proposal.
191-
192-===== Development scripts =====
193-When you are working with the development code check the folder "''./tools/''" for some useful scripts.
194
195=== removed file 'HACKING/Formats_modules.txt'
196--- HACKING/Formats_modules.txt 2011-06-05 21:51:36 +0000
197+++ HACKING/Formats_modules.txt 1970-01-01 00:00:00 +0000
198@@ -1,5 +0,0 @@
199-
200-TODO: more info about writing format modules
201-
202-Each module in zim.formats should contains exactly one subclass of DumperClass and exactly one subclass of ParserClass (optional for export formats). This includes imports, so do not import other classes that derive from these base classes directly into the module. When zim loads the format it finds the correct parser and dumper classes by looking at the base class of each class object.
203-
204
205=== added file 'HACKING/Gtk_and_GObject_and_signals.txt'
206--- HACKING/Gtk_and_GObject_and_signals.txt 1970-01-01 00:00:00 +0000
207+++ HACKING/Gtk_and_GObject_and_signals.txt 2014-08-19 17:10:33 +0000
208@@ -0,0 +1,22 @@
209+Content-Type: text/x-zim-wiki
210+Wiki-Format: zim 0.4
211+Creation-Date: 2014-07-31T21:07:55+02:00
212+
213+====== Gtk and GObject and signals ======
214+
215+Zim is build on top of pygtk and pygobject. However, the classes outside the 'gui' namespace are not allowed to use the gtk libraries, and should work when only gobject is available. This potentially allows zim to run without a graphical interface, also it gives a strict boundry between UI and data layers in the class hierarchy. The exception are modules in the 'plugin' namespace, which can of course package their own UI components. However, these should check on initialization if we are running in graphical mode or not.
216+
217+Zim should be able to run at systems with at least Gtk+ version 2.6, however up to date systems can be expected to have at least Gtk+ version 2.20 or newer. Therefore Gtk+ 2.20 is required to use all functionality. This means that you can use features not available for Gtk < 2.20 but you should wrap them in a block that checks the Gtk version first. Critical functions that can not be disabled should not rely on Gtk > 2.6. Note that the Gtk API documentation specifies what version a function was introduced (if not specified it can be assumed to be present in all 2.x versions).
218+
219+===== Signals =====
220+Keep in mind that with each "**connect()**" an object reference is created. The reference is kept by the object that is being connected to and is only broken when that object is being destroyed. So if you do
221+
222+ object_A.connect('some-signal', object_B.some_method)
223+
224+then object_B will not be destroyed as long as object_A is alive. On the other hand, if object_A is destroyed, object_B simply doesn't get any signals anymore.
225+
226+This seems not to be a problem when you e.g. connect a button signal within a dialog object. Reason is probably that the circular reference is broken when the dialog is destroyed. But it is a problem when e.g. a dialog connects to an external object, or a plugin connect to the main interface objects. In those cases signals need to be explicitly disconnected when you close the dialog or remove the plugin.
227+
228+See the **ConnectorMixin** class in zim.signals for some convenience methods for dealing with this.
229+
230+A special not about **connect_object()**. In the Python API it looks like this method is only intended to swap the object argument when calling the callback, however in the C API it is mentioned that this method also results in an additional object reference and it has a bug in current versions with cleaning up those references. So it is better avoided.
231
232=== added file 'HACKING/HACKING.txt'
233--- HACKING/HACKING.txt 1970-01-01 00:00:00 +0000
234+++ HACKING/HACKING.txt 2014-08-19 17:10:33 +0000
235@@ -0,0 +1,29 @@
236+Content-Type: text/x-zim-wiki
237+Wiki-Format: zim 0.4
238+Creation-Date: 2014-07-31T20:56:17+02:00
239+
240+====== HACKING ======
241+
242+This notebook contains random notes and thoughts that may be useful when you want to change source code for zim.
243+
244+It is not a complete tutorial or something like that for working with the zim object API. In fact all real API documentation can be found directly in the python modules. Anyway, the code is always the most up to date documentation for development.
245+
246+When you are working with the development code check the folder "./tools/" for some useful scripts.
247+
248+
249+In addition we use the following resources to communicate on zim development:
250+
251+Bug tracker:
252+https://bugs.launchpad.net/zim
253+Also for feature requests
254+
255+Development wiki:
256+http://www.zim-wiki.org/wiki/
257+Has e.g. the planning for some of the developers
258+
259+Mailing list:
260+https://launchpad.net/~zim-wiki
261+For all your questions
262+
263+IRC Channel:
264+#zim-wiki on Freenode IRC network. (Access from your web browser at https://webchat.freenode.net/?channels=%23zim-wiki .)
265
266=== removed directory 'HACKING/Ideas'
267=== removed file 'HACKING/Ideas.txt'
268--- HACKING/Ideas.txt 2010-01-19 18:41:47 +0000
269+++ HACKING/Ideas.txt 1970-01-01 00:00:00 +0000
270@@ -1,108 +0,0 @@
271-Content-Type: text/x-zim-wiki
272-Wiki-Format: zim 0.4
273-Creation-Date: Sat, 04 Apr 2009 10:56:09 +0200
274-
275-====== Ideas ======
276-
277-How about a plugin to have a quick "scratch" pad, that concats notes to a certain page
278-+ commandline option to trigger this by a desktop keybinding (more sticky note like)
279-Or put these stickies in a special namespace, with a top level list view
280--> file scratch + timestamps + separator
281-
282-Similar integration with a special window for databse plugin would make zim into a custom address book etc.
283-
284------
285-
286-Entries can now have inline icons now - see
287-http://library.gnome.org/devel/gtk/unstable/GtkEntry.html :)
288-
289-Using these for browse buttons etc will be very slick.
290-Check gtk version in form code and fallback to adding a small button
291-behind the entry.
292-
293-How does a browse dialog for pages / namespaces look ??
294-
295-=== file links for www ===
296-
297-When serving:
298-* attachments should be served - link needs to be from server root
299-* files under doc root (if any) should be served - idem
300-* bullet icons should be served
301-* all other files get linked as file:/// urls
302-
303-When exporting:
304-* attachments should be copied - link should be reative file path from html file
305-* doc root never needs to be copied, absolute path or use doc_root url setting
306-* other files maybe copied or linked with absolute path
307-
308-== Get a self hosting documentation browser ==
309-
310-For example './zim.py zim/' should show all modules and classes in the treeview
311-clicking a page should show API documentation. Using the xdot view should show
312-relations between classes.
313-
314-* Store that generates pydoc like output for a python source file
315-* Commandline option to force default store ?
316-* Link types to distinguise inheritance and collaboration
317-
318-
319-== File format ==
320-
321-Header for mtime in pages causes conflicts when merging versions :S
322-Get rid of all headers except the content-type line defining a zim version.
323-
324-Move to a new extension (.wkz ?) and assign the proper mimetype and icon.
325-Move notebook.zim to hidden ".notebook.zim" (not in .zim dir - .zim should
326-be ignored when versioning).
327-Add zim format version to notebook as well. When we detect older version
328-< 0.42, prompt user to upgrade the notebook to new format and move the files
329-around. Do automated backup first! Do we need to keep backward compat mode ??
330-
331-( Code to store the tar.gz backup can be used as well for e.g. for a
332-backup plugin, or to publish read-only notebooks. )
333-
334-== Locking ==
335-
336-Do not use mtime or lock, even mtime fails on network drives.
337-Use lock directory (similar to bzr) and if locked prompt user
338-to break lock or open read-only. If detected that other
339-process broke lock, warn user and switch to read-only.
340-
341-Assume single user usage, so this is only prevention.
342-For multi-user setups need network client etc.
343-
344-== mtime stuff ==
345-
346-After reading a page get the mtime and keep the original raw file in memory.
347-When at writing timestamps do not match, calculate md5 of original in memory
348-and of the file on disk. Only prompt user if md5 sums really do not match.
349-This makes us more reliable for network file systems etc.
350-
351-== file buffers ==
352-
353-The idea to read /write diretly from and to files from e.g. a format was
354-a pre-mature optimalization. Adapt the Buffer object to be a in-memory buffer
355-of an existing file. Pass file data around in memory after reading.
356-
357-== Daemon ==
358-
359-Same daemon architecture as we have now. However need to maintain interface
360-type for clients, so server and gui can have same notebook open.
361-Need daemonclient object class and interfaceclient class.
362-Also need a stub daemon class for stand alone and testing.
363-
364-== Network client ==
365-
366-If we want to run a network version and decide to fix all the related
367-concurency and authentication stuff the proper way would be to extend the
368-server with the needed interfaces. E.g. plain source pages and an SQL
369-query interface with some plain text result page.
370-
371-== Autoformatting ==
372-
373-Instead of hardcoding autoformatting maintain a list of regexes and
374-actions that is aither user editable or at least editiable from a
375-plugin script. Implement in a generic fashion.
376-
377-+ need to be able to enable / disable sets based on prefs
378-
379
380=== removed file 'HACKING/Ideas/Attachments.txt'
381--- HACKING/Ideas/Attachments.txt 2010-01-19 18:41:47 +0000
382+++ HACKING/Ideas/Attachments.txt 1970-01-01 00:00:00 +0000
383@@ -1,23 +0,0 @@
384-Content-Type: text/x-zim-wiki
385-Wiki-Format: zim 0.4
386-
387-====== Attachments ======
388-Created Friday 18 December 2009
389-
390-Attachments live in the same folder as the notes. This limits the use of text files as attachments as zim will confuse them for pages and try to open them. Sometimes that is not an issue other times it may fail.
391-
392-To distinguish better between notes and attachments we could check the content, e.g. match on the header of a note. However this may have the disadvantage that we have a name conflict between page names and attachment names.
393-
394-Alternatively we can use a special file extension for zim (like .zim or .zim.txt). This has been discussed extensively, but no consensus reached. Also this can not for 100% exclude the possibility of conflicts.
395-
396-So we need to be able to deal with conflicts anyway. In that case checking page contents is not a bad idea. Would need a property for this behavior for backward compatibility. Maybe even a user visible property "all text files are pages".
397-
398-
399-Useful features when we implement an attachment manager
400-* Rename, Move, Delete
401-* Create from template (like filebrowser)
402-* Open with application - would need vfs or check out + modify + checkin ?
403-
404-
405-Another issue with attachments is that we can generate a lot of orphaned attachments, e.g. due to the equation editor. Tool to clean up all unlinked attachments could be useful.
406-
407
408=== removed file 'HACKING/Ideas/Database_and_Album.txt'
409--- HACKING/Ideas/Database_and_Album.txt 2010-01-19 18:41:47 +0000
410+++ HACKING/Ideas/Database_and_Album.txt 1970-01-01 00:00:00 +0000
411@@ -1,77 +0,0 @@
412-Content-Type: text/x-zim-wiki
413-Wiki-Format: zim 0.4
414-
415-====== Database and Album ======
416-Created Wednesday 23 December 2009
417-
418-Database
419-
420- * Both form based notebooks and album notebooks are databses
421- * Both are record based
422- * Image name itself is record value, but also meta data like EXIF stuff..
423- * Key features that make this different from other pages
424-
425- * Each page has a uniform set of attributes - the record columns
426- * Some special UI layout will be needed to show these attributes
427- * Search interface can be extended to know about these attributes
428- * Either a column based record list or thumbnail view as browsing interface replaces index sidepane
429- o for thumbnails left placement could work, for table it should be top
430- * Page name becomes much less relevant - in fact only needed for matching rest of zim
431- o key based on arbitrary ID (photo number / some other ID number)
432- o ID needs to be and remain unique because of linking - no "rename"
433- o name in side pane does not need to match this unique ID..
434- * Need to configure properties per namespace
435- * Storage may use flat file or even db file instead of separate text files
436- * Still want to associate free form text to make it wiki-like -- but pages can have multiple free-form text fields, with templates for the text..
437- * Next logical step is to have separate namespaces as "tables" and link between properties of seperate tables
438- o From there you go to business logic, like enforce relationships ... ewww :S
439-
440- * In core of zim
441- o Function to deal with property tables / forms in wiki source
442- o Fucntion to deal with including these in HTML export
443- o Page property to show you can not rename it - is moving allowed than ? guess not - show error after drag-drop in side pane
444- o Side pane should support diff between real name and name to display (also do the right thing when pasting by draggin link into editor)
445- + Or allow plugin to hide namespace from side pane ?
446- o commandline argument to load plugin early, so it can subclass GTKInterface
447- + e.g. PhotoAlbum interface could derive from GTKInterface, but hide some widgets and initialize a album notebook by default
448- + but really this should be determined by the "profile" option
449- * In plugin
450- o Control extra widgets for a namespace - e.g. column view or thumbnail view
451- o Control display of a single page - e.g. form or image view
452- * Database plugin
453- o Allow user to specify form columns and layout
454- o Add table view above page
455- o Add search function ?
456- o Let user choose way to store, file per item or single file
457- * Album plugin
458- o Add thumbnail view
459- o Make pages render images
460- o Store all meta data in a flat file by default
461- o Think about fullscreen view / slideshow
462- o Think about using saved search to make collections and still show correct controls
463- + Saved search is just a page with links I guess ? Or show as a namespace ?
464- # prefer list of links, as namespace is messing with uniqueness
465- o Think about nice gallery template to export photos to webpage
466- * Book plugin
467- o Build on database plugin
468- o Add some lib to extract to ISBN numbers (which one)
469- o Add import from list of scanned ISBN numbers
470- + Maybe put import / export CSV in dabase plugin features
471-
472- * notebook property profile="photoalbum"
473- * Makes sure photoalbum plugin is loaded
474- * Makes sure plugin is managing root namespace
475- * Changes MainWindow to fine tuned view for photo collection
476- * plugin has features both use embedded in a other notebook and this "stand alone" usage
477- + first develop stand alone use to avoid complexity
478-
479-So concrete to add a album application:
480-
481- * Write the backend for it using a flat file (XML ?) + the photo directy - allow subdirectories
482- * Have notebook support multiple children for real and add configuration options for it
483- * Add plugin to render pages in such a namespace differently (need hooks for this in pageview / mainwindow)
484-
485- o First hack is to just look at first element of parsetree to be an image - optimise later with definition table logic
486- * Add support for profile option to kickstart plugin
487- * Add advanced option in "new notebooks" to assign profile
488-
489
490=== removed file 'HACKING/Ideas/Export_OpenOffice.txt'
491--- HACKING/Ideas/Export_OpenOffice.txt 2010-01-19 18:41:47 +0000
492+++ HACKING/Ideas/Export_OpenOffice.txt 1970-01-01 00:00:00 +0000
493@@ -1,15 +0,0 @@
494-Content-Type: text/x-zim-wiki
495-Wiki-Format: zim 0.4
496-
497-====== Export OpenOffice ======
498-Created Wednesday 23 December 2009
499-
500-* just zip file with XML, could handle it with etree library - but even easier as plain text
501- * use template, open zip file, look for template instructions, fill them in
502- * Need OO specific XML output - see: http://books.evc-cit.info/odbook/book.html
503-
504-While at it create base class for import / export xml formats
505-* have a map with 1-to-1 translations
506-* allow methods for specific tags that need conversion logic
507-* import can ignore unknown tags (optional ?)
508-
509
510=== removed file 'HACKING/Ideas/Forms.txt'
511--- HACKING/Ideas/Forms.txt 2010-01-19 18:41:47 +0000
512+++ HACKING/Ideas/Forms.txt 1970-01-01 00:00:00 +0000
513@@ -1,14 +0,0 @@
514-Content-Type: text/x-zim-wiki
515-Wiki-Format: zim 0.4
516-Creation-Date: Wed, 24 Jun 2009 23:45:10 +0200
517-
518-====== Forms ======
519-Created Wednesday 24 June 2009
520-
521-Clearly we need per page templates. To be able to edit these from zim they should be valid wiki pages. This is possible because we can include template instructions without problem.
522-
523-Open issue: how to layout forms with gtk entries as input in wiki ? Maybe just consider these embedded widgets and use some kind of "insert form" menu item to create them ?
524-
525-Would be able to use definition lists like "author:: foo" or similar, but would not allow e.g. right aligned image next to fields.
526-
527-Open issue: how to set initial cursor position ?
528
529=== removed file 'HACKING/Ideas/Inline_Latex.txt'
530--- HACKING/Ideas/Inline_Latex.txt 2010-01-19 18:41:47 +0000
531+++ HACKING/Ideas/Inline_Latex.txt 1970-01-01 00:00:00 +0000
532@@ -1,44 +0,0 @@
533-Content-Type: text/x-zim-wiki
534-Wiki-Format: zim 0.4
535-Creation-Date: Unknown
536-
537-====== Inline latex ======
538-Created Tuesday 24 March 2009
539-
540-Need support for inline objects that render images on the fly
541-
542-Object contains latex code, takes MD5, does a lookup in .zim/images/
543-If the image does not exist it is generated by calling latex and
544-dvi2png and saves under MD5
545-
546-Same can be done for equation edited with the equation editor
547-
548-Advantage is that copying an equation will do the copy on write for
549-the image automatically.
550-
551-Define a cleaning action, e.g. when re-building the index, which
552-removes all these cached images
553-Also when editing with the editor it can clean up previous image (even
554-if it is used elsewhere it will be re-created anyway when needed)
555-
556-Same appraoch can be used for e.g. for graphs and diagrams.
557-Abstract the generator such that same editor dialog can serve for
558-different inline formats
559-Editor should use gtksourceview when available...
560-
561-Alternative the editor should allow saving as an external file by
562-specifying a name, link as object, not as image. But allow image link
563-for backward compatibility.
564-
565-Also for external files we can save the image by MD5 sum.
566-
567-
568-* How to mark inline blocks in the wiki source ?
569- * Support "$$ ... $$" for latex formulas ?
570- * Support some block construct like "''{{{ latex\n ... \n}}}\n''"
571- * distuinguise from block of latex for syntax highlighting ...
572- * research wikipedia and creole syntax for such a block
573-* How to trigger image generation when rendering the page ?
574-* How to trigger image generation or copying when e.g. exporting to HTML ?
575-
576-
577
578=== removed file 'HACKING/Ideas/Parse_Tree.txt'
579--- HACKING/Ideas/Parse_Tree.txt 2010-01-19 18:41:47 +0000
580+++ HACKING/Ideas/Parse_Tree.txt 1970-01-01 00:00:00 +0000
581@@ -1,13 +0,0 @@
582-Content-Type: text/x-zim-wiki
583-Wiki-Format: zim 0.4
584-
585-====== Parse Tree ======
586-Created Wednesday 23 December 2009
587-
588-Make our internal format more HTML like. Still want to keep whitespace within paragraphs - encoding every extra space, tab and newline with an extra tag seems silly. So make every paragraph like a <pre>
589-
590-However for spaces between paragraphs we maybe need to be more flexible - rendering html now gets too many additional <br> elements. Use hint for number of newlines like with perl version ? Have parsetree builder clean up redundant whitespace after heading etc. Wrap everything in para elements to make structure more clear. (and properly nest list elements)
591-
592-How about indented part within a paragraph? Probably should use generic block element for this. Now it could be seen as a separate para, what is not necessarily what we want.
593-
594-Also Use "i" and "b" instead of "emphasis" and "strong" ??
595
596=== removed file 'HACKING/Ideas/Profiles.txt'
597--- HACKING/Ideas/Profiles.txt 2010-01-19 18:41:47 +0000
598+++ HACKING/Ideas/Profiles.txt 1970-01-01 00:00:00 +0000
599@@ -1,12 +0,0 @@
600-Content-Type: text/x-zim-wiki
601-Wiki-Format: zim 0.4
602-Creation-Date: Thu, 06 Aug 2009 18:40:25 +0200
603-
604-====== Profiles ======
605-Created Thursday 06 August 2009
606-
607-If we want preferences per notebook the right way to go would be by using profiles. This allows grouping notebooks which will share the same preferences set. Also different users can still have different preferences for the same notebook.
608-
609-* notebook has a "profile" name
610-* each profiles has it's own preferences file
611-* if the profiles doesn't exist, copy default
612
613=== removed file 'HACKING/Ideas/SavedSearch.txt'
614--- HACKING/Ideas/SavedSearch.txt 2010-01-19 18:41:47 +0000
615+++ HACKING/Ideas/SavedSearch.txt 1970-01-01 00:00:00 +0000
616@@ -1,30 +0,0 @@
617-Content-Type: text/x-zim-wiki
618-Wiki-Format: zim 0.4
619-Creation-Date: Thu, 30 Jul 2009 21:37:25 +0200
620-
621-====== SavedSearch ======
622-Created Thursday 30 July 2009
623-
624-Saved Search would be generic interface used for e.g. by [[Task List]] plugin or a [[Tags]] plugin. It would use a table in the index database to account which page matches what search. This table would be updated all the time by hooking the queries into updates of the database.
625-
626-Need a way to hook query objects to the index update routines and then get pages that matched the query from the index.
627-
628-As a seperate feature it would be nice to have special pages, which are read-only and have links to all matches. Or should this go into a special UI element, e.g. in the side pane ?
629-
630-===== Implementation =====
631-* Add two tables
632- * one joining page ids and property ids
633- * one mapping property names to ids
634-* Have a method to register queries versus
635-* Each query determines the match for a single property name
636-* Optimise search to use these properties
637- * Special keyword
638-
639-E.g. the task list could register the "has_tasks" property, or even "has_tasks" and "has_open_tasks"
640-And the tags plugin could register a property "tag_foo" for a tag "@foo".
641-Special saved search pages would have a property name like "matches_search001"
642-etc.
643-
644-In SQL we can query multiple properties at the same time using the "INTERSECT" operator.
645-
646-
647
648=== removed file 'HACKING/Ideas/Search.txt'
649--- HACKING/Ideas/Search.txt 2010-01-31 10:58:51 +0000
650+++ HACKING/Ideas/Search.txt 1970-01-01 00:00:00 +0000
651@@ -1,45 +0,0 @@
652-Content-Type: text/x-zim-wiki
653-Wiki-Format: zim 0.4
654-Creation-Date: Thu, 30 Jul 2009 22:06:16 +0200
655-
656-====== Search ======
657-Created Thursday 30 July 2009
658-
659-* search should be able to find orphaned pages
660-* idem for non-existent pages (linked but non existing)
661-* allow search query for all backlinks to a namespace recursively (tags plugin)
662-
663-Once we have support for complex search queries we should use this to define selections of pages. These are basically saved searches. E.g. "all pages linked by ":index" or "all pages linking to :tag:favourite".
664-
665-* We can use selections to define a group of pages for export.
666-* We can use selections to filer in the TODOList dialog.
667-
668-Typical use cases:
669-* Look for pages matching a word
670-* Look for page matching a word in a certain namespace
671-* Look for back links
672-
673-Advanced use cases
674-* Look for pages that are not linked from anywhere
675-* Look for dead links
676-
677-Since we also want to use search queries to collect pages for export it also makes sense to be able to list all pages linked by a certain page.
678-
679-===== Syntax =====
680-
681-See Usage:Searching for query syntax description.
682-
683-Inspiration for syntax from http://xesam.org/main/XesamUserSearchLanguage
684-
685-Main points are:
686-* Be easy and simple to use
687-* Any Google-like search should do the expected
688-* Don't cater for complex queries (hence sub-queries/braces are left out)
689-
690-So just 3 logical ops: AND, OR and NOT and some aliases (and, &&, +, or, ||, -). No grouping or sub-queries and only one quoting style. Only difference is that we need a space after a field selector because page names can also contain ":".
691-
692-For the average user the complexity allow by the current syntax will be more than enough.
693-
694-===== Suggestions =====
695-* Use proximity to set ranking (e.g. all terms less than 20 words apart and less than 50 words apart)
696-
697
698=== removed file 'HACKING/Ideas/Separate_Window.txt'
699--- HACKING/Ideas/Separate_Window.txt 2010-01-19 18:41:47 +0000
700+++ HACKING/Ideas/Separate_Window.txt 1970-01-01 00:00:00 +0000
701@@ -1,15 +0,0 @@
702-Content-Type: text/x-zim-wiki
703-Wiki-Format: zim 0.4
704-
705-====== Separate Window ======
706-Created Wednesday 23 December 2009
707-
708-* Need to be able to manage multiple pageviews
709- * make sure we handle all by signals
710- * ui needs to track open pages instead of single page attribute -> check who accesses ui.page
711- * pageviews should coordinate to use the same model if they open the same page
712-* Need class for secondairy window + own menubar / toolbar definition
713-* Need option to split the window (horizontal / vertical) - once or multiple times ?
714-* Need to decide up front how this combines with overloading pageview for special page types
715-
716-Quick hack would be to have separate windows that are strictly read-only and open links in the main window.
717
718=== removed file 'HACKING/Ideas/Spell_Checking.txt'
719--- HACKING/Ideas/Spell_Checking.txt 2010-01-19 18:41:47 +0000
720+++ HACKING/Ideas/Spell_Checking.txt 1970-01-01 00:00:00 +0000
721@@ -1,27 +0,0 @@
722-Content-Type: text/x-zim-wiki
723-Wiki-Format: zim 0.4
724-
725-====== Spell Checking ======
726-Created Sunday 29 November 2009
727-
728-Problem: Getting language config per notebook / page / paragraph correct is difficult - most office suites this is still a sore point
729-
730-Solution: Instead of specifying the language determine it by heuristics
731-
732-* Installed dictionaries are probably the language the user is likely to use
733-* gtkspell has access to both the dictionaries and the text buffer
734-* Heuristics can determine language per paragraph or even per sentence. Just lookup in multiple dictionaries and take language with least errors...
735-* Pango has function to detect begin & end of paragraph or sentence
736-* As a further refinement e.g. quoted words can be supposed to be in an other language
737-* Popup menu for spell suggestions should indicate the language it thinks we are using.
738-
739-This would properly belong in the gtkspell library, so all gtk application can benefit. Gtkspell3 is dead in the water (see mailing list archive / cvs history) - so gtkspell2 is the proper target.
740-
741-Gtkspell uses enchant, which manages the dictionary backends. So gtkspell is just the abstraction layer between gtktextbuffer and enchant.
742-
743-If we want to prototype without modifying gtkspell, we should re-implement the gtktextbuffer interaction and interface with enchant directly. Need to check if pyEnchant bindings are functional. If this works it would still be nice for the community to patch gtkspell itself as well, so others can benefit.
744-
745-Give Bert credits for the radical idea of getting rid of language as a document option.
746-
747-
748-Note: the latest version of gtkspellcheck seems to add a menu to switch languages. This is a good idea as well, to be able to force a certain dictionary to be used. This in part removes the urgency of fixing detailed language settings in zim notebooks.
749
750=== removed file 'HACKING/Ideas/Storage.txt'
751--- HACKING/Ideas/Storage.txt 2010-01-19 18:41:47 +0000
752+++ HACKING/Ideas/Storage.txt 1970-01-01 00:00:00 +0000
753@@ -1,24 +0,0 @@
754-Content-Type: text/x-zim-wiki
755-Wiki-Format: zim 0.4
756-Creation-Date: Thu, 30 Jul 2009 21:34:55 +0200
757-
758-====== Storage ======
759-Created Thursday 30 July 2009
760-
761-Ideas for new storage formats. Also see [[Attachments]]
762-
763-Formatting
764-* Markdown
765-* Mediawiki
766-* Dokuwiki
767-
768-Storage
769-* IMap
770-* Mediawiki remote interface ?
771-* Dokuwiki RPC interface
772-
773-Alternatively any storage that can be mounted through e.g. a fuse layer will just work out of the box. But may need to adjust directory layout details for other wiki types.
774-
775-Pages are abstract enough that any remote storage will work. However we do not include a full file manager for attachments (yet). So to make a truly remote some work would be needed to manage remote attachments as well.
776-
777-So we need to be able to deal with conflicts anyway. In that case checking content is not a bad idea. Have a notebook property to flag for backward compatibility and have a user visible property "all text files are pages" so peopel can choose depending on their usage. Woudl also need a one time upgrade action for old notebooks.
778
779=== removed file 'HACKING/Ideas/Synchronization.txt'
780--- HACKING/Ideas/Synchronization.txt 2010-01-19 18:41:47 +0000
781+++ HACKING/Ideas/Synchronization.txt 1970-01-01 00:00:00 +0000
782@@ -1,23 +0,0 @@
783-Content-Type: text/x-zim-wiki
784-Wiki-Format: zim 0.4
785-
786-====== Synchronization ======
787-Created Friday 18 December 2009
788-
789-Multiple ways to do synchronisation
790-* Version control push / pull (bazaar, subversion, git ...)
791-* rsync, unison, ...
792-* email
793-* webmail (e.g. gmail)
794-* dedicated http / webdav server
795-
796-The first two options are preferred because we can leave all the hassle of doing the actual sync or merge to a specialized program.
797-
798-Would need a trigger to determine when to sync. Probably the same as for autosaving a version in version control. E.g. when leaving the program, daily, weekly, on an explicit user action.
799-
800-Would like a generic plugin for this similar to versioncontrol and probably integrated with it when both are loaded. THe synchronization plugin could handle various backends.
801-
802-Would be nice if we can syncronize by sending diffs to email.
803-* You don't have to have a server online (and webmail accounts are cheap)
804-* If you can send the email through HTTP including proxy settings you can work from any network without to much hassle
805-I think bazaar can generate the diffs.
806
807=== removed file 'HACKING/Ideas/Tables.txt'
808--- HACKING/Ideas/Tables.txt 2010-01-19 18:41:47 +0000
809+++ HACKING/Ideas/Tables.txt 1970-01-01 00:00:00 +0000
810@@ -1,71 +0,0 @@
811-Content-Type: text/x-zim-wiki
812-Wiki-Format: zim 0.4
813-
814-====== Tables ======
815-Created Thursday 07 January 2010
816-
817-===== Wiki format =====
818-
819-The dokuwiki format looks quite OK - documented [[http://www.dokuwiki.org/syntax|here]].
820-
821-'''
822-^ Heading 1 ^ Heading 2 ^ Heading 3 ^
823-| Row 1 Col 1 | Row 1 Col 2 | Row 1 Col 3 |
824-| Row 2 Col 1 | some colspan (note the double pipe) ||
825-| Row 3 Col 1 | Row 3 Col 2 | Row 3 Col 3 |
826-'''
827-
828-
829-'''
830-| ^ Heading 1 ^ Heading 2 ^
831-^ Heading 3 | Row 1 Col 2 | Row 1 Col 3 |
832-^ Heading 4 | no colspan this time | |
833-^ Heading 5 | Row 2 Col 2 | Row 2 Col 3 |
834-'''
835-
836-
837-Only disadvantage is that it may not be easy to parse this unambiguously from a page. Some "frame" around it ? E.g.
838-
839-
840-
841-'''
842-{{{Table:
843-| Foo | Bar | Baz |
844-| Foo | Bar | Baz |
845-}}}
846-'''
847-
848-
849-===== Rendering =====
850-Can either be done with a gtk.TreeView or with a gtk.Table of TextViews. The later is preferred because it allows full use of formatting.
851-
852-On the other hand we could choose to have a TreeView based table that explicitly does not allow formatting. This sounds on-zim like, but makes implementation a lot easier.
853-
854-In both cases we have an object embedded into the parent pageview. THis means it needs to be recognized as such when dumping the parse tree. Also the cursor in the parent view will step "over" the table instead of into it unless we overload cursor movements. Similarly we need to do something with the tab and shift-tab key bindings to cycle the cells.
855-
856-Will need a full set of controls to create and modify the table layout, resize cells, delete, insert etc. etc. complicated.
857-
858-Can we somehow unify this with outlining lists? E.g. an embedded treeview could render lists, but not in a direct editable way. The problem here is that while the cell renderer can render almost any formatting, it doesn't do cursors. **NO**, user interface for outlining is quite different from user interface for tables.
859-
860-A radical different approach is to somehow control tab stops over a region and essentially consider the table more like a list with tabs in them. **NO**, this will not allow for multi line cells. Also this you can not set tabs per line, this is per region. (Also we might need some tab control to make lists render somewhat nicer.)
861-
862-Do we need to do rendering of the cells ourselves (in effect replace gtk.Table with a custom widget) ? Probable it will be difficult to force exact cell sizes on a cell unless we do this. But as long as we assume people insert their own line breaks it is OK.
863-
864-See gtk.TextView.set_accepts_tab for fixing tabbing between cells.
865-
866-Benchmark showed textview is not so memory heavy as one might think. Of course Treeview based is lighter...
867-
868-===== Open issues =====
869-* What are the primary use cases ? little text per cell (number, items etc.) or full formatting per cell ?
870- * Let's assume it should be free-form, but not huge (we have spreadsheet programs for that)
871-* Do we need / want table headings ? Seems nice to have them for structuring content, but would complicate setting text styles for those cells. If we do probably overriding the format in those cells is the way to go.
872-* Do we want rowspan / colspan ? We can allow it when based on a gtk.Table object, but does it add much ? Well, it does allow for nice spanning headers. But also adds more complexity.
873-* What happens when you paste a heading into a table - or a table into a table ??
874- * Prevent this one way or the other, only allow inline formats and images
875-
876-
877-
878-==== Related ====
879-* To avoid people mis-purposing tables, make a widget for images setting the caption in a box below the image.
880-
881-
882
883=== removed file 'HACKING/Ideas/Tags.txt'
884--- HACKING/Ideas/Tags.txt 2010-01-19 18:41:47 +0000
885+++ HACKING/Ideas/Tags.txt 1970-01-01 00:00:00 +0000
886@@ -1,29 +0,0 @@
887-Content-Type: text/x-zim-wiki
888-Wiki-Format: zim 0.4
889-
890-====== Tags ======
891-
892-One way to work with tags is to link pages to a special tag page and use back links to track all pages that have this tag. The main thing a tag plugin would do is add UI elements for this way of working.
893-
894-* Autocomplete tags, e.g. drop down if you type "@" with known tags
895-* Autolink tags when they are typed
896-* Make tag pages really special and autogenerate them with a list of links
897- * This could be build on top of [[SavedSearch]]
898-* Tag based side pane view to replace hierarchy view
899- * This would mean caching tags per page in the index database
900-* Integrate tags better in the search dialog ?
901- * special key word - register it somewhere and use property table from [[SavedSearch]]
902-* Integrate tags better in the Task List dialog ?
903- * apply page tag to all tasks in that page ?? might be confusing
904-* Add a tag cloud somewhere ?
905-
906-Really need good interface to browse by tag - especially for photos
907-
908-
909----
910-As an alternative, how about adding an entry on the bottom of the page for listing tags ?
911-Separate them from content, but add them as special links in properties / contents
912-Have an "+" icon that shows a popup of known tags
913-Might work nicer
914-
915--> Disadvantage is that the tags are no longer part of the text. E.g. several sections in the same page can have their own tags. If the page later gets split up, the tags go with the text, not stay with the original page.
916
917=== removed file 'HACKING/Ideas/Task_List.txt'
918--- HACKING/Ideas/Task_List.txt 2014-01-05 12:33:21 +0000
919+++ HACKING/Ideas/Task_List.txt 1970-01-01 00:00:00 +0000
920@@ -1,119 +0,0 @@
921-Content-Type: text/x-zim-wiki
922-Wiki-Format: zim 0.4
923-Creation-Date: Unknown
924-
925-====== Task List ======
926-
927-Start by renaming TODOList to "Task List"...
928-
929-----
930-Keep it simple, only 2 preference options:
931-[*] Include all checkboxes in the task list
932-[ ] Include lines by tag
933- Tags: [TODO, FIXME ]
934-
935-Do not take into account headers anymore
936-2 use cases:
937-1) task management - use checkboxes
938-2) writing a document - use tagged lines as placeholders
939-
940-request to do further filtering of headings / namespaces etc. only
941-after clear use case
942-
943-Index should have two signals
944-* initialize tables
945-* index page
946-
947-On initialize table add extra table for tasks
948-On index page scan for tasks and put them in the table
949-We trust the index to be up2date
950-
951-Changing preferences -> popup index needs to be rebuild do it now? yes/no
952-
953-Also TreeModel that maps that table - re-use code from PageIndex ??
954-
955------
956-**Possible improvement for the Task List plugin for GTD flow**
957-1. A tree view, showing hierarchy of checkbox lists and page
958-2. A list view only showing outer branches which represent actionable items
959-3. Distinguish a tree view showing dates for the next few days
960-4. Support items that are waiting - tag @waiting or special checkbox icon
961-5. Idem tasks not yet started (waiting for previous in the list to finish)
962-
963-------
964-
965-
966-How could zim and gtg integrate ?
967-
968-GTG:
969-1) Support hyperlinks to zim notes - easy
970-2) Use zim as a backend to store data - medium ?
971- -> Need to check how the task XML looks
972-3) Adopt zim PageView for full editing capabilities - lot of work
973- -> Probably too much work for features not really needed in gtg
974-
975-Zim:
976-1) Select text, have a action "create task", opens gtg with the text
977-inserted in a new task
978- -> depends on how easy it is to open GTG with this input
979-2) Hyperlink to GTG tasks
980- -> depends on commandline arguments for GTG
981-
982-Too merge the programs apply all of the above and run from a single
983-process, or at least connect both processes with the same daemon.
984-
985-For zim Task List plugin:
986-The "create task" item would be real nice, this allows selecting a
987-single line in a checkbox list and turn it into a "taks" sub page.
988-(Turn checkbox into other bullet, copy text, dates etc into subpage,
989-set type to "task")
990-
991-This allows turning an inbox of items into fully specified tasks and
992-allows adding longer descriptions and more line items for subtasks
993-etc.
994-
995-Selecting a list would create one task and move all other items to
996-line items in that task.
997-
998-Have a "New task" dialog popup
999--> Project: dropdown / namespace selector - should have a "new" item
1000--> Title: First line for selected text (max X words ?)
1001--> Text Area - full zim page view for editing
1002--> Calendar selectors for due date etc.
1003--> OK / CANCEL
1004-
1005-On ok a new page with this title is saved in a specific namespace
1006-Because page is type "task" it will have the calendar selectors
1007-embedded in the ui.
1008-
1009-Since Task List would give complete hierarchy of pages with line items
1010-and task items this integrates nicely.
1011-
1012--> Need tag plugin
1013-@tag syntax is good, add autolink for this syntax (hook from plugin)
1014-after all, tags are just links to special pages
1015-
1016-----
1017-
1018->From mailing list comment:
1019-
1020-Moreover, I have a GTD suggestion to Zim. Can it add a "Show
1021-actionable tasks only" check box in the TODO list window? A task is
1022-actionable if it don't have any sub-task , or all the subtasks are
1023-finished. It could be used to determine next actionable task easily.
1024-(p.s Subtask should inherit the priority from its parent) Any comment?
1025-
1026-
1027-----
1028-
1029-Actually current checkbox feature works quite well too
1030-Just need an extra bullet type for "waiting" and have the todolist exclude this but add in side pane
1031-
1032----
1033-
1034-In list view put column with basename page in front of summary - this provides context - tooltip should show whole item
1035-Limit the max length of an item, use ellipsis (make sure filter works on full content)
1036-
1037----
1038-
1039-Due dates relate to tickler calendar file - incorporate these in the calendar view / pageview for calendar pages
1040
1041=== removed file 'HACKING/Ideas/Templates.txt'
1042--- HACKING/Ideas/Templates.txt 2010-01-19 18:41:47 +0000
1043+++ HACKING/Ideas/Templates.txt 1970-01-01 00:00:00 +0000
1044@@ -1,14 +0,0 @@
1045-Content-Type: text/x-zim-wiki
1046-Wiki-Format: zim 0.4
1047-Creation-Date: Thu, 30 Jul 2009 21:37:50 +0200
1048-
1049-====== Templates ======
1050-
1051-Store page templates in notebook _templates/wiki - can always extend later for global templates etc.
1052-
1053-Add drop down to "New page" dialog
1054-
1055-Add menu item "tools -> set template" which is only active when the
1056-page is not saved yet
1057-
1058-Add menu item "tools -> edit templates" just open directory - worry about UI control later
1059
1060=== removed file 'HACKING/Ideas/Transclusion.txt'
1061--- HACKING/Ideas/Transclusion.txt 2010-02-11 21:46:23 +0000
1062+++ HACKING/Ideas/Transclusion.txt 1970-01-01 00:00:00 +0000
1063@@ -1,46 +0,0 @@
1064-Content-Type: text/x-zim-wiki
1065-Wiki-Format: zim 0.4
1066-Creation-Date: 2010-02-01T21:44:14.249245
1067-
1068-====== Transclusion ======
1069-Created Monday 01 February 2010
1070-
1071-**Problem**:
1072-How to incorporate non-wiki text in a wiki page
1073-
1074-**Examples**:
1075-Embedded code snippets, with syntax highlighting
1076-Embedded views for external files, e.g. code or logs or ..
1077-
1078-**Concepts**:
1079-Embedded text: source is stored within the wiki page itself
1080-Embedded view: source is stored externally - transclusion
1081-Images are an example of external files that are viewed in the page,
1082-but are not stored in the wiki page.
1083-For the equation editor we also want embedded latex, but still render as image
1084-
1085-**Details**:
1086-Some code for including arbitrary source types, ''{{{mimetype\n...\n}}}''
1087-Some code for including files ''{{file}}'' (like images, but for
1088-non-image mimetypes as well)
1089-Generic container for these parts in the parse tree - probably one
1090-container type for all mimetype, including images
1091-Pageview should support embedded widgets (not just pixbufs) (also wrap
1092-images in a widget ?)
1093-Any image file can be rendered by default as it is now
1094-Any text file can be rendered with a gtksourceview
1095-For included text add some methods to open the file with an external
1096-program (same as for images)
1097-Have a method to register mimetype handlers, e.g. for rendering equations
1098-Each object type also need some code for exporting, fallback to either
1099-text or image
1100-
1101-Bonus would be to detect changes in the source file and refresh the
1102-object in real time
1103-Could check on regular intervals, but maybe adds to much IO overhead
1104-
1105-**Other**
1106-Equation rendering handler would compute md5 of text and use that as
1107-basis for image name, put it in a cache directory
1108-Handler for equations should play nicely with latex export, but
1109-produce image for any other export type
1110
1111=== added file 'HACKING/LIMITATIONS.txt'
1112--- HACKING/LIMITATIONS.txt 1970-01-01 00:00:00 +0000
1113+++ HACKING/LIMITATIONS.txt 2014-08-19 17:10:33 +0000
1114@@ -0,0 +1,8 @@
1115+Content-Type: text/x-zim-wiki
1116+Wiki-Format: zim 0.4
1117+Creation-Date: 2014-07-31T21:09:29+02:00
1118+
1119+====== LIMITATIONS ======
1120+
1121+Main assumption about the whole file handling and page rendering is that files are small enough that we can load them into memory several times. This seems a valid assumption as notebooks are spread over many files. Having really huge files with contents is outside the scope of the design. If this is what you want to do, you probably need a more heavy duty text editor.
1122+
1123
1124=== removed file 'HACKING/Limitations.txt'
1125--- HACKING/Limitations.txt 2010-01-19 18:41:47 +0000
1126+++ HACKING/Limitations.txt 1970-01-01 00:00:00 +0000
1127@@ -1,7 +0,0 @@
1128-Content-Type: text/x-zim-wiki
1129-Wiki-Format: zim 0.4
1130-Creation-Date: Sat, 04 Apr 2009 10:58:28 +0200
1131-
1132-====== LIMITATIONS ======
1133-
1134-Main assumption about the whole file handling and page rendering is that files are small enough that we can load them into memory several times. This seems a valid assumption as notebooks are spread over many files. Having really huge files with contents is outside the scope of the design. If this is what you want to do, you probably need a more heavy duty text editor.
1135
1136=== removed file 'HACKING/Parse_Tree.txt'
1137--- HACKING/Parse_Tree.txt 2010-01-19 18:41:47 +0000
1138+++ HACKING/Parse_Tree.txt 1970-01-01 00:00:00 +0000
1139@@ -1,84 +0,0 @@
1140-Content-Type: text/x-zim-wiki
1141-Wiki-Format: zim 0.4
1142-Creation-Date: Tue, 19 May 2009 19:48:40 +0200
1143-
1144-====== Parse Tree ======
1145-
1146-FIXME - make rules more lax - p should be optional
1147-FIXME - add bullet and checkbox lists (+ numbered lists in proposals)
1148-
1149-This page documents the parse tree format as used in zim. This is the format all the formats should support and is also understood by the gui editor widget.
1150-
1151-The parse tree is a parse tree object that supports the xml.etree API. The root node tag always must be "**zim-tree**".
1152-
1153-Structure below consists of headings and paragraphs. Top level can not contain any text except for whitespace. Whitespace between headings and paragraphs is considered a hint but can be ignored when exporting.
1154-
1155-//Top level tags://
1156-**h** - heading
1157- attribute //level// 1 .. 6 gives nesting
1158- can only contain text
1159-**p** - paragraph
1160- can contain text, formats, links, images and lists
1161- attribute //indent// gives indent level
1162-**tt** - verbatim paragraph
1163- con only contain text, nothing else
1164- attribute //indent// gives indent level
1165-
1166-Basically all content should be wrapped in paragraph nodes.
1167-
1168-//Available formattings://
1169-**em** - emphasis, default rendered italic
1170-**strong** - default rendered in bold
1171-**mark** - default rendered as highlighted, alternatively underline
1172-**strike **- strike through
1173-**code** - verbatim text
1174-
1175- Formats can only contain text and links, with the exception of the **tt** format, which can only contains text.
1176-
1177-Links:
1178-**link**
1179- can contain: none
1180- text: any
1181- attrib:
1182- href: string, actual link
1183- _href-type: page | url | file | ... (if none, href not yet resolved)
1184- _href: string, resolved target, depends on type
1185- type: string, semantic type of the link
1186-
1187-//Images://
1188-**img**
1189- text: none
1190- attrib:
1191- src: string, link as it apeared in the source (optional)
1192- file: string, absolute file name
1193- width int
1194- height: int
1195- alt: string, name / short description, used e.g. for tooltip
1196- obj-type: string, reserved for objects that render as image in the gui
1197-
1198-Captions can be implied if the image is followed directly by a paragraph without whitespace in between (so the tail of the img element is empty). In this case the exporter can render this paragraph the caption of the image.
1199-
1200-===== Proposed extensions =====
1201-
1202-==== Horizontal lines ====
1203-Allow a <hr> element.
1204-
1205-==== Image links ====
1206-How to add linking capability to images ? Either add the link as a child node of the image or merge the attributes.
1207-
1208-Alternatively we could consider clickable images an object.
1209-
1210-==== Image captions ====
1211-For more elaborate documents you may want to have image captions. These should start with "Image XXX:" where the images are numbered automatically. In that case you also want references, links that link these images with their text automatically updated to the right number. (How to integrate this with latex export, which does this natively ?)
1212-
1213-One way to do this is to assume that any text directly below an image is the caption, if the image is on it's own line and there is no text above the image. In the tree this is a paragraph starting with an image and the tail of the image starting with "\n".
1214-
1215-==== Icons ====
1216-Special case of images, instead of a image file we give a stock image name. This can be used e.g. for smileys or tags. When images can be links as well an icon that is clickable can be nice for plugin development.
1217-
1218-==== Objects ====
1219-Generic tag "obj" which contains arbitrary content. In the interface there is a class for each object type which can render it. For serializing it could have special code for specific formats and a generic fallback that returns some parse tree consisting of basic types. For example an equation object would have a method to put a pixbuf in the gui + override the context menu. It also would have support for latex and wiki formats, (wiki parser needs to know about it though) and the fallback gives the image, which is used e.g. in html export.
1220-
1221-Base class for object extensions could fallback to parse tree with "image not found" icon.
1222-
1223-If we want captions for objects it should work same as for images. This is something we want for tables - although maybe it should be configurable whether the caption is above or below the object per type (below for images, above for tables). Also equations can have a captions etc.
1224
1225=== removed file 'HACKING/Signals.txt'
1226--- HACKING/Signals.txt 2012-07-18 20:54:43 +0000
1227+++ HACKING/Signals.txt 1970-01-01 00:00:00 +0000
1228@@ -1,17 +0,0 @@
1229-Content-Type: text/x-zim-wiki
1230-Wiki-Format: zim 0.4
1231-Creation-Date: 2012-07-14T11:54:59+02:00
1232-
1233-====== Signals ======
1234-
1235-Keep in mind that with each "connect" an object reference is created. The reference is kept by the object that is being connected to and is only broken when that object is being destroyed. So if you do
1236-
1237- ''object_A.connect('some-signal', object_B.some_method)''
1238-
1239-then object_B will not be destroyed as long as object_A is alive. On the other hand, if object_A is destroyed, object_B simply doesn't get any signals anymore.
1240-
1241-This seems not to be a problem when you e.g. connect a button signal within a dialog object. Reason is probably that the circular reference is broken when the dialog is destroyed.
1242-
1243-But it is a problem when e.g. a dialog connects to an external object, or a plugin connect to the main interface objects. In those cases signals need to be explicitly disconnected when you close the dialog or remove the plugin. See the ''ConnectorMixin'' class in ''zim.signals''.
1244-
1245-A special not about **connect_object**. In the Python API it looks like this method is only intended to swap the object argument when calling the callback, however in the C API it is mentioned that this method also results in an additional object reference and it has a bug in current versions with cleaning up those references. So it is better avoided.
1246
1247=== removed file 'HACKING/Start.txt'
1248--- HACKING/Start.txt 2012-07-18 20:54:43 +0000
1249+++ HACKING/Start.txt 1970-01-01 00:00:00 +0000
1250@@ -1,27 +0,0 @@
1251-Content-Type: text/x-zim-wiki
1252-Wiki-Format: zim 0.4
1253-Creation-Date: Unknown
1254-
1255-====== HACKING ======
1256-These notes contains general development documentation on zim.
1257-
1258-===== Contents =====
1259-* [[Development tools]]
1260-* [[Coding Style & Guidelines]]
1261- * [[Signals]]
1262-* [[Writing plugins]]
1263-* [[Test Suite]]
1264-
1265-* [[Compatibility]]
1266-* [[Config Files]]
1267-* [[Limitations]]
1268-* [[Parse Tree]]
1269-
1270-* [[Release checklist]]
1271-
1272-===== Other documentation =====
1273-Detailed API documentation is part of the source code in the form of python doc strings. To generate a full set of API docs install "[[http://epydoc.sourceforge.net/|epydoc]]" and run "''make epydoc''" in the zim source dir
1274-
1275-And of course any information about the usage of the application can be found in the user manual which can be found in [[~/code/pyzim-trunk/data/manual|../../data/manual]].
1276-
1277-To find information online check our website at http://zim-wiki.org
1278
1279=== removed file 'HACKING/Store_modules.txt'
1280--- HACKING/Store_modules.txt 2011-06-05 21:51:36 +0000
1281+++ HACKING/Store_modules.txt 1970-01-01 00:00:00 +0000
1282@@ -1,5 +0,0 @@
1283-
1284-TODO: more info about writing format modules
1285-
1286-Each module in zim.stores should contains exactly one subclass of StoreClass. This includes imports, so do not import other classes that derive from this base class directly into the module. When zim loads the store it finds the correct store class by looking at the base class of each class object.
1287-
1288
1289=== removed file 'HACKING/Tasks.txt'
1290--- HACKING/Tasks.txt 2010-03-23 21:33:04 +0000
1291+++ HACKING/Tasks.txt 1970-01-01 00:00:00 +0000
1292@@ -1,52 +0,0 @@
1293-Content-Type: text/x-zim-wiki
1294-Wiki-Format: zim 0.4
1295-Creation-Date: Sat, 04 Apr 2009 10:54:39 +0200
1296-
1297-====== Tasks ======
1298-
1299-**Also grep for TODO and FIXME tags in the code**
1300-
1301-== Packaging ==
1302-[ ] Build windows installer (with or without Python + Gtk included)
1303-[ ] Update manual (check sections that were copied from perl version and TODO tags)
1304-
1305-== Defects & Missing Features ==
1306-[ ] Pageview: Para indenting tags and bullet offset tags need to be split up
1307-[ ] WWW: check resolving of files and icons for web server
1308- * Add extra check that no files outside allowed directories are served
1309-[ ] Implement Fuse filesystem plugins for auto-mounting sshfs / encfs / ..
1310- * Preferences dialog should have list of mount points
1311-[ ] Make server gui also use the daemon
1312-
1313-== New Features ==
1314-[*] Add custom commands in tools menu Custom Commands
1315-[ ] Add search selection in export
1316-[ ] Pageview: menu controls to indent / unindent / move up move down a list item
1317- * Check emission of indent-changed signal to set state of buttons
1318- * Also think about keybindings for moving a line up / down or top / bottom
1319-[ ] Pageview: support numbered lists
1320-[ ] Moving page should try to merge if namespace already exists
1321- * Prompt + check recursively no children conflict before attempt
1322-[ ] Implement "Open with custom command" dialog
1323-[ ] Implement zip and tar.gz notebooks
1324-[ ] Implement gjots notebooks
1325-[ ] WWW: Implement a '--public' option + add checkbox in GUI to allow remote connections (default localhost only)
1326-[ ] Calendar needs to register additional template methods in order to refine it's template
1327-[ ] Templates: setting to determing use of <br> elements (default yes)
1328-[ ] Templates: need settings to enforce level of headings in output
1329- * see ParseTree.cleanup_headings() - needs test
1330-[ ] Templates: Need [% FOREACH section %] statement to export multiple zim pages to a single output
1331-[ ] Exporter: Need to support exporting multiple zim pages to a single output
1332-[ ] Templates: Need option to split body on H1 and HR
1333- * Needed for S5 output - multiple slides in one page
1334- * Also want to be able to control headings per slide
1335-[*] Templates: Add the option to include special pages into the template, e.g. index page
1336-[ ] Pageview: add support for arbitrary widgets, so we can have objects
1337- * Also wrap pixbufs into widgets so we can set tooltips etc.
1338-[ ] Add iconview with attachments at botton of pageview
1339- * Use an expander or similar to show / hide
1340-[ ] Archive function as an alternative to delete - put it in a special namespace + add timestamp to page name
1341-[ ] Allow multiple pageviews to be active, e.g. in separate windows, tabs
1342- * Share TextBuffers between pageviews - ref count on ui object owning the parse tree ?
1343- * add menus and toolbars to separate windows
1344- * remember state (and history ?) per window
1345
1346=== removed directory 'HACKING/Usage'
1347=== removed file 'HACKING/Usage/Document_Editing_and_Creative_Writing.txt'
1348--- HACKING/Usage/Document_Editing_and_Creative_Writing.txt 2012-12-11 18:43:27 +0000
1349+++ HACKING/Usage/Document_Editing_and_Creative_Writing.txt 1970-01-01 00:00:00 +0000
1350@@ -1,15 +0,0 @@
1351-Content-Type: text/x-zim-wiki
1352-Wiki-Format: zim 0.4
1353-Creation-Date: 2012-12-11T19:21:55+01:00
1354-
1355-====== Document Editing and Creative Writing ======
1356-Created Tuesday 11 December 2012
1357-
1358-Manual snippet (Usage)
1359-
1360- If the same notebook contains both notes on the document and the polished text it is useful to be able to only export the polished text without the notes. In order to this one can either separate notes and final text in different namespaces or one can create an "index" page listing links to all the pages that are part of the final document. That way one can make the selection in the export dialog to only export the relevant sections.
1361-
1362-
1363-MISSING: export many pages to single document -- needed for latex export document
1364-MISSING: selection in export dialog based on index page as describe above -- needed for latex export document
1365-MISSING: custom ordering in a namespace -- MUST have
1366
1367=== added file 'HACKING/Working_with_Bazaar.txt'
1368--- HACKING/Working_with_Bazaar.txt 1970-01-01 00:00:00 +0000
1369+++ HACKING/Working_with_Bazaar.txt 2014-08-19 17:10:33 +0000
1370@@ -0,0 +1,24 @@
1371+Content-Type: text/x-zim-wiki
1372+Wiki-Format: zim 0.4
1373+Creation-Date: 2014-07-31T21:05:49+02:00
1374+
1375+====== Working with Bazaar ======
1376+
1377+The zim code is kept under version control using the bazaar version control system. See the website for documentation on using this system: http://bazaar.canonical.com/en/
1378+
1379+The quick course, to get a copy of the zim code:
1380+
1381+ bzr branch lp:zim
1382+
1383+To check in some changes (don't forget to give some meaningful changelog)
1384+
1385+ bzr add
1386+ bzr commit
1387+
1388+
1389+To create a patch that can be mailed:
1390+
1391+ bzr send -o some-description.patch
1392+
1393+Alternatively you can publish your branch directly on the launchpad website and file a merge proposal.
1394+
1395
1396=== modified file 'HACKING/Writing_plugins.txt'
1397--- HACKING/Writing_plugins.txt 2011-08-06 17:18:57 +0000
1398+++ HACKING/Writing_plugins.txt 2014-08-19 17:10:33 +0000
1399@@ -7,43 +7,14 @@
1400
1401 **NOTE:** Under the GPL license used for distributing this program all plugins should also be licensed under the GPL. A closed source plugin extension is not allowed. A plugin is allowed to call any non-GPL program as long as the plugin itself is under GPL and the non-GPL program runs as a separate process with a clearly defined inter process communication interface.
1402
1403-===== Topics: =====
1404-
1405-==== What does a plugin look like ====
1406-A plugin is just a python module that is a sub-module of ''zim.plugins'' . It should contain exactly one class that is a subclass of the base class "''PluginClass''". When zim loads a plugin a instance of this sub-class is created and attached to the main application object. From there it can access most other parts of the object structure that drives the application.
1407-
1408-Since a plugin is just a sub-module of "''zim.plugins''" a plugin is installed by placing it in any folder in the python path that maps to the "''zim.plugins''" module. So for example you can put them directly in "''./zim/plugins''" in the source code. When installing a plugin locally you may want to put it in the "[[http://docs.python.org/whatsnew/2.6.html#pep-370-per-user-site-packages-directory|per user site packages directory]]". If (after restarting) zim detects the new plugin it will show up in the plugins tab of the preferences dialog.
1409-
1410-See the API documentation for "''zim.plugins''" for all the implementation details of the plugin class. Of course the quickest way to get started is to take an existing plugin that does something you want to do as well and copy it, then start modifying it to suit your needs.
1411-
1412-TODO: we may want a system in the future where plugins can be packaged as zip files and installed by dropping the zip file in a place like "''.local/share/zim/plugins''"
1413-
1414-==== Plugin Info ====
1415-Each plugin object should have an attribute "''plugin_info''" which is a dict with basic information about the plugin such as the name, the author and a short description. This info is displayed to the user in the preferences dialog.
1416-
1417-==== How to use preferences ====
1418-The "''preferences''" attribute of the plugin holds a dict with preferences that are global for all zim instances. Typically these are editable by the customer in the preferences dialog. However to make preferences show up in the user interface they should be defined in the "''plugin_preferences''" attribute, which has some basic info like the name, a description, a type and a default value for each preference.
1419-
1420-==== How to use uistate ====
1421-The "''uistate''" attribute of the plugin holds a dict which is intended to keep a persistent state for a specific notebook. You may use it for example to store that last folder the user opened so next time you can start at the same place - or you can put the window size of a dialog in it.
1422-
1423-==== How to add menu items ====
1424-The menu bar and the toolbar in zim are managed by a Gtk UIManager. This construct consists of two parts: 1) a piece of XML that defines what items should show up in which menu or at which place in the toolbar and 2) a definition of "actions" which gives the description, icon, keybinding, etc. for each menu item. Zim has a wrapper for this that hooks each "action" to a method of the plugin object with the same name.
1425-
1426-See the PrintToBrowser plugin for a very simple plugin that just defines one menu item. To get a view of the whole menu structure have a look at ''./data/menubar.xml''.
1427-
1428-==== How to call a plugin from the commandline ====
1429-For interprocess communication it is nice if a plugin can be called externally. This for example used by the Quicknote and TrayIcon plugins that are shipped with zim which both have a few commandline options (see the user manual).
1430-
1431-To make it possible to call a plugin from the commandline the plugin module should define a "''main()''" function. When zim is called with "''zim --plugin pluginname''" this main function is called.
1432-
1433-This ''main()'' method is called directly in the new process. If you want to connect to a running instance of zim the plugin should load the daemon interface itself. See the TrayIcon plugin for an example.
1434-
1435-==== How to package ====
1436+===== Writing a new plugin =====
1437+See the module documentation in ''zim.plugins'' for documentation of the plugin framework.
1438+
1439+===== How to package =====
1440 TODO: At this moment we have no standard way for packagin plugins
1441
1442-==== Manual page ====
1443+===== Manual page =====
1444 Each plugin should have it's own page in the user manual that explains what it does and how to use it. Please write it before proposing your plugin to be included in the main package.
1445
1446-==== Unit testing ====
1447+===== Unit testing =====
1448 As stated in the guidelines [[Test Suite|unittesting]] is good. Please try to write some test cases for your plugin before proposing it to be included in the main package. Other developers may not use your plugin, so if it breaks later on it may go undetected unless there is a test case for it.
1449
1450=== modified file 'HACKING/notebook.zim'
1451--- HACKING/notebook.zim 2010-01-19 18:41:47 +0000
1452+++ HACKING/notebook.zim 2014-08-19 17:10:33 +0000
1453@@ -1,9 +1,13 @@
1454 [Notebook]
1455-autosave_version=0
1456+version=0.4
1457+name=HACKING
1458+interwiki=
1459+home=HACKING
1460+icon=
1461 document_root=
1462-home=:Start
1463-icon=
1464-name=HACKING
1465-slow_fs=False
1466-version=0.4
1467+shared=False
1468+endofline=unix
1469+disable_trash=False
1470+profile=
1471+
1472
1473
1474=== modified file 'data/manual/FAQ.txt'
1475--- data/manual/FAQ.txt 2013-04-13 11:09:42 +0000
1476+++ data/manual/FAQ.txt 2014-08-19 17:10:33 +0000
1477@@ -64,5 +64,5 @@
1478 Zim is written as a "single user" program, so it is not intended for multiple people using the same notebook. However it can be used with version control like Bazaar, Git or Mercurial. This way multiple users can work on the same notebook and merge their changes. See the [[Plugins:Version Control|Version Control plugin]].
1479
1480 ===== I have a useful trick or tip. How can I share it with other users? =====
1481-You can have a look at the [[http://www.zim-wiki.org/wiki/doku.php|zim documentation wiki]]. It has a section dedicated to [[http://www.zim-wiki.org/wiki/doku.php?id=tips_and_tricks|tricks and tips]]. Or write a mail to the mailing list, see the [[https://launchpad.net/~zim-wiki|team page]] on launchpad
1482+You can have a look at the [[http://www.zim-wiki.org/wiki/|zim documentation wiki]]. It has a section dedicated to tricks and tips. Or write a mail to the mailing list, see the [[https://launchpad.net/~zim-wiki|team page]] on launchpad
1483
1484
1485=== modified file 'data/manual/Help/Commandline_Options.txt'
1486--- data/manual/Help/Commandline_Options.txt 2013-04-14 10:40:44 +0000
1487+++ data/manual/Help/Commandline_Options.txt 2014-08-19 17:10:33 +0000
1488@@ -31,7 +31,7 @@
1489
1490 GUI Options:
1491 --list show the list with notebooks instead of
1492- opening the default notebook
1493+ opening the default notebook
1494 --geometry window size and position as WxH+X+Y
1495 --fullscreen start in fullscreen mode
1496 --standalone start a single instance, no background process
1497@@ -42,14 +42,14 @@
1498 --gui run the gui wrapper for the server
1499
1500 Export Options:
1501- --format format to use (defaults to 'html')
1502- --template name of the template to use
1503- -o, --output output directory
1504- --root-url url to use for the document root
1505- --index-page index page name
1506-
1507- You can use the export option to print a single page to stdout.
1508- When exporting a whole notebook you need to provide a directory.
1509+ -o, --output output directory (mandatory option)
1510+ --format format to use (defaults to 'html')
1511+ --template name of the template to use
1512+ --root-url url to use for the document root
1513+ --index-page index page name
1514+ -r, --recursive when exporting a page, also export sub-pages
1515+ -s, --singlefile export all pages to a single output file
1516+ -O, --overwrite force overwriting existing file(s)
1517
1518 Search Options:
1519 None
1520
1521=== modified file 'data/manual/Help/Config_Files.txt'
1522--- data/manual/Help/Config_Files.txt 2014-02-09 12:22:50 +0000
1523+++ data/manual/Help/Config_Files.txt 2014-08-19 17:10:33 +0000
1524@@ -55,6 +55,8 @@
1525
1526 Note that if you want interwiki page to include url syntax like a "#" to link to anchors in the target page, you should write your URLs with "''{NAME}''".
1527
1528+As a special case you can add other zim notebooks in the url list using "''zim+file://''" as URL scheme. These URLs will trigger zim to open the notebook directly.
1529+
1530 ==== Date format list ====
1531 The file ''XDG_DATA/zim/dates.list'' gives a list of strftime formats, one on each line, to be used to populate the "Insert Date and Time" dialog. The first instance of this file that is found in the XDG_DATA path is used.
1532
1533
1534=== modified file 'data/manual/Help/Custom_Tools.txt'
1535--- data/manual/Help/Custom_Tools.txt 2012-07-18 22:01:21 +0000
1536+++ data/manual/Help/Custom_Tools.txt 2014-08-19 17:10:33 +0000
1537@@ -3,12 +3,12 @@
1538
1539 ====== Custom Tools ======
1540
1541-Zim allows you to define custom tools to extend the basic functionality with simple functions that can be handled by an external program or script. The custom tool manager allows you to quickly extend functionality in zim by shell scripts or other external programs. It is also useful to prototype plugin functionality without having to learn the full API for plugins.
1542+Zim allows you to define custom tools to extend the basic functionality with simple functions that can be handled by an external program or script. The custom tool manager allows you to quickly extend functionality in zim by shell scripts or other external programs. It is also useful to prototype plugin functionality without having to learn the full API for plugins.
1543
1544 ===== Add a custom tool =====
1545 To add a custom tool, open up the custom tool manager dialog using the menu item //Tools// -> //Custom Tools//. Now press the "Add" icon button and zim will prompt for the properties of a new tool.
1546
1547-The **Name** is used in the tools menu for this tool and is mandatory.
1548+The **Name** is used in the tools menu for this tool and is mandatory.
1549
1550 The **Description** should be a slightly more verbose description of this tool. It is used for example as the tooltip for toolbar items.
1551
1552@@ -21,13 +21,15 @@
1553 **%s** for the real page source file (if any)
1554 **%n** for the notebook location (file or folder)
1555 **%D** for the document root (if any)
1556-**%t** for the selected text or word under cursor
1557+**%t** for the selected text or word under cursor
1558
1559 The optional **Icon** is used for toolbar items and for menu items depending on your Gtk appearance settings.
1560
1561 Then there is the checkbox **Command does not modify data**. Enable this option if your tool only read data but does not modify it. In that case zim will execute the application in the background and keep running. Otherwise, if your tool does modify data, zim has to wait for it to finish and reload the current page.
1562
1563-The chcekbox **Show in the toolbar** determines if this tool will be added to the toolbar or not.
1564+If the option **Output should replace current selection** is enabled (and the above checkbox is not enabled) the output of the command is used to replace the current selection in the editor window. This can be used for custom commands that modify a single word or sentence.
1565+
1566+The option **Show in the toolbar** determines if this tool will be added to the toolbar or not.
1567
1568 Tools are always visible in the "Tools" menu in the main window. Depending on the parameters they take, they may also be shown in the context menus for pages or for selected text in the editor.
1569
1570
1571=== modified file 'data/manual/Help/Export.txt'
1572--- data/manual/Help/Export.txt 2012-12-11 18:43:27 +0000
1573+++ data/manual/Help/Export.txt 2014-08-19 17:10:33 +0000
1574@@ -14,6 +14,8 @@
1575
1576 The option **Single page** allows to select you a single page to export.
1577
1578+When the **Include subpages** option is selected all pages below the selected page will be exported as well recursively.
1579+
1580 ==== Step 2: Select the export format ====
1581 The **Format** allows the choice of the output format.
1582
1583@@ -22,6 +24,8 @@
1584 If your notebook has a Document Root (see [[Properties]]) you can select what to do with links to files under that document root. Either **Link files under document root with full file path**, which means files will be linked by their absolute file path, or **Map document root to URL**, which will result in links with the given URL as prefix. This can be useful when you [[Usage:Publishing|publish]] pages as part of a larger website.
1585
1586 ==== Step 3: Select the output file or folder ====
1587+Depending on the choice of pages to export and the format to export you can get to choice to either **Export each page to a separate file** or to **Export all pages to a single file**. Exporting each page to a separate file typically results in a folder with multiple files, one for each page that is exported, very similar to the zim notebook itself. Exporting to a single file creates a different view where all pages are combined in a single output template.
1588+
1589 Here you can select the **output folder** (if you are exporting multiple pages) or the **output file** (if you export a single page).
1590
1591 If you specify an **Index page** a page will be generated that contains a list with links to all pages that were exported. This can e.g. be used as a site map.
1592
1593=== modified file 'data/manual/Help/Links.txt'
1594--- data/manual/Help/Links.txt 2013-04-13 10:56:39 +0000
1595+++ data/manual/Help/Links.txt 2014-08-19 17:10:33 +0000
1596@@ -42,7 +42,7 @@
1597 ===== Interwiki =====
1598 There is a list of pre-defined urls in "''share/zim/urls.list''" which lists most commonly used online wikis. These urls can be refered to by a keyword so you don't have to type the full url every time; also you can update all links to a certain wiki by changing the url in the file. Have a look at the list to get an idea of how to use this.
1599
1600-This link for example goes to wikipedia.org: [[wp?wiki]]
1601+This link for example goes to wikipedia.org and opens the page "wiki": [[wp?wiki]]
1602
1603 To add your own urls use "''~/.local/share/zim/urls.list''". All types of urls which are supported by zim can be added. Consider adding "''file://''" urls for directories you refer often from zim. See [[Config Files]] for more details.
1604
1605
1606=== modified file 'data/manual/Help/Templates.txt'
1607--- data/manual/Help/Templates.txt 2013-04-13 10:56:39 +0000
1608+++ data/manual/Help/Templates.txt 2014-08-19 17:10:33 +0000
1609@@ -37,111 +37,267 @@
1610
1611 Templates are located in ''/usr/share/zim/templates/'' and ''~/.local/share/zim/templates'' by default. You can add templates you use more often there. To modify a template copy it to the ''~/.local/...'' directory and edit it.
1612
1613-Template syntax:
1614-
1615-'''
1616-[% var %] # interpolates a variable
1617-[%- var %] # + strip line break before the expression
1618-[% var -%] # + strip line break after the expression
1619-[%- var -%] # + strip line breaks on both sides
1620-
1621+==== Template syntax ====
1622+
1623+The template is a combination of the normal document output that you want to produce (e.g. HTML or Latex) combined with template instructions. The normal text is passed on to the output unmodified, while the instructions get replaced by the output of the template processing.
1624+
1625+Template instructions can have to forms:
1626+
1627+'''
1628+[% some expression %]
1629+'''
1630+
1631+''and''
1632+
1633+''<!--[% some expression %]-->''
1634+
1635+The first is typically used but the second form can look better in HTML or XML templates as it is parsed as an XML comment by other tools.
1636+
1637+=== Expressions ===
1638+
1639+Expressions used in template instructions can be:
1640+
1641+'''
1642+True, False, None # literal true, false and none
1643+"string", 'string', 5, 5.0 # text or number
1644+[.., .., ..] # list
1645+variable # variable name
1646+variable.name, # variable name with named attributes
1647+mylist.0 # variable containing a list with index
1648+function(.., ..) # function call
1649+'''
1650+
1651+
1652+The following operators can be used:
1653+'''
1654+
1655+and
1656+or
1657+not
1658+== # Equals
1659+!= # Not equals
1660+> # Greater than
1661+>= # Greater or equal than
1662+< # Less than
1663+<= # Less or equal than
1664+'''
1665+
1666+
1667+Valid variable names can contain letters (a - z) and numbers (0 - 9) and underscores ( _ ). The first character of the variable name must be a letter.
1668+
1669+=== Instructions ===
1670+
1671+An instruction can be one of the following:
1672+
1673+'''
1674+GET
1675+SET
1676+IF expr EL(S)IF expr .. ELSE .. END
1677+FOR var IN expr .. END
1678+FOREACH var = expr ... END
1679+BLOCK name .. END
1680+INCLUDE name or expr -- block or file
1681+'''
1682+
1683+
1684+The instruction is always in upper case letters. The expressions within can be either upper or lower case names.
1685+
1686+== Get & Set ==
1687+
1688+'''
1689+[% expr %] # implicit GET
1690+
1691+[% GET expr %]
1692+'''
1693+
1694+With GET you evaluate an expression and the result is put into the output. Typically the expression will just be a variable name.
1695+
1696+'''
1697+[% SET var = expr %]
1698+'''
1699+
1700+With SET you assign a variable. It does not output in the result and the instruction will be removed.
1701+
1702+== Conditionals ==
1703+
1704+'''
1705 [% IF expr %] ... [% END %] # conditionals
1706+'''
1707
1708+'''
1709 [% IF expr %]
1710- ...
1711+...
1712 [% ELSIF expr %]
1713- ...
1714+...
1715 [% ELSE %]
1716- ...
1717-[% END %]
1718-
1719-[% FOREACH name = var ] # loop
1720-... [% name %] ...
1721-[% END %]
1722-
1723-[% strftime("%c") %] # current time stamp
1724-[% strftime("%c", var) %] # date from variable
1725-
1726-
1727-[% options.option_name = value %] # set the template option option_name to value
1728-
1729-[% page.properties["Creation-Date"] %] # access dict
1730-'''
1731-
1732-
1733-For the ''IF'' and ''ELIF'' statements the expression can be either just a variable, in which case it is evaluated boolean, or an equality expression with "==". For example "''[% IF page.name == 'foo' %]''" is understood. More complex expressions are not supported.
1734-
1735-Available variables:
1736-
1737-'''
1738-zim.version # version of zim
1739-notebook.name # name of the notebook
1740-notebook.interwiki # interwiki key of the notebook if any
1741-
1742-page.name # complete page name
1743-page.namespace # namespace
1744-page.basename # last part of the page name
1745-page.properties # dict with page properties
1746-page.title # first heading in the page or the basename
1747-page.heading # first heading in the page
1748-page.body # content of the page without the leading heading
1749-page.content # content of the page including the leading heading
1750-page.has_links # True if the page has links to other pages
1751-page.links # list of page objects for pages linked in this page
1752-page.has_backlinks # True if the page is linked by other pages
1753-page.backlinks # list of page objects for pages linking to this page
1754-page.has_attachments # True if the page has attachments
1755-page.attachments # list of file objects for attachments
1756-
1757-# These special pages have the same properties as the 'page' object
1758-pages.index # the index page generated when exporting
1759-pages.home # the home page
1760-pages.next # the next page in the index (if any)
1761-pages.previous # the previous page in the index (if any)
1762-
1763-options # dict where format specific options can be set
1764-'''
1765-
1766-
1767-File objects returned by page.attachments have the following attributes:
1768-
1769-'''
1770-file.path # (relative) path as a string
1771-file.basename # file basename
1772-file.mtime # file mtime, to be used with strftime()
1773-file.size # file size as human readable string
1774-'''
1775-
1776-
1777-Functions available:
1778-
1779- ''url(link)''
1780- Turns a zim link or file object into an URL
1781-
1782- ''resource(filename)''
1783- Returns an URL for a template resource (see below)
1784+...
1785+[% END %]
1786+'''
1787+
1788+In any of these constructs the expression is evaluated and depending on the result one of the blocks is added to the output. Used to include optional output in the template
1789+
1790+== Loops ==
1791+
1792+'''
1793+[% FOR var IN expr %] # loop
1794+...
1795+[% END %]
1796+'''
1797+
1798+'''
1799+[% FOREACH var = expr ]
1800+...
1801+[% END %]
1802+'''
1803+
1804+In these instructions the result of an expression — typically an input variable — is iterated and for each iteration the block is evaluated.
1805+
1806+Within the loop the variable "''var''" is available and set to result of the iteration.
1807+
1808+In addition there is a special variable "''loop''" that can be used to access the loop state. It is defined with following attributes:
1809+
1810+'''
1811+loop.first True / False
1812+loop.last True / False
1813+loop.parity "even" or "odd"
1814+loop.even True / False
1815+loop.odd True / False
1816+loop.size n
1817+loop.max n-1
1818+loop.index 0 .. n-1
1819+loop.count 1 .. n
1820+loop.outer outer "loop" or None
1821+loop.prev previous item or None
1822+loop.next next item or None
1823+'''
1824+
1825+
1826+== Blocks ==
1827+
1828+'''
1829+[% BLOCK name %]
1830+...
1831+[% END %]
1832+
1833+[% INCLUDE name %]
1834+'''
1835+
1836+
1837+A BLOCK can be defined anywhere in the template and then included in other places using the INCLUDE statement. This is useful e.g. for template parts that repeat multiple times in the document. Note that a BLOCK is always defined in the top level scope, so you can not put them e.g. in an IF clause to define alternative versions. A BLOCK may be defined after the location where they are used.
1838+
1839+=== Objects and functions ===
1840+
1841+Most variables behave partially as objects, which means that it is possible to call object methods on them. The following standard (python) object methods are commonly supported:
1842+
1843+Strings: '''capitalize''', '''center''', '''count''', '''endswith''', '''expandtabs''', '''ljust''', '''lower''', '''lstrip''', '''replace''', '''rjust''', '''rsplit''', '''rstrip''', '''split''', '''splitlines''', '''startswith''', '''title''', '''upper'''
1844+
1845+Lists: '''get''', '''len''', '''reversed''', '''sorted'''
1846+
1847+Dictionary: '''get''', '''keys''', '''values''', '''items''', '''len''', '''reversed''', '''sorted'''
1848+
1849+The following standard (python) functions are supported: ''len(list)'', ''sorted(list)'', ''reversed(list)'', ''range(start, end)''.
1850+
1851+
1852+Other functions defined for all templates:
1853+
1854+ ''html_encode(text)''
1855+ Format text in HTML encoding
1856+
1857+ ''url_encode(text)''
1858+ Format text in URL encoding
1859
1860 ''strftime(template, date)''
1861 Format a date, see standard library for codes
1862
1863- strfcal(template, date)
1864+ ''strfcal(template, date)''
1865 Format a week number, accepts:
1866
1867 %w for day of week according to locale
1868 %W for weeknumber according to locale
1869 %Y for the year to which the week belongs
1870
1871- pageindex''(namespace, collapse, ignore_empty)''
1872- Creates a page index of a given namespace or the whole notebook
1873+
1874+
1875+==== Variables available for new pages ====
1876+
1877+Only a single variable is defined for new pages:
1878+
1879+'''
1880+page
1881+ .name
1882+ .namespace
1883+ .basename
1884+'''
1885+
1886+See also the [[Plugins:Journal|Journal Plugin]] for some properties available for journal pages
1887+
1888+
1889+==== Variables and functions available for Export templates ====
1890+
1891+When using templates when [[Export|exporting]] the following set of variables and functions is available in the export template:
1892+
1893+'''
1894+generator
1895+ .name Zim version number "Zim x.xx"
1896+ .user The current user name
1897+
1898+title Page title
1899+
1900+navigation links to other export pages (if not included in the same output file)
1901+ .home
1902+ .up
1903+ .prev
1904+ .next
1905+
1906+links Sorted dictionary with links to other export pages (index, plugins, etc.)
1907+
1908+ link
1909+ .name
1910+ .basename
1911+
1912+pages Iterator over all content to be exported (special + content)
1913+ .special Sorted dictionary with special pages to be included (index, plugins, etc.)
1914+ .content Iterator with pages being exported to this file (1 or more)
1915+
1916+ page
1917+ .title
1918+ .name
1919+ .namespace
1920+ .basename
1921+ .heading First heading of the page
1922+ .body Full body minus first heading
1923+ .content Heading + body
1924+ .properties
1925+ .links
1926+ .backlinks
1927+ .attachments
1928+
1929+ file
1930+ .basename
1931+ .mtime
1932+ .size
1933+
1934+options Dictionary with template options that can be set to control the output format
1935+'''
1936+
1937+Functions:
1938+
1939+ ''uri(link|file)''
1940+ Turns a zim link or file object into an URL
1941+
1942+ ''anchor(page)''
1943+ Turns an output page into an achor that can be linked
1944+
1945+ ''resource(filename)''
1946+ Returns an URL for a template resource (see below)
1947+
1948+ ''index(namespace, collapse, ignore_empty)''
1949+ Creates a page index of a given namespace or the set of pages being exported.
1950
1951 ''namespace'': the starting root - defaults to the top level (":")
1952- ''collapse:'' if ''TRUE'' only branches related to the current page are visible//, // if ''FALSE'' all branches are visible - defaults to TRUE
1953- ''ignore_empty:'' if ''TRUE'' empty pages are ignored — defaults to TRUE.
1954-
1955- //Example//: ''menu(page.namespace, TRUE, FALSE)''
1956-
1957-
1958-See also the [[Plugins:Journal|Journal Plugin]] for some properties available for journal pages
1959+ ''collapse'': if ''TRUE'' only branches related to the current page are visible, if ''FALSE'' all branches are visible - defaults to ''TRUE''
1960+ ''ignore_empty'': if ''TRUE'' empty pages are ignored — defaults to ''TRUE''.
1961+
1962
1963 ==== Template Resources ====
1964 To add additional files to the template, create a folder of the same name as the template. Any files in the folder (like style sheets, images, javascript files, etc.) will be copied along when this template is used to export data in zim. There is a function "''resource(filename)''" to refer to these files in the template.
1965
1966=== modified file 'data/manual/Plugins.txt'
1967--- data/manual/Plugins.txt 2014-01-05 12:04:25 +0000
1968+++ data/manual/Plugins.txt 2014-08-19 17:10:33 +0000
1969@@ -21,6 +21,7 @@
1970 * [[+Link Map|Link Map]]
1971 * [[+Line Sorter|Line Sorter]]
1972 * [[+Log events with Zeitgeist|Log events with Zeitgeist]]
1973+* [[+Mendeley Citations|Mendeley Citations]]
1974 * [[+Print to Browser|Print to Browser]]
1975 * [[+Quick Note|Quick Note]]
1976 * [[+Score Editor|Score Editor]]
1977@@ -45,5 +46,5 @@
1978 See http://www.python.org/dev/peps/pep-0370/ for details.
1979
1980 ===== Writing Plugins =====
1981-If you are looking for information on **writing plugins** please download the source package and have a look at the notes included in the "HACKING" folder.
1982+If you are looking for information on **writing plugins** please download the source package and have a look at the module documentation of the "''zim.plugins''" python module.
1983
1984
1985=== modified file 'data/manual/Plugins/Ditaa_Editor.txt'
1986--- data/manual/Plugins/Ditaa_Editor.txt 2012-03-18 11:40:18 +0000
1987+++ data/manual/Plugins/Ditaa_Editor.txt 2014-08-19 17:10:33 +0000
1988@@ -14,15 +14,15 @@
1989 |Document| |!magic!| | |
1990 | {d}| | | | |
1991 +---+----+ +-------+ +-------+
1992- : ^
1993- | Lots of work |
1994- +-------------------------+
1995+ : ^
1996+ | Lots of work |
1997+ +-------------------------+
1998 '''
1999
2000
2001 After conversion using ditaa, the above file becomes:
2002 {{./ditaa.png?type=ditaa}}
2003-ditaa interprets ascci art as a series of open and closed shapes, but it also uses special markup syntax to increase the possibilities of shapes and symbols that can be rendered.
2004+ditaa interprets ascii art as a series of open and closed shapes, but it also uses special markup syntax to increase the possibilities of shapes and symbols that can be rendered.
2005 ditaa is open source and free software (free as in free speech), since it is released under the GPL license.
2006
2007 See http://ditaa.sourceforge.net/ for more information about ditaa
2008
2009=== added file 'data/manual/Plugins/Mendeley_Citations.txt'
2010--- data/manual/Plugins/Mendeley_Citations.txt 1970-01-01 00:00:00 +0000
2011+++ data/manual/Plugins/Mendeley_Citations.txt 2014-08-19 17:10:33 +0000
2012@@ -0,0 +1,17 @@
2013+Content-Type: text/x-zim-wiki
2014+Wiki-Format: zim 0.4
2015+Creation-Date: 2014-01-19T14:38:51-05:00
2016+
2017+====== Mendeley Citations ======
2018+Uses the Mendeley Desktop API to insert citations as links that open in either the user's Mendeley library or the http://dx.doi.org DOI redirection service. The plugin does not currently keep track of the citations within a notebook and hence is not able to render a bibliography or keep track of numerical citation indices at this time.
2019+
2020+**Dependencies: **This plugin has no additional dependencies.
2021+
2022+===== Options =====
2023+The following options may be specified for the plugin:
2024+
2025+**Citation Style **is the style in which the citations are rendered. Any style from http://www.zotero.org/styles/ may be used, with the style specified as the suffix of the available URLs, e.g. //apa// for http://zotero.org/styles/apa or //springer-physics-author-date// for http://www.zotero.org/styles/springer-physics-author-date.
2026+
2027+**Links point to **determines whether the inserted links point to the user's personal Mendeley Library or the DOI Reference service at http://dx.doi.org. The links to a personal library are not portable, but are based on a paper's UUID and are gauranteed to exist. DOI information may not be available for a paper, in which case the insertion of citations will fail.
2028+
2029+Numerical citation styles are, at present, not rendered properly for reasons outlined above.
2030
2031=== modified file 'data/templates/html/Default.html'
2032--- data/templates/html/Default.html 2013-01-03 06:59:21 +0000
2033+++ data/templates/html/Default.html 2014-08-19 17:10:33 +0000
2034@@ -1,99 +1,98 @@
2035 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2036 <html>
2037- <head>
2038- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
2039- <title>[% title %]</title>
2040- <meta name='Generator' content='Zim [% zim.version %]'>
2041- <style type='text/css'>
2042- a { text-decoration: none }
2043- a:hover { text-decoration: underline }
2044- a:active { text-decoration: underline }
2045- strike { color: grey }
2046- u { text-decoration: none;
2047- background-color: yellow }
2048- tt { color: #2e3436; }
2049- pre { color: #2e3436;
2050- margin-left: 20px }
2051- h1 { text-decoration: underline;
2052- color: #4e9a06 }
2053- h2 { color: #4e9a06 }
2054- h3 { color: #4e9a06 }
2055- h4 { color: #4e9a06 }
2056- h5 { color: #4e9a06 }
2057- span.insen { color: grey }
2058- div.zim-object {
2059- border-style:solid;
2060- border-width:1px;
2061- }
2062- </style>
2063- </head>
2064- <body>
2065+<head>
2066+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
2067+ <title>[% title %]</title>
2068+ <meta name='Generator' content='[% generator.name %]'>
2069+ <style type='text/css'>
2070+ a { text-decoration: none }
2071+ a:hover { text-decoration: underline }
2072+ a:active { text-decoration: underline }
2073+ strike { color: grey }
2074+ u { text-decoration: none;
2075+ background-color: yellow }
2076+ tt { color: #2e3436; }
2077+ pre { color: #2e3436;
2078+ margin-left: 20px }
2079+ h1 { text-decoration: underline;
2080+ color: #4e9a06 }
2081+ h2 { color: #4e9a06 }
2082+ h3 { color: #4e9a06 }
2083+ h4 { color: #4e9a06 }
2084+ h5 { color: #4e9a06 }
2085+ span.insen { color: grey }
2086+ div.zim-object {
2087+ border-style:solid;
2088+ border-width:1px;
2089+ }
2090+ </style>
2091+</head>
2092+<body>
2093
2094 <!-- Header -->
2095-
2096-[% IF pages.previous -%]
2097- [ <a href='[% url(pages.previous) %]'>Prev</a> ]
2098-[%- ELSE -%]
2099+<div class='header'>
2100+[% IF navigation.prev %]
2101+ [ <a href='[% uri(navigation.prev) %]'>Prev</a> ]
2102+[% ELSE %]
2103 [ <span class='insen'>Prev</span> ]
2104-[%- END %]
2105+[% END %]
2106
2107-[% IF page.properties.type == 'namespace-index' -%]
2108+[% IF links.get("index") %]
2109+ [ <a href='[% uri(links.get("index")) %]'>Index</a> ]
2110+[% ELSE %]
2111 [ <span class='insen'>Index</span> ]
2112-[%- ELSE -%]
2113- [% IF pages.index -%]
2114- [ <a href='[% url(pages.index) %]'>Index</a> ]
2115- [%- ELSE -%]
2116- [ <a href='/'>Index</a> ]
2117- [%- END %]
2118-[%- END %]
2119+[% END %]
2120
2121-[% IF pages.next -%]
2122- [ <a href='[% url(pages.next) %]'>Next</a> ]
2123-[%- ELSE -%]
2124+[% IF navigation.next %]
2125+ [ <a href='[% uri(navigation.next) %]'>Next</a> ]
2126+[% ELSE %]
2127 [ <span class='insen'>Next</span> ]
2128-[%- END %]
2129-
2130-<!-- End Header -->
2131+[% END %]
2132+</div>
2133
2134 <hr />
2135
2136 <!-- Wiki content -->
2137
2138-[% IF page.properties.type == 'namespace-index' -%]
2139-<h1>Document Index</h1>
2140-[%- ELSE -%]
2141-<h1>[% page.heading %]</h1>
2142-[%- END %]
2143-
2144-[% page.body %]
2145-
2146-<!-- End wiki content -->
2147-
2148-<hr />
2149-
2150-<!-- Attachments and Backlinks -->
2151-
2152-[% IF page.has_backlinks -%]
2153- <b>Backlinks:</b>
2154- [%- FOREACH link = page.backlinks -%]
2155- <a href='[% url(link) %]'>[% link.name %]</a>
2156- [%- END -%]
2157-[%- ELSE -%]
2158- <i>No backlinks to this page.</i>
2159-[%- END %]
2160-<br><br>
2161-
2162-[% IF page.has_attachments -%]
2163- <b>Attachments:</b>
2164- <table>
2165- [%- FOREACH file = page.attachments -%]
2166- <tr><td><a href='[% url(file) %]'>[% file.basename %]</a></td><td>&nbsp;</td><td>[% file.size %]</td></tr>
2167- [%- END -%]
2168- </table>
2169-[%- END %]
2170-
2171-<!-- End Attachments and Backlinks -->
2172-
2173- </body>
2174-
2175+<div class='pages'>
2176+<!--[% FOR page IN pages %]-->
2177+ <div class='heading'>
2178+ <h1>[% page.heading %] <a name='[% anchor(page) %]'></a></h1>
2179+ </div>
2180+
2181+ <div class='content'>
2182+ [% page.body %]
2183+ </div>
2184+
2185+ <br />
2186+
2187+ <div class='page-footer'>
2188+ <!--[% FOR link IN page.backlinks %]-->
2189+ [% IF loop.first %]<b>Backlinks:</b>[% END %]
2190+
2191+ <a href='[% uri(link) %]'>[% link.name %]</a>
2192+
2193+ [% IF loop.last %]<br /><br />[% END %]
2194+ <!--[% END %]-->
2195+
2196+ <!--[% FOR file IN page.attachments %]-->
2197+ [% IF loop.first %]
2198+ <b>Attachments:</b>
2199+ <table>
2200+ [% END %]
2201+
2202+ <tr><td><a href='[% uri(file) %]'>[% file.basename %]</a></td><td>&nbsp;</td><td>[% file.size %]</td></tr>
2203+
2204+ [% IF loop.last %]
2205+ </table>
2206+ [% END %]
2207+ <!--[% END %]-->
2208+ </div>
2209+
2210+ [% IF not loop.last %]<hr />[% END %]
2211+
2212+<!--[% END %]-->
2213+</div>
2214+
2215+</body>
2216 </html>
2217
2218=== modified file 'data/templates/html/Default_with_index.html'
2219--- data/templates/html/Default_with_index.html 2014-01-05 10:41:13 +0000
2220+++ data/templates/html/Default_with_index.html 2014-08-19 17:10:33 +0000
2221@@ -1,85 +1,110 @@
2222 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2223 <html>
2224- <head>
2225- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
2226- <title>[% page.title %]</title>
2227- <meta name='Generator' content='Zim [% zim.version %]'>
2228- <style type='text/css'>
2229- a { text-decoration: none }
2230- a:hover { text-decoration: underline }
2231- a:active { text-decoration: underline }
2232- strike { color: grey }
2233- u { text-decoration: none;
2234- background-color: yellow }
2235- tt { color: #2e3436; }
2236- pre { color: #2e3436;
2237- margin-left: 20px }
2238- h1 { text-align: center;
2239- color: #4e9a06 }
2240- h2 { color: #4e9a06 }
2241- h3 { color: #4e9a06 }
2242- h4 { color: #4e9a06 }
2243- h5 { color: #4e9a06 }
2244- div.zim-object {
2245- border-style:solid;
2246- border-width:1px;
2247- }
2248- span.insen { color: grey }
2249- .page { max-width: 1000px;}
2250- .menu{
2251- float:left; width: 300px;
2252- }
2253-
2254- .content { padding-left: 320px;}
2255- .notebook{font-variant: small-caps;
2256- color:#4e9a06;
2257- padding: 0px 20px;}
2258- hr{clear:both;}
2259- </style>
2260-
2261- </head>
2262- <body>
2263- <div class="page">
2264- <div class="heading">
2265- [% IF page.properties.type == 'namespace-index' -%]
2266- <h1>Document Index</h1>
2267- [%- ELSE -%]
2268- <h1>[% page.heading %]</h1>
2269- [%- END %]
2270- </div>
2271- <hr>
2272- <div class="menu">
2273- [% pageindex() %]
2274- </div>
2275- <div class="content">
2276- <!-- Wiki content -->
2277- [% page.body %]
2278-
2279- <!-- End wiki content -->
2280- </div>
2281- <hr>
2282- <!-- Backlinks -->
2283- <div class="footer">
2284- [% IF page.has_backlinks -%]
2285- <b>Backlinks:</b>
2286- [%- FOREACH link = page.backlinks -%]
2287- <a href='[% url(link) %]'>[% link.name %]</a>
2288- [%- END -%]
2289- [%- ELSE -%]
2290- <i>No backlinks to this page.</i>
2291- [%- END %]
2292- <br><br>
2293-
2294- [% IF page.has_attachments -%]
2295- <b>Attachments:</b>
2296- <table>
2297- [%- FOREACH file = page.attachments -%]
2298- <tr><td><a href='[% url(file) %]'>[% file.basename %]</a></td><td>&nbsp;</td><td>[% file.size %]</td></tr>
2299- [%- END -%]
2300- </table>
2301- [%- END %]
2302- <!-- End Backlinks -->
2303- </div>
2304- </div>
2305- </body>
2306+<head>
2307+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
2308+ <title>[% title %]</title>
2309+ <meta name='Generator' content='[% generator.name %]'>
2310+ <style type='text/css'>
2311+ a { text-decoration: none }
2312+ a:hover { text-decoration: underline }
2313+ a:active { text-decoration: underline }
2314+ strike { color: grey }
2315+ u { text-decoration: none;
2316+ background-color: yellow }
2317+ tt { color: #2e3436; }
2318+ pre { color: #2e3436;
2319+ margin-left: 20px }
2320+ h1 { text-decoration: underline;
2321+ color: #4e9a06 }
2322+ h2 { color: #4e9a06 }
2323+ h3 { color: #4e9a06 }
2324+ h4 { color: #4e9a06 }
2325+ h5 { color: #4e9a06 }
2326+ div.zim-object {
2327+ border-style:solid;
2328+ border-width:1px;
2329+ }
2330+ span.insen { color: grey }
2331+
2332+ .pages { max-width: 1000px;}
2333+ .menu{
2334+ float:left; width: 300px;
2335+ }
2336+ .content { padding-left: 320px;}
2337+ hr{clear:both;}
2338+ </style>
2339+</head>
2340+<body>
2341+
2342+<!-- Header -->
2343+<div class='header'>
2344+[% IF navigation.prev %]
2345+ [ <a href='[% uri(navigation.prev) %]'>Prev</a> ]
2346+[% ELSE %]
2347+ [ <span class='insen'>Prev</span> ]
2348+[% END %]
2349+
2350+[% IF links.get("index") %]
2351+ [ <a href='[% uri(links.get("index")) %]'>Index</a> ]
2352+[% ELSE %]
2353+ [ <span class='insen'>Index</span> ]
2354+[% END %]
2355+
2356+[% IF navigation.next %]
2357+ [ <a href='[% uri(navigation.next) %]'>Next</a> ]
2358+[% ELSE %]
2359+ [ <span class='insen'>Next</span> ]
2360+[% END %]
2361+</div>
2362+
2363+<hr />
2364+
2365+<div class="menu">
2366+[% index() %]
2367+</div>
2368+
2369+
2370+<!-- Wiki content -->
2371+
2372+<div class='pages'>
2373+<!--[% FOR page IN pages %]-->
2374+ <div class='heading'>
2375+ <h1>[% page.heading %] <a name='[% anchor(page) %]'></a></h1>
2376+ </div>
2377+
2378+ <div class='content'>
2379+ [% page.body %]
2380+ </div>
2381+
2382+ <br />
2383+
2384+ <div class='page-footer'>
2385+ <!--[% FOR link IN page.backlinks %]-->
2386+ [% IF loop.first %]<b>Backlinks:</b>[% END %]
2387+
2388+ <a href='[% uri(link) %]'>[% link.name %]</a>
2389+
2390+ [% IF loop.last %]<br /><br />[% END %]
2391+ <!--[% END %]-->
2392+
2393+ <!--[% FOR file IN page.attachments %]-->
2394+ [% IF loop.first %]
2395+ <b>Attachments:</b>
2396+ <table>
2397+ [% END %]
2398+
2399+ <tr><td><a href='[% uri(file) %]'>[% file.basename %]</a></td><td>&nbsp;</td><td>[% file.size %]</td></tr>
2400+
2401+ [% IF loop.last %]
2402+ </table>
2403+ [% END %]
2404+ <!--[% END %]-->
2405+ </div>
2406+
2407+ [% IF not loop.last %]<hr />[% END %]
2408+
2409+<!--[% END %]-->
2410+</div>
2411+
2412+</body>
2413 </html>
2414
2415=== modified file 'data/templates/html/Presentation.html'
2416--- data/templates/html/Presentation.html 2011-10-28 16:36:10 +0000
2417+++ data/templates/html/Presentation.html 2014-08-19 17:10:33 +0000
2418@@ -1,8 +1,8 @@
2419 <html>
2420 <head>
2421 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
2422- <title>[% page.title %]</title>
2423- <meta name='Generator' content='Zim [% zim.version %]'>
2424+ <title>[% title %]</title>
2425+ <meta name='Generator' content='[% generator.name %]'>
2426 <style type='text/css'>
2427 body { background-color: #fff;
2428 color: #000;
2429@@ -28,11 +28,11 @@
2430 // Slideshow script - adapted from old kwiki package
2431
2432 function nextSlide() {
2433- window.location="[% url(pages.next) %]"
2434+ window.location="[% uri(navigation.next) %]"
2435 }
2436
2437 function prevSlide() {
2438- window.location="[% url(pages.previous) %]"
2439+ window.location="[% uri(navigation.prev) %]"
2440 }
2441
2442
2443@@ -71,11 +71,7 @@
2444 <table width="100%" height="100%" border="0" cellpadding="5" cellspacing="0">
2445 <tr>
2446 <td colspan="3" align="center" valign="middle" class="bar">
2447- [% IF page.properties.type == 'namespace-index' -%]
2448- <h3 class='bar'>Table Of Contents</h3>
2449- [% ELSE %]
2450- <h3 class='bar'>[% page.title %]</h3>
2451- [% END %]
2452+ <h3 class='bar'>[% title %]</h3>
2453 </td>
2454 </tr>
2455 <tr>
2456@@ -85,7 +81,9 @@
2457 <table><tr><td align='left'>
2458 <!-- Wiki content -->
2459
2460+[% FOR page IN pages %]
2461 [% page.body %]
2462+[% END %]
2463
2464 <!-- End wiki content -->
2465
2466@@ -96,16 +94,16 @@
2467 </tr>
2468 <tr>
2469 <td colspan="3" align="center" valign="middle">
2470- [% IF pages.previous -%]
2471- <a href='[% url(pages.previous) %]' class="nav">&lt;</a>
2472+ [% IF navigation.prev -%]
2473+ <a href='[% uri(navigation.prev) %]' class="nav">&lt;</a>
2474 [%- ELSE -%]
2475 <span class="nav">&lt;</span>
2476 [%- END %]
2477
2478- <a href='[% url(pages.index) %]' class="nav">+</a>
2479+ <a href='[% uri(links.get("index")) %]' class="nav">+</a>
2480
2481- [% IF pages.next -%]
2482- <a href='[% url(pages.next) %]' class="nav">&gt;</a>
2483+ [% IF navigation.next -%]
2484+ <a href='[% uri(navigation.next) %]' class="nav">&gt;</a>
2485 [%- ELSE -%]
2486 <span class="nav">&gt;</span>
2487 [%- END %]
2488
2489=== modified file 'data/templates/html/Print.html'
2490--- data/templates/html/Print.html 2013-01-03 06:59:21 +0000
2491+++ data/templates/html/Print.html 2014-08-19 17:10:33 +0000
2492@@ -1,39 +1,41 @@
2493 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2494 <html>
2495- <head>
2496- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
2497- <title>[% page.title %]</title>
2498- <meta name='Generator' content='Zim [% zim.version %]'>
2499- <style type='text/css'>
2500- a { text-decoration: none }
2501- a:hover { text-decoration: underline }
2502- a:active { text-decoration: underline }
2503- strike { color: grey }
2504- u { text-decoration: none;
2505- background-color: yellow }
2506- tt { color: #2e3436; }
2507- pre { color: #2e3436;
2508- margin-left: 20px }
2509- h1 { text-decoration: underline;
2510- color: #4e9a06 }
2511- h2 { color: #4e9a06 }
2512- h3 { color: #4e9a06 }
2513- h4 { color: #4e9a06 }
2514- h5 { color: #4e9a06 }
2515- div.zim-object {
2516- border-style:solid;
2517- border-width:1px;
2518- }
2519- </style>
2520- </head>
2521- <body>
2522+<head>
2523+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
2524+ <title>[% title %]</title>
2525+ <meta name='Generator' content='[% generator.name %]'>
2526+ <style type='text/css'>
2527+ a { text-decoration: none }
2528+ a:hover { text-decoration: underline }
2529+ a:active { text-decoration: underline }
2530+ strike { color: grey }
2531+ u { text-decoration: none;
2532+ background-color: yellow }
2533+ tt { color: #2e3436; }
2534+ pre { color: #2e3436;
2535+ margin-left: 20px }
2536+ h1 { text-decoration: underline;
2537+ color: #4e9a06 }
2538+ h2 { color: #4e9a06 }
2539+ h3 { color: #4e9a06 }
2540+ h4 { color: #4e9a06 }
2541+ h5 { color: #4e9a06 }
2542+ div.zim-object {
2543+ border-style:solid;
2544+ border-width:1px;
2545+ }
2546+ </style>
2547+</head>
2548+<body>
2549
2550 <!-- Wiki content -->
2551
2552-[% page.content %]
2553+[% FOR page IN pages %]
2554+ [% page.content %]
2555+[% END %]
2556
2557 <!-- End wiki content -->
2558
2559- </body>
2560+</body>
2561
2562 </html>
2563
2564=== modified file 'data/templates/html/SlideShow_(S5).html'
2565--- data/templates/html/SlideShow_(S5).html 2008-10-05 20:49:36 +0000
2566+++ data/templates/html/SlideShow_(S5).html 2014-08-19 17:10:33 +0000
2567@@ -1,14 +1,14 @@
2568-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2569+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2570 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2571
2572 <html xmlns="http://www.w3.org/1999/xhtml">
2573
2574-[% SET mode = s5 %]
2575+[% SET mode = "s5" %]
2576
2577 <head>
2578- <title>[% page.title %]</title>
2579+ <title>[% title %]</title>
2580 <!-- metadata -->
2581-<meta name="generator" content="Zim [% zim.version %]" />
2582+<meta name="generator" content="[% generator.name %]" />
2583 <meta name="version" content="S5 1.1" />
2584 <meta name="presdate" content="" />
2585 <meta name="author" content="" />
2586@@ -41,7 +41,9 @@
2587
2588 <div class="presentation">
2589
2590+[% FOR page IN pages %]
2591 [% page.body %]
2592+[% END %]
2593
2594 </div>
2595
2596
2597=== modified file 'data/templates/html/ZeroFiveEight.html'
2598--- data/templates/html/ZeroFiveEight.html 2013-04-13 10:53:03 +0000
2599+++ data/templates/html/ZeroFiveEight.html 2014-08-19 17:10:33 +0000
2600@@ -5,7 +5,7 @@
2601 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
2602
2603 <title>[% title %]</title>
2604- <meta name="generator" content="Zim [% zim.version %]">
2605+ <meta name="generator" content="[% generator.name %]">
2606 <meta name="viewport" content="width=device-width">
2607 <!-- <link href='http://fonts.googleapis.com/css?family=Open+Sans:300&subset=latin,greek,latin-ext' rel='stylesheet' type='text/css'> -->
2608 <![if !IE]>
2609@@ -116,28 +116,27 @@
2610 </head>
2611 <body>
2612 <div class="wrapper">
2613- <div id="navigation"> [% pageindex() %] </div>
2614+ <div id="navigation"> [% index() %] </div>
2615 <div id="content">
2616- [% IF page.properties.type=='namespace-index' -%]
2617- <h1>Document Index</h1>
2618- [%- ELSE -%]
2619- <h1>[% page.heading %]</h1>
2620- [%- END %]
2621+ <!--[% FOR page IN pages %]-->
2622+ <h1>[% page.heading %] <a name='[% anchor(page) %]'></a></h1>
2623
2624 [% page.body %]
2625
2626 <br>
2627- <hr class='footnotes'>
2628 <span class="backlinks">
2629- Backlinks:
2630- [% IF page.backlinks -%]
2631- [%- FOREACH link=page.backlinks -%]
2632- <a href='[% url(link) %]'>[% link.name %]</a>
2633- [%- END -%]
2634- [%- ELSE -%]
2635- none
2636- [%- END %]
2637+ <!--[% FOR link IN page.backlinks %]-->
2638+ [% IF loop.first %]
2639+ <hr class='footnotes'>
2640+ <b>Backlinks:</b>
2641+ [% END %]
2642+ <a href='[% uri(link) %]'>[% link.name %]</a>
2643+ [% IF loop.last %]<br /><br />[% END %]
2644+ <!--[% END %]-->
2645 </span>
2646+
2647+ [% IF not loop.last %]<hr class='pageseparator'/>[% END %]
2648+ <!--[% END %]-->
2649 </div>
2650 </div>
2651 </body>
2652
2653=== modified file 'data/templates/latex/Article.tex'
2654--- data/templates/latex/Article.tex 2014-01-05 14:00:33 +0000
2655+++ data/templates/latex/Article.tex 2014-08-19 17:10:33 +0000
2656@@ -26,7 +26,7 @@
2657
2658 [% options.document_type = 'article' %]
2659
2660-\title{[% page.basename %]}
2661+\title{[% title %]}
2662 \date{[% strftime("%A %d %B %Y") %]}
2663 \author{}
2664
2665@@ -34,6 +34,12 @@
2666
2667 \maketitle
2668
2669-[% page.body %]
2670+[% FOR page IN pages %]
2671+ [% IF loop.first and loop.last %]
2672+ [% page.content %]
2673+ [% ELSE %]
2674+ [% page.content %]
2675+ [% END %]
2676+[% END %]
2677
2678 \end{document}
2679
2680=== modified file 'data/templates/latex/Part.tex'
2681--- data/templates/latex/Part.tex 2010-03-08 19:15:39 +0000
2682+++ data/templates/latex/Part.tex 2014-08-19 17:10:33 +0000
2683@@ -1,4 +1,7 @@
2684 [% options.document_type = 'report' %]
2685-\part{[% page.basename %]}
2686-
2687-[% page.body %]
2688+
2689+\part{[% title %]}
2690+
2691+[% FOR page IN pages %]
2692+[% page.content %]
2693+[% END %]
2694
2695=== modified file 'data/templates/latex/Report.tex'
2696--- data/templates/latex/Report.tex 2012-02-15 21:14:31 +0000
2697+++ data/templates/latex/Report.tex 2014-08-19 17:10:33 +0000
2698@@ -26,7 +26,7 @@
2699
2700 [% options.document_type = 'report' %]
2701
2702-\title{[% page.basename %]}
2703+\title{[% title %]}
2704 \date{[% strftime("%A %d %B %Y") %]}
2705 \author{}
2706
2707@@ -36,6 +36,8 @@
2708
2709 \tableofcontents
2710
2711-[% page.body %]
2712+[% FOR page IN pages %]
2713+[% page.content %]
2714+[% END %]
2715
2716 \end{document}
2717
2718=== modified file 'data/templates/markdown/Default.markdown'
2719--- data/templates/markdown/Default.markdown 2012-02-17 21:29:04 +0000
2720+++ data/templates/markdown/Default.markdown 2014-08-19 17:10:33 +0000
2721@@ -1,2 +1,4 @@
2722+[% FOR page IN pages %]
2723 # [% page.title %]
2724 [% page.body %]
2725+[% END %]
2726
2727=== modified file 'data/templates/rst/Default.rst'
2728--- data/templates/rst/Default.rst 2012-06-12 11:10:48 +0000
2729+++ data/templates/rst/Default.rst 2014-08-19 17:10:33 +0000
2730@@ -1,4 +1,6 @@
2731+[% FOR page IN pages %]
2732 ================
2733 [% page.title %]
2734 ================
2735 [% page.body %]
2736+[% END %]
2737
2738=== modified file 'debian/changelog'
2739--- debian/changelog 2013-04-30 17:27:20 +0000
2740+++ debian/changelog 2014-08-19 17:10:33 +0000
2741@@ -1,3 +1,9 @@
2742+zim (0.61) UNRELEASED; urgency=low
2743+
2744+ * Update to release 0.61
2745+
2746+ -- Jaap Karssenberg <jaap.karssenberg@gmail.com> Thu, 31 Jul 2014 21:44:09 +0200
2747+
2748 zim (0.60) quantal; urgency=low
2749
2750 * Update to release 0.60
2751
2752=== modified file 'tests/applications.py'
2753--- tests/applications.py 2013-12-13 19:15:54 +0000
2754+++ tests/applications.py 2014-08-19 17:10:33 +0000
2755@@ -62,6 +62,9 @@
2756 result = entry.parse_exec(args)
2757 self.assertEqual(result, wanted)
2758
2759+ cwd, argv = entry._checkargs(None, args)
2760+ self.assertEqual(tuple(a.decode(zim.fs.ENCODING) for a in argv), wanted)
2761+
2762 entry['Desktop Entry']['Icon'] = 'xxx'
2763 entry.file = File('/foo.desktop')
2764 for app, args, wanted in (
2765
2766=== modified file 'tests/calendar.py'
2767--- tests/calendar.py 2014-01-14 20:00:50 +0000
2768+++ tests/calendar.py 2014-08-19 17:10:33 +0000
2769@@ -14,6 +14,7 @@
2770 import zim.plugins
2771 from zim.notebook import Path
2772 from zim.templates import get_template
2773+from zim.formats import get_dumper
2774
2775 from zim.plugins.calendar import NotebookExtension, \
2776 MainWindowExtensionDialog, MainWindowExtensionEmbedded, \
2777@@ -205,25 +206,25 @@
2778 def testTemplate(self):
2779 pluginklass = zim.plugins.get_plugin_class('calendar')
2780 plugin = pluginklass()
2781+ plugin.preferences['namespace'] = Path('Calendar')
2782
2783 notebook = tests.new_notebook()
2784-
2785- template = get_template('wiki', 'Journal')
2786+ plugin.extend(notebook)
2787+
2788+ dumper = get_dumper('wiki')
2789+
2790 zim.datetimetz.FIRST_DAY_OF_WEEK = \
2791 zim.datetimetz.MONDAY
2792- plugin.preferences['namespace'] = Path('Calendar')
2793-
2794 for path in (
2795- 'Calendar:2012',
2796- 'Calendar:2012:04:27',
2797- 'Calendar:2012:Week 17',
2798- 'Calendar:2012:04',
2799+ Path('Calendar:2012'),
2800+ Path('Calendar:2012:04:27'),
2801+ Path('Calendar:2012:Week 17'),
2802+ Path('Calendar:2012:04'),
2803 ):
2804- page = notebook.get_page(Path(path))
2805- lines = template.process(notebook, page)
2806- text = ''.join(lines)
2807- #~ print text
2808- self.assertTrue(not 'Created' in text) # No fall back
2809- if 'Week' in path:
2810+ tree = notebook.get_template(path)
2811+ lines = dumper.dump(tree)
2812+ #~ print lines
2813+ self.assertTrue(not 'Created' in ''.join(lines)) # No fall back
2814+ if 'Week' in path.name:
2815 days = [l for l in lines if l.startswith('=== ')]
2816 self.assertEqual(len(days), 7)
2817
2818=== added file 'tests/data/TestTemplate.html'
2819--- tests/data/TestTemplate.html 1970-01-01 00:00:00 +0000
2820+++ tests/data/TestTemplate.html 2014-08-19 17:10:33 +0000
2821@@ -0,0 +1,90 @@
2822+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2823+<html>
2824+ <head>
2825+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
2826+ <title>[% title %]</title>
2827+ <meta name='Generator' content='[% generator.name %]'>
2828+ <style type='text/css'>
2829+ a { text-decoration: none }
2830+ a:hover { text-decoration: underline }
2831+ a:active { text-decoration: underline }
2832+ strike { color: grey }
2833+ u { text-decoration: none;
2834+ background-color: yellow }
2835+ tt { color: #2e3436; }
2836+ pre { color: #2e3436;
2837+ margin-left: 20px }
2838+ h1 { text-decoration: underline;
2839+ color: #4e9a06 }
2840+ h2 { color: #4e9a06 }
2841+ h3 { color: #4e9a06 }
2842+ h4 { color: #4e9a06 }
2843+ h5 { color: #4e9a06 }
2844+ span.zim-tag { color: orange }
2845+ span.insen { color: grey }
2846+ </style>
2847+ </head>
2848+ <body>
2849+
2850+<!-- Header -->
2851+
2852+[% IF navigation.prev %]
2853+ [ <a href='[% uri(navigation.prev) %]'>Prev</a> ]
2854+[% ELSE %]
2855+ [ <span class='insen'>Prev</span> ]
2856+[% END %]
2857+
2858+[% IF links.get("index") %]
2859+ [ <a href='[% uri(links.get("index")) %]'>Index</a> ]
2860+[% ELSE %]
2861+ [ <a href='/'>Index</a> ]
2862+[% END %]
2863+
2864+[% IF navigation.next %]
2865+ [ <a href='[% uri(navigation.next) %]'>Next</a> ]
2866+[% ELSE %]
2867+ [ <span class='insen'>Next</span> ]
2868+[% END %]
2869+
2870+<!-- End Header -->
2871+
2872+ <hr />
2873+
2874+<!-- Wiki content -->
2875+
2876+<!--[% FOR page IN pages %]-->
2877+ <h1>[% page.heading %] <a name='[% anchor(page) %]'>&para;</a></h1>
2878+ [% page.body %]
2879+
2880+ <br />
2881+ <!--[% FOR link IN page.backlinks %]-->
2882+ [% IF loop.first %]<b>Backlinks:</b>[% END %]
2883+
2884+ <a href='[% uri(link) %]'>[% link.name %]</a>
2885+
2886+ [% IF loop.last %]<br /><br />[% END %]
2887+ <!--[% END %]-->
2888+
2889+ [% FOREACH file=page.attachments -%]
2890+ [% IF loop.first %]
2891+ <b>Attachments:</b>
2892+ <table>
2893+ [% END %]
2894+
2895+ <tr><td><a href='[% uri(file) %]'>[% file.basename %]</a></td><td>&nbsp;</td><td>[% file.size %]</td></tr>
2896+
2897+ [% IF loop.last %]
2898+ </table>
2899+ [% END %]
2900+ [% END %]
2901+
2902+ [% IF not loop.last %]<hr />[% END %]
2903+
2904+<!--[% END %]-->
2905+
2906+
2907+<!-- End wiki content -->
2908+
2909+ </body>
2910+
2911+</html>
2912
2913=== modified file 'tests/data/formats/export.html'
2914--- tests/data/formats/export.html 2013-12-23 09:52:33 +0000
2915+++ tests/data/formats/export.html 2014-08-19 17:10:33 +0000
2916@@ -63,7 +63,7 @@
2917
2918 <p>
2919 <a href="mailto:foo@bar.org" title="mailto:foo@bar.org" class="mailto">mailto:foo@bar.org</a><br>
2920-<a href="http://en.wikipedia.org/wiki/Test" title="wp?Test" class="interwiki">wp?Test</a>
2921+<a href="interwiki:wp?Test" title="wp?Test" class="interwiki">wp?Test</a>
2922 </p>
2923
2924 <p>
2925
2926=== modified file 'tests/data/formats/export.markdown'
2927--- tests/data/formats/export.markdown 2012-11-06 18:20:37 +0000
2928+++ tests/data/formats/export.markdown 2014-08-19 17:10:33 +0000
2929@@ -40,7 +40,7 @@
2930 [Foo](Foo)[Bar](Bar)
2931
2932 <mailto:foo@bar.org>
2933-[wp?Test](http://en.wikipedia.org/wiki/Test)
2934+[wp?Test](interwiki:wp?Test)
2935
2936 External links like <http://nongnu.org> and [foo@bar.org](mailto:foo@bar.org) are also supported
2937
2938
2939=== modified file 'tests/data/formats/export.rst'
2940--- tests/data/formats/export.rst 2013-01-05 16:03:45 +0000
2941+++ tests/data/formats/export.rst 2014-08-19 17:10:33 +0000
2942@@ -41,7 +41,7 @@
2943 `Foo <Foo>`_`Bar <Bar>`_
2944
2945 `mailto:foo@bar.org <mailto:foo@bar.org>`_
2946-`wp?Test <http://en.wikipedia.org/wiki/Test>`_
2947+`wp?Test <interwiki:wp?Test>`_
2948
2949 External links like `http://nongnu.org <http://nongnu.org>`_ and `foo@bar.org <mailto:foo@bar.org>`_ are also supported
2950
2951
2952=== modified file 'tests/data/formats/export.tex'
2953--- tests/data/formats/export.tex 2013-01-05 16:03:45 +0000
2954+++ tests/data/formats/export.tex 2014-08-19 17:10:33 +0000
2955@@ -78,7 +78,7 @@
2956
2957 \href{mailto:foo@bar.org}{mailto:foo@bar.org}
2958
2959-\href{http://en.wikipedia.org/wiki/Test}{wp?Test}
2960+\href{interwiki:wp?Test}{wp?Test}
2961
2962
2963
2964
2965=== modified file 'tests/data/templates/html/Default.html'
2966--- tests/data/templates/html/Default.html 2013-03-23 10:09:57 +0000
2967+++ tests/data/templates/html/Default.html 2014-08-19 17:10:33 +0000
2968@@ -1,85 +1,89 @@
2969 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2970 <html>
2971- <head>
2972- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
2973- <title>[% title %]</title>
2974- <meta name='Generator' content='Zim [% zim.version %]'>
2975- <style type='text/css'>
2976- a { text-decoration: none }
2977- a:hover { text-decoration: underline }
2978- a:active { text-decoration: underline }
2979- strike { color: grey }
2980- u { text-decoration: none;
2981- background-color: yellow }
2982- tt { color: #2e3436; }
2983- pre { color: #2e3436;
2984- margin-left: 20px }
2985- h1 { text-decoration: underline;
2986- color: #4e9a06 }
2987- h2 { color: #4e9a06 }
2988- h3 { color: #4e9a06 }
2989- h4 { color: #4e9a06 }
2990- h5 { color: #4e9a06 }
2991- span.insen { color: grey }
2992- </style>
2993- </head>
2994- <body>
2995+<head>
2996+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
2997+ <title>[% title %]</title>
2998+ <meta name='Generator' content='[% generator.name %]'>
2999+ <style type='text/css'>
3000+ a { text-decoration: none }
3001+ a:hover { text-decoration: underline }
3002+ a:active { text-decoration: underline }
3003+ strike { color: grey }
3004+ u { text-decoration: none;
3005+ background-color: yellow }
3006+ tt { color: #2e3436; }
3007+ pre { color: #2e3436;
3008+ margin-left: 20px }
3009+ h1 { text-decoration: underline;
3010+ color: #4e9a06 }
3011+ h2 { color: #4e9a06 }
3012+ h3 { color: #4e9a06 }
3013+ h4 { color: #4e9a06 }
3014+ h5 { color: #4e9a06 }
3015+ span.insen { color: grey }
3016+ div.zim-object {
3017+ border-style:solid;
3018+ border-width:1px;
3019+ }
3020+ </style>
3021+</head>
3022+<body>
3023
3024 <!-- Header -->
3025
3026-[% IF pages.previous -%]
3027- [ <a href='[% url(pages.previous) %]'>Prev</a> ]
3028-[%- ELSE -%]
3029+[% IF navigation.prev %]
3030+ [ <a href='[% uri(navigation.prev) %]'>Prev</a> ]
3031+[% ELSE %]
3032 [ <span class='insen'>Prev</span> ]
3033-[%- END %]
3034-
3035-[% IF page.properties.type == 'namespace-index' -%]
3036- [ <span class='insen'>Index</span> ]
3037-[%- ELSE -%]
3038- [% IF pages.index -%]
3039- [ <a href='[% url(pages.index) %]'>Index</a> ]
3040- [%- ELSE -%]
3041- [ <a href='/'>Index</a> ]
3042- [%- END %]
3043-[%- END %]
3044-
3045-[% IF pages.next -%]
3046- [ <a href='[% url(pages.next) %]'>Next</a> ]
3047-[%- ELSE -%]
3048+[% END %]
3049+
3050+[% IF links.get("index") %]
3051+ [ <a href='[% uri(links.get("index")) %]'>Index</a> ]
3052+[% ELSE %]
3053+ [ <a href='/'>Index</a> ]
3054+[% END %]
3055+
3056+[% IF navigation.next %]
3057+ [ <a href='[% uri(navigation.next) %]'>Next</a> ]
3058+[% ELSE %]
3059 [ <span class='insen'>Next</span> ]
3060-[%- END %]
3061-
3062-<!-- End Header -->
3063+[% END %]
3064
3065 <hr />
3066
3067 <!-- Wiki content -->
3068
3069-[% IF page.properties.type == 'namespace-index' -%]
3070-<h1>Document Index</h1>
3071-[%- ELSE -%]
3072-<h1>[% page.heading %]</h1>
3073-[%- END %]
3074-
3075-[% page.body %]
3076-
3077-<!-- End wiki content -->
3078-
3079-<hr />
3080-<a href="http:/www.zim-wiki.org" style="float:right;">Powered by Zim [% zim.version %]<img src="[% resource('foo/bar.png') %]" alt=" "></a>
3081-<!-- Backlinks -->
3082-
3083-[% IF page.backlinks -%]
3084- Backlinks:
3085- [%- FOREACH link = page.backlinks -%]
3086- <a href='[% url(link) %]'>[% link.name %]</a></li>
3087- [%- END -%]
3088-[%- ELSE -%]
3089- No backlinks to this page.
3090-[%- END %]
3091-
3092-<!-- End Backlinks -->
3093-
3094- </body>
3095+<!--[% FOR page IN pages %]-->
3096+ <h1>[% page.heading %] <a name='[% anchor(page) %]'></a></h1>
3097+ [% page.body %]
3098+
3099+ <br />
3100+ <!--[% FOR link IN page.backlinks %]-->
3101+ [% IF loop.first %]<b>Backlinks:</b>[% END %]
3102+
3103+ <a href='[% uri(link) %]'>[% link.name %]</a>
3104+
3105+ [% IF loop.last %]<br /><br />[% END %]
3106+ <!--[% END %]-->
3107+
3108+ <!--[% FOR file IN page.attachments %]-->
3109+ [% IF loop.first %]
3110+ <b>Attachments:</b>
3111+ <table>
3112+ [% END %]
3113+
3114+ <tr><td><a href='[% uri(file) %]'>[% file.basename %]</a></td><td>&nbsp;</td><td>[% file.size %]</td></tr>
3115+
3116+ [% IF loop.last %]
3117+ </table>
3118+ [% END %]
3119+ <!--[% END %]-->
3120+
3121+ [% IF not loop.last %]<hr />[% END %]
3122+
3123+<!--[% END %]-->
3124+
3125+
3126+</body>
3127
3128 </html>
3129
3130=== modified file 'tests/export.py'
3131--- tests/export.py 2013-10-23 20:31:46 +0000
3132+++ tests/export.py 2014-08-19 17:10:33 +0000
3133@@ -1,43 +1,184 @@
3134 # -*- coding: utf-8 -*-
3135
3136-# Copyright 2009 Jaap Karssenberg <jaap.karssenberg@gmail.com>
3137+# Copyright 2009-2014 Jaap Karssenberg <jaap.karssenberg@gmail.com>
3138+
3139+from __future__ import with_statement
3140
3141 import tests
3142
3143 import os
3144
3145 from zim.fs import _md5, File, Dir
3146+
3147 from zim.config import data_file, SectionedConfigDict
3148 from zim.notebook import Path, Notebook, init_notebook, \
3149 interwiki_link, get_notebook_list, NotebookInfo
3150-from zim.exporter import Exporter, StaticLinker
3151-from zim.applications import Application
3152+#~ from zim.exporter import Exporter, StaticLinker
3153+#~ from zim.applications import Application
3154+from zim.templates import list_templates
3155
3156-import zim.main
3157+from zim.main import ExportCommand
3158
3159 # TODO add check that attachments are copied correctly
3160
3161
3162+from functools import partial
3163+
3164+
3165+from zim.export import *
3166+from zim.export.layouts import *
3167+from zim.export.linker import *
3168+from zim.export.selections import *
3169+from zim.export.template import *
3170+from zim.export.exporters.files import *
3171+from zim.export.exporters.mhtml import MHTMLExporter
3172+
3173+from zim.templates import Template
3174+from zim.templates.expression import ExpressionParameter
3175+
3176+from zim.notebook import Path
3177+
3178+from zim.command import UsageError
3179+
3180+
3181 def md5(f):
3182 return _md5(f.raw())
3183
3184
3185+
3186+class TestMultiFileLayout(tests.TestCase):
3187+
3188+ # dir/
3189+ # `--> _resources/
3190+ # `--> page.html
3191+ # `--> page/
3192+ # `--> attachment.png
3193+
3194+ def runTest(self):
3195+ dir = Dir(self.get_tmp_name())
3196+ rdir = dir.subdir('_resources')
3197+
3198+
3199+ layout = MultiFileLayout(dir, 'html')
3200+ self.assertEqual(layout.relative_root, dir)
3201+ self.assertEqual(layout.resources_dir(), rdir)
3202+
3203+ for path, file, adir in (
3204+ (Path('Foo'), dir.file('Foo.html'), dir.subdir('Foo')),
3205+ (Path('Foo:Bar'), dir.file('Foo/Bar.html'), dir.subdir('Foo/Bar')),
3206+ ):
3207+ self.assertEqual(layout.page_file(path), file)
3208+ self.assertEqual(layout.attachments_dir(path), adir)
3209+
3210+ self.assertRaises(PathLookupError, layout.page_file, Path(':'))
3211+
3212+
3213+ layout = MultiFileLayout(dir, 'html', namespace=Path('Test'))
3214+ self.assertEqual(layout.relative_root, dir)
3215+ self.assertEqual(layout.resources_dir(), rdir)
3216+
3217+ for path, file, adir in (
3218+ (Path('Test:Foo'), dir.file('Foo.html'), dir.subdir('Foo')),
3219+ (Path('Test:Foo:Bar'), dir.file('Foo/Bar.html'), dir.subdir('Foo/Bar')),
3220+ ):
3221+ self.assertEqual(layout.page_file(path), file)
3222+ self.assertEqual(layout.attachments_dir(path), adir)
3223+
3224+ self.assertRaises(PathLookupError, layout.page_file, Path(':'))
3225+ self.assertRaises(PathLookupError, layout.page_file, Path('Foo'))
3226+
3227+
3228+class TestFileLayout(tests.TestCase):
3229+
3230+ # page.html
3231+ # page_files/
3232+ # `--> attachment.png
3233+ # `--> subpage.html
3234+ # `--> subpage/attachment.pdf
3235+ # `--> _resources/
3236+
3237+ def runTest(self):
3238+ tdir = Dir(self.get_tmp_name())
3239+ topfile = tdir.file('page.html')
3240+ dir = tdir.subdir('page_files')
3241+ rdir = dir.subdir('_resources')
3242+
3243+ layout = FileLayout(topfile, Path('Test'), 'html')
3244+ self.assertEqual(layout.relative_root, dir)
3245+ self.assertEqual(layout.resources_dir(), rdir)
3246+
3247+ for path, file, adir in (
3248+ (Path('Test'), topfile, dir),
3249+ (Path('Test:Foo'), dir.file('Foo.html'), dir.subdir('Foo')),
3250+ (Path('Test:Foo:Bar'), dir.file('Foo/Bar.html'), dir.subdir('Foo/Bar')),
3251+ ):
3252+ self.assertEqual(layout.page_file(path), file)
3253+ self.assertEqual(layout.attachments_dir(path), adir)
3254+
3255+ self.assertRaises(PathLookupError, layout.page_file, Path(':'))
3256+ self.assertRaises(PathLookupError, layout.page_file, Path('Foo'))
3257+
3258+
3259+class TestSingleFileLayout(tests.TestCase):
3260+
3261+ # page.html
3262+ # page_files/
3263+ # `--> attachment.png
3264+ # `--> subpage/attachment.pdf
3265+ # `--> _resources/
3266+
3267+ def runTest(self):
3268+ tdir = Dir(self.get_tmp_name())
3269+ topfile = tdir.file('page.html')
3270+ dir = tdir.subdir('page_files')
3271+ rdir = dir.subdir('_resources')
3272+
3273+ layout = SingleFileLayout(topfile, page=Path('Test'))
3274+ self.assertEqual(layout.relative_root, dir)
3275+ self.assertEqual(layout.resources_dir(), rdir)
3276+
3277+ for path, file, adir in (
3278+ (Path('Test'), topfile, dir),
3279+ (Path('Test:Foo'), topfile, dir.subdir('Foo')),
3280+ (Path('Test:Foo:Bar'), topfile, dir.subdir('Foo/Bar')),
3281+ ):
3282+ self.assertEqual(layout.page_file(path), file)
3283+ self.assertEqual(layout.attachments_dir(path), adir)
3284+
3285+ self.assertRaises(PathLookupError, layout.page_file, Path(':'))
3286+ self.assertRaises(PathLookupError, layout.page_file, Path('Foo'))
3287+
3288+
3289+
3290 class TestLinker(tests.TestCase):
3291
3292 def runTest(self):
3293- '''Test proper linking of files in export'''
3294- notebook = tests.new_notebook(fakedir='/source/dir/')
3295-
3296- linker = StaticLinker('html', notebook)
3297- linker.set_usebase(True) # normally set by html format module
3298- linker.set_path(Path('foo:bar')) # normally set by exporter
3299- linker.set_base(Dir('/source/dir/foo')) # normally set by exporter
3300-
3301- self.assertEqual(linker.link_page('+dus'), './bar/dus.html')
3302- self.assertEqual(linker.link_page('dus'), './dus.html')
3303- self.assertEqual(linker.link_file('./dus.pdf'), './bar/dus.pdf')
3304- self.assertEqual(linker.link_file('../dus.pdf'), './dus.pdf')
3305- self.assertEqual(linker.link_file('../../dus.pdf'), '../dus.pdf')
3306+ dir = Dir(self.get_tmp_name())
3307+ notebook = tests.new_notebook(fakedir=dir.subdir('notebook'))
3308+ layout = MultiFileLayout(dir.subdir('layout'), 'html')
3309+ source = Path('foo:bar')
3310+ output = layout.page_file(source)
3311+
3312+ linker = ExportLinker(notebook, layout,
3313+ source=source, output=output, usebase=True
3314+ )
3315+
3316+ self.assertEqual(linker.link('+dus'), './bar/dus.html')
3317+ self.assertEqual(linker.link('dus'), './dus.html')
3318+ self.assertEqual(linker.link('./dus.pdf'), './bar/dus.pdf')
3319+ self.assertEqual(linker.link('../dus.pdf'), './dus.pdf')
3320+ self.assertEqual(linker.link('../../dus.pdf'), '../dus.pdf')
3321+ self.assertEqual(linker.link('/dus.pdf'), File('/dus.pdf').uri)
3322+
3323+ # TODO:
3324+ # img
3325+ # icon
3326+ # resource
3327+ # resolve_source_file
3328+ # page_object
3329+ # file_object
3330+ #
3331+ # document_root_url
3332
3333 ## setup environment for interwiki link
3334 if os.name == 'nt':
3335@@ -55,139 +196,432 @@
3336 self.assertEqual(linker.link('foo?Ideas:Task List'), uri + '/Ideas/Task_List.txt')
3337
3338
3339-@tests.slowTest
3340-class TestExport(tests.TestCase):
3341-
3342- options = {'format': 'html', 'template': 'Default'}
3343-
3344- def setUp(self):
3345- self.dir = Dir(self.create_tmp_dir('exported_files'))
3346-
3347- def export(self):
3348- notebook = tests.new_notebook(fakedir='/foo/bar')
3349-
3350- exporter = Exporter(notebook, **self.options)
3351- exporter.export_all(self.dir)
3352-
3353- def runTest(self):
3354- '''Test export notebook to html'''
3355- self.export()
3356-
3357- file = self.dir.file('Test/foo.html')
3358- self.assertTrue(file.exists())
3359- text = file.read()
3360- self.assertTrue('<!-- Wiki content -->' in text, 'template used')
3361- self.assertTrue('<h1>Foo</h1>' in text)
3362-
3363- for icon in ('checked-box',): #'unchecked-box', 'xchecked-box'):
3364- # Default template doesn't have its own checkboxes
3365- self.assertTrue(self.dir.file('_resources/%s.png' % icon).exists())
3366- self.assertEqual(
3367- md5(self.dir.file('_resources/%s.png' % icon)),
3368- md5(data_file('pixmaps/%s.png' % icon))
3369- )
3370-
3371-
3372-@tests.slowTest
3373-class TestExportTemplateResources(TestExport):
3374-
3375- data = './tests/data/templates/'
3376-
3377- options = {
3378- 'format': 'html',
3379- 'template': './tests/data/templates/html/Default.html'
3380- }
3381-
3382- def runTest(self):
3383- pass # should not run, block just in case
3384-
3385- def testExportResources(self):
3386- '''Test export notebook to html with template resources'''
3387- self.export()
3388-
3389- file = self.dir.file('Test/foo.html')
3390- self.assertTrue(file.exists())
3391- text = file.read()
3392- self.assertTrue('src="../_resources/foo/bar.png"' in text)
3393- self.assertTrue(self.dir.file('_resources/foo/bar.png').exists())
3394-
3395- for icon in ('checked-box',): #'unchecked-box', 'xchecked-box'):
3396- # Template has its own checkboxes
3397- self.assertTrue(self.dir.file('_resources/%s.png' % icon).exists())
3398- self.assertNotEqual(
3399- md5(self.dir.file('_resources/%s.png' % icon)),
3400- md5(data_file('pixmaps/%s.png' % icon))
3401- )
3402-
3403- def testListTemplates(self):
3404- '''Assert list templates still works with resource folders present'''
3405- import shutil
3406- from zim.config import XDG_DATA_HOME
3407- from zim.templates import list_templates, get_template
3408-
3409- # Make sure our template with resources is first in line
3410- datahome = XDG_DATA_HOME.subdir('zim/templates/')
3411- assert not datahome.exists()
3412- shutil.copytree(self.data, datahome.path)
3413-
3414- for name, basename in list_templates('html'):
3415- if name == 'Default':
3416- self.assertEqual(basename, 'Default.html')
3417-
3418- template = get_template('html', 'Default')
3419- self.assertEqual(template.file, datahome.file('html/Default.html').path)
3420- self.assertEqual(template.resources_dir, datahome.subdir('html/Default'))
3421- self.assertTrue(template.resources_dir.exists())
3422-
3423-
3424-
3425-class TestExportFullOptions(TestExport):
3426-
3427- options = {'format': 'html', 'template': 'Default',
3428- 'index_page': 'index', 'document_root_url': 'http://foo.org/'}
3429-
3430- def runTest(self):
3431- '''Test export notebook to html with all options'''
3432- TestExport.runTest(self)
3433- file = self.dir.file('index.html')
3434- self.assertTrue(file.exists())
3435- indexcontent = file.read()
3436- self.assertTrue('<a href="./Test/foo.html" title="foo" class="page">foo</a>' in indexcontent)
3437-
3438-
3439-class TestExportCommandLine(TestExportFullOptions):
3440-
3441- def export(self):
3442- dir = Dir(self.create_tmp_dir('source_files'))
3443- init_notebook(dir)
3444- notebook = Notebook(dir=dir)
3445- for name, text in tests.WikiTestData:
3446- page = notebook.get_page(Path(name))
3447- page.parse('wiki', text)
3448- notebook.store_page(page)
3449- file = dir.file('Test/foo.txt')
3450- self.assertTrue(file.exists())
3451-
3452- argv = ('./zim.py', '--export', '--template=Default', dir.path, '--output', self.dir.path, '--index-page', 'index')
3453- #~ zim = Application(argv)
3454- #~ zim.run()
3455-
3456- cmd = zim.main.build_command(argv[1:])
3457+
3458+
3459+
3460+class TestExportTemplateContext(tests.TestCase):
3461+
3462+ def setUp(self):
3463+ tmpdir = self.get_tmp_name()
3464+ notebook = tests.new_notebook(tmpdir + '/notebook')
3465+ layout = MultiFileLayout(Dir(tmpdir + '/export'), 'html')
3466+ linker_factory = partial(ExportLinker,
3467+ notebook=notebook,
3468+ layout=layout,
3469+ output=layout.page_file(Path('test')),
3470+ usebase=True
3471+ )
3472+ dumper_factory = get_format('html').Dumper
3473+
3474+ title = 'Test Export'
3475+ self.content = [notebook.get_page(Path('Test:foo'))]
3476+ self.context = ExportTemplateContext(
3477+ notebook, linker_factory, dumper_factory,
3478+ title, self.content, special=None,
3479+ home=None, up=None, prevpage=None, nextpage=None,
3480+ links=None,
3481+ )
3482+
3483+ def runTest(self):
3484+ def get(name):
3485+ param = ExpressionParameter(name)
3486+ return param(self.context)
3487+
3488+ # Test context setup
3489+ self.assertIsInstance(get('generator.name'), basestring)
3490+ self.assertTrue(get('generator.name').startswith('Zim'))
3491+ self.assertIsInstance(get('generator.user'), basestring)
3492+
3493+ self.assertEqual(get('title'), 'Test Export')
3494+
3495+ pages = list(get('pages'))
3496+ self.assertEqual(len(pages), 1)
3497+ self.assertTrue(all(isinstance(o, PageProxy) for o in pages))
3498+ self.assertEqual(pages[0]._page, self.content[0])
3499+ ## TODO
3500+ # pages
3501+ # content
3502+ # special
3503+ #
3504+ # navigation - links to other export pages (if not included here)
3505+ # home
3506+ # up
3507+ # prev -- prev export file or None
3508+ # next -- next export file or None
3509+ #
3510+ # links -- links to other export pages (index & plugins / ...) - sorted dict to have Index, Home first followed by plugins
3511+ #
3512+ # link
3513+ # .name
3514+ # .basename
3515+
3516+
3517+ # Test PageProxy
3518+ self.context['mypage'] = pages[0]
3519+ self.assertEqual(get('mypage.title'), 'Foo')
3520+ self.assertEqual(get('mypage.name'), 'Test:foo')
3521+ self.assertEqual(get('mypage.namespace'), 'Test')
3522+ self.assertEqual(get('mypage.basename'), 'foo')
3523+
3524+ self.assertEqual(get('mypage.heading'), 'Foo')
3525+ self.assertIsInstance(get('mypage.content'), basestring)
3526+ self.assertIsInstance(get('mypage.body'), basestring)
3527+ self.assertIsInstance(get('mypage.properties'), dict)
3528+
3529+
3530+ # .links
3531+ # .backlinks
3532+ # .attachments
3533+ #
3534+
3535+ # Test FileProxy
3536+ # file
3537+ # .basename
3538+ # .mtime
3539+ # .size
3540+ #
3541+
3542+ ## TODO
3543+ # options -- dict with template options (for format)
3544+ #
3545+ # toc([page]) -- iter of headings in this page or all of pages
3546+ # index([namespace]) -- index of full export job, not just in this page
3547+ # uri(link|file)
3548+ # resource(file)
3549+ # anchor(page|section)
3550+ #
3551+ # From template:
3552+ # range() / len() / sorted() / reversed()
3553+ # strftime()
3554+ # strfcal()
3555+ #
3556+ # test single page by "IF loop.first and loop.last"
3557+
3558+
3559+ ## TODO test all of the attributes / items accesible through the
3560+ ## context dict are string, expressionfunction, or proxy defined in this module
3561+
3562+ ## TODO test modification of options by template ends up in context
3563+ ## test setting other local paramters in template does NOT affect context object
3564+ ## test setting page properties is NOT allowed
3565+
3566+ ## TODO list simple template with processor to test looping through pages
3567+
3568+
3569+
3570+
3571+class TestPageSelections(tests.TestCase):
3572+
3573+ def _test_iface(self, selection):
3574+ self.assertIsNotNone(selection.name)
3575+ self.assertIsNotNone(selection.title)
3576+ self.assertIsNotNone(selection.notebook)
3577+ for p in selection:
3578+ self.assertIsInstance(p, Page)
3579+
3580+ # TODO add alternative method to walk names for ToC
3581+ # TODO add __len__ that gives total pages for progress
3582+ # TODO Use collections subclass to make interface complete ?
3583+
3584+ def testAllPages(self):
3585+ selection = AllPages(tests.new_notebook())
3586+ self._test_iface(selection)
3587+
3588+ def testSinglePage(self):
3589+ notebook = tests.new_notebook()
3590+ page = notebook.get_page(Path('Test'))
3591+ selection = SinglePage(notebook, page)
3592+ self._test_iface(selection)
3593+ self.assertIsNotNone(selection.prefix)
3594+
3595+ def testSubPages(self):
3596+ notebook = tests.new_notebook()
3597+ page = notebook.get_page(Path('Test'))
3598+ selection = SinglePage(notebook, page)
3599+ self._test_iface(selection)
3600+ self.assertIsNotNone(selection.prefix)
3601+
3602+
3603+class TestMultiFileExporter(tests.TestCase):
3604+
3605+ def runTest(self):
3606+ dir = Dir(self.create_tmp_dir())
3607+ #~ dir = VirtualDir('/test')
3608+ notebook = tests.new_notebook(fakedir='/foo')
3609+ pages = AllPages(notebook)
3610+
3611+ exporter = build_notebook_exporter(dir, 'html', 'Default')
3612+ self.assertIsInstance(exporter, MultiFileExporter)
3613+ exporter.export(pages)
3614+
3615+ file = exporter.layout.page_file(Path('roundtrip'))
3616+ text = file.read()
3617+ self.assertIn('Lorem ipsum dolor sit amet', text)
3618+
3619+
3620+
3621+class TestSingleFileExporter(tests.TestCase):
3622+
3623+ def runTest(self):
3624+ dir = Dir(self.create_tmp_dir())
3625+ #~ dir = VirtualDir('/test')
3626+ file = dir.file('export.html')
3627+ notebook = tests.new_notebook(fakedir='/foo')
3628+ pages = AllPages(notebook)
3629+
3630+ exporter = build_single_file_exporter(file, 'html', 'Default')
3631+ self.assertIsInstance(exporter, SingleFileExporter)
3632+ exporter.export(pages)
3633+
3634+ text = file.read()
3635+ self.assertIn('Lorem ipsum dolor sit amet', text)
3636+
3637+
3638+@tests.slowTest # Slow because it uses a tmp file internally
3639+class TestMHTMLExporter(tests.TestCase):
3640+
3641+ def runTest(self):
3642+ dir = Dir(self.create_tmp_dir())
3643+ #~ dir = VirtualDir('/test')
3644+ file = dir.file('export.mht')
3645+ notebook = tests.new_notebook(fakedir='/foo')
3646+ pages = AllPages(notebook)
3647+
3648+ exporter = build_mhtml_file_exporter(file, 'Default')
3649+ self.assertIsInstance(exporter, MHTMLExporter)
3650+ exporter.export(pages)
3651+
3652+ text = file.read()
3653+ self.assertIn('Lorem ipsum dolor sit amet', text)
3654+
3655+
3656+class TestExportFormat(object):
3657+
3658+ def runTest(self):
3659+ dir = Dir(self.create_tmp_dir())
3660+ #~ dir = VirtualDir('/test')
3661+
3662+ i = 0
3663+ print ''
3664+ for template, file in list_templates(self.format):
3665+ print 'Testing template: %s' % template
3666+ notebook = tests.new_notebook(fakedir='/foo')
3667+ pages = AllPages(notebook) # TODO - sub-section ?
3668+ exporter = build_notebook_exporter(dir.subdir(template), self.format, template)
3669+ self.assertIsInstance(exporter, MultiFileExporter)
3670+ exporter.export(pages)
3671+
3672+ file = exporter.layout.page_file(Path('roundtrip'))
3673+ text = file.read()
3674+ self.assertIn('Lorem ipsum dolor sit amet', text)
3675+
3676+ i += 1
3677+
3678+ if self.format in ('html', 'latex'):
3679+ self.assertTrue(i >= 3)
3680+
3681+
3682+class TestExportFormatHtml(TestExportFormat, tests.TestCase):
3683+ format = 'html'
3684+
3685+
3686+class TestExportFormatLatex(TestExportFormat, tests.TestCase):
3687+ format = 'latex'
3688+
3689+
3690+class TestExportFormatMarkDown(TestExportFormat, tests.TestCase):
3691+ format = 'markdown'
3692+
3693+
3694+class TestExportFormatRst(TestExportFormat, tests.TestCase):
3695+ format = 'rst'
3696+
3697+
3698+
3699+## TODO test all exports templates
3700+
3701+
3702+#~ @tests.slowTest
3703+#~ class TestExportTemplateResources(TestExport):
3704+#~
3705+ #~ data = './tests/data/templates/'
3706+#~
3707+ #~ options = {
3708+ #~ 'format': 'html',
3709+ #~ 'template': './tests/data/templates/html/Default.html'
3710+ #~ }
3711+#~
3712+ #~ def runTest(self):
3713+ #~ pass # should not run, block just in case
3714+#~
3715+ #~ def testExportResources(self):
3716+ #~ '''Test export notebook to html with template resources'''
3717+ #~ self.export()
3718+#~
3719+ #~ file = self.dir.file('Test/foo.html')
3720+ #~ self.assertTrue(file.exists())
3721+ #~ text = file.read()
3722+ #~ self.assertTrue('src="../_resources/foo/bar.png"' in text)
3723+ #~ self.assertTrue(self.dir.file('_resources/foo/bar.png').exists())
3724+#~
3725+ #~ for icon in ('checked-box',): #'unchecked-box', 'xchecked-box'):
3726+ #~ # Template has its own checkboxes
3727+ #~ self.assertTrue(self.dir.file('_resources/%s.png' % icon).exists())
3728+ #~ self.assertNotEqual(
3729+ #~ md5(self.dir.file('_resources/%s.png' % icon)),
3730+ #~ md5(data_file('pixmaps/%s.png' % icon))
3731+ #~ )
3732+#~
3733+ #~ def testListTemplates(self):
3734+ #~ '''Assert list templates still works with resource folders present'''
3735+ #~ import shutil
3736+ #~ from zim.config import XDG_DATA_HOME
3737+ #~ from zim.templates import list_templates, get_template
3738+#~
3739+ #~ # Make sure our template with resources is first in line
3740+ #~ datahome = XDG_DATA_HOME.subdir('zim/templates/')
3741+ #~ assert not datahome.exists()
3742+ #~ shutil.copytree(self.data, datahome.path)
3743+#~
3744+ #~ for name, basename in list_templates('html'):
3745+ #~ if name == 'Default':
3746+ #~ self.assertEqual(basename, 'Default.html')
3747+#~
3748+ #~ template = get_template('html', 'Default')
3749+ #~ self.assertEqual(template.file, datahome.file('html/Default.html').path)
3750+ #~ self.assertEqual(template.resources_dir, datahome.subdir('html/Default'))
3751+ #~ self.assertTrue(template.resources_dir.exists())
3752+
3753+
3754+
3755+class TestExportCommand(tests.TestCase):
3756+
3757+ def setUp(self):
3758+ self.tmpdir = Dir(self.create_tmp_dir())
3759+ self.notebook = self.tmpdir.subdir('notebook')
3760+ init_notebook(self.notebook)
3761+
3762+ def testOptions(self):
3763+ # Only testing we get a valid exporter, not the full command,
3764+ # because the command is very slow
3765+
3766+ ## Full notebook, minimal options
3767+ cmd = ExportCommand('export')
3768+ cmd.parse_options(self.notebook.path)
3769+ self.assertRaises(UsageError, cmd.get_exporter, None)
3770+
3771+ cmd = ExportCommand('export')
3772+ cmd.parse_options(self.notebook.path,
3773+ '--output', self.tmpdir.subdir('output').path,
3774+ )
3775+ exp = cmd.get_exporter(None)
3776+ self.assertIsInstance(exp, MultiFileExporter)
3777+ self.assertIsInstance(exp.layout, MultiFileLayout)
3778+ self.assertIsInstance(exp.layout.dir, Dir)
3779+ self.assertIsInstance(exp.template, Template)
3780+ self.assertIsNone(exp.document_root_url)
3781+ self.assertIsNotNone(exp.format)
3782+ self.assertIsNone(exp.index_page)
3783+
3784+ ## Full notebook, full options
3785+ cmd = ExportCommand('export')
3786+ cmd.parse_options(self.notebook.path,
3787+ '--format', 'markdown',
3788+ '--template', './tests/data/TestTemplate.html',
3789+ '--output', self.tmpdir.subdir('output').path,
3790+ '--root-url', '/foo/',
3791+ '--index-page', 'myindex',
3792+ '--overwrite',
3793+ )
3794+ exp = cmd.get_exporter(None)
3795+ self.assertIsInstance(exp, MultiFileExporter)
3796+ self.assertIsInstance(exp.layout, MultiFileLayout)
3797+ self.assertIsInstance(exp.layout.dir, Dir)
3798+ self.assertIsInstance(exp.template, Template)
3799+ self.assertIsNotNone(exp.document_root_url)
3800+ self.assertIsNotNone(exp.format)
3801+ self.assertIsNotNone(exp.index_page)
3802+
3803+ ## Single page
3804+ cmd = ExportCommand('export')
3805+ cmd.parse_options(self.notebook.path, 'Foo:Bar',
3806+ '--output', self.tmpdir.subdir('output').path,
3807+ )
3808+ exp = cmd.get_exporter(Path('Foo:Bar'))
3809+ self.assertIsInstance(exp, MultiFileExporter)
3810+ self.assertIsInstance(exp.layout, FileLayout)
3811+ self.assertIsInstance(exp.layout.file, File)
3812+ self.assertIsInstance(exp.template, Template)
3813+ self.assertIsNone(exp.document_root_url)
3814+ self.assertIsNotNone(exp.format)
3815+ self.assertIsNone(exp.index_page)
3816+
3817+ cmd = ExportCommand('export')
3818+ cmd.parse_options(self.notebook.path, 'Foo:Bar',
3819+ '--recursive',
3820+ '--output', self.tmpdir.subdir('output').path,
3821+ )
3822+ exp = cmd.get_exporter(Path('Foo:Bar'))
3823+ self.assertIsInstance(exp, MultiFileExporter)
3824+ self.assertIsInstance(exp.layout, FileLayout)
3825+ self.assertIsInstance(exp.layout.file, File)
3826+ self.assertIsInstance(exp.template, Template)
3827+ self.assertIsNone(exp.document_root_url)
3828+ self.assertIsNotNone(exp.format)
3829+ self.assertIsNone(exp.index_page)
3830+
3831+ cmd = ExportCommand('export')
3832+ cmd.parse_options(self.notebook.path, 'Foo:Bar',
3833+ '-rs',
3834+ '--output', self.tmpdir.subdir('output').path,
3835+ )
3836+ exp = cmd.get_exporter(Path('Foo:Bar'))
3837+ self.assertIsInstance(exp, SingleFileExporter)
3838+ self.assertIsInstance(exp.layout, SingleFileLayout)
3839+ self.assertIsInstance(exp.layout.file, File)
3840+ self.assertIsInstance(exp.template, Template)
3841+ self.assertIsNone(exp.document_root_url)
3842+ self.assertIsNotNone(exp.format)
3843+
3844+ ## MHTML exporter
3845+ cmd = ExportCommand('export')
3846+ cmd.parse_options(self.notebook.path, 'Foo:Bar',
3847+ '-rs', '--format', 'mhtml',
3848+ '--output', self.tmpdir.subdir('output').path,
3849+ )
3850+ exp = cmd.get_exporter(Path('Foo:Bar'))
3851+ self.assertIsInstance(exp, MHTMLExporter)
3852+ self.assertIsInstance(exp.file, File)
3853+ self.assertIsInstance(exp.template, Template)
3854+ self.assertIsNone(exp.document_root_url)
3855+
3856+ @tests.slowTest
3857+ def testExport(self):
3858+ # Only test single page, just to show "run()" works
3859+ file = self.notebook.file('Foo/Bar.txt')
3860+ file.write('=== Foo\ntest 123\n')
3861+
3862+ output = self.tmpdir.file('output.html')
3863+
3864+ cmd = ExportCommand('export')
3865+ cmd.parse_options(self.notebook.path, 'Foo:Bar',
3866+ '--output', output.path,
3867+ '--template', 'tests/data/TestTemplate.html'
3868+ )
3869 cmd.run()
3870
3871- def runTest(self):
3872- '''Test export notebook to html from commandline'''
3873- TestExportFullOptions.runTest(self)
3874-
3875-# TODO test export single page from command line
3876+ self.assertTrue(output.exists())
3877+ html = output.read()
3878+ self.assertTrue('<h1>Foo' in html)
3879+ self.assertTrue('test 123' in html)
3880
3881
3882 @tests.slowTest
3883 class TestExportDialog(tests.TestCase):
3884
3885- def runTest(self):
3886+ def testDialog(self):
3887 '''Test ExportDialog'''
3888- from zim.gui.exportdialog import ExportDialog
3889+ from zim.gui.exportdialog import ExportDialog, ExportDoneDialog
3890
3891 dir = Dir(self.create_tmp_dir())
3892
3893@@ -215,7 +649,8 @@
3894 page = dialog.get_page()
3895 page.form['folder'] = dir
3896 page.form['index'] = 'INDEX_PAGE'
3897- dialog.assert_response_ok()
3898+ with tests.DialogContext(ExportDoneDialog):
3899+ dialog.assert_response_ok()
3900
3901 file = dir.file('Test/foo.html')
3902 self.assertTrue(file.exists())
3903@@ -243,7 +678,8 @@
3904
3905 page = dialog.get_page()
3906 page.form['file'] = dir.file('SINGLE_FILE_EXPORT.html').path
3907- dialog.assert_response_ok()
3908+ with tests.DialogContext(ExportDoneDialog):
3909+ dialog.assert_response_ok()
3910
3911 file = dir.file('SINGLE_FILE_EXPORT.html')
3912 self.assertTrue(file.exists())
3913@@ -255,3 +691,74 @@
3914 self.assertEqual(dialog.uistate, ui.uistate['ExportDialog'])
3915 self.assertIsInstance(dialog.uistate['output_file'], File)
3916 self.assertIsInstance(dialog.uistate['output_folder'], Dir) # Keep this in state as well
3917+
3918+ def testLogging(self):
3919+ from zim.gui.exportdialog import LogContext
3920+
3921+ mylogger = logging.getLogger('zim.export')
3922+ foologger = logging.getLogger('zim.foo')
3923+ log_context = LogContext()
3924+
3925+ with tests.LoggingFilter(message='Test'):
3926+ with log_context:
3927+ mylogger.warn('Test export warning')
3928+ mylogger.debug('Test export debug')
3929+ foologger.warn('Test foo')
3930+
3931+ file = log_context.file
3932+ self.assertTrue(file.exists())
3933+ #~ print ">>>\n", file.read(), "\n<<<"
3934+ self.assertTrue('Test export warning' in file.read())
3935+ self.assertFalse('Test export debug' in file.read())
3936+ self.assertFalse('Test foo' in file.read())
3937+
3938+
3939+class VirtualDir(object):
3940+
3941+ def __init__(self, path):
3942+ self.path = path
3943+ if '/' in path:
3944+ x, self.basename = path.rsplit('/', 1)
3945+ else:
3946+ self.basename = path
3947+ self._contents = {}
3948+
3949+ def file(self, path):
3950+ # TODO normalize path
3951+ if path in self._contents:
3952+ assert isinstance(self._contents[path], VirtualFile)
3953+ else:
3954+ self._contents[path] = VirtualFile(self, self.path + '/' + path)
3955+ return self._contents[path]
3956+
3957+ def subdir(self, path):
3958+ # TODO normalize path
3959+ if path in self._contents:
3960+ assert isinstance(self._contents[path], VirtualDir)
3961+ else:
3962+ self._contents[path] = VirtualDir(self.path + '/' + path)
3963+ return self._contents[path]
3964+
3965+
3966+class VirtualFile(object):
3967+
3968+ def __init__(self, dir, path):
3969+ self.path = path
3970+ if '/' in path:
3971+ x, self.basename = path.rsplit('/', 1)
3972+ else:
3973+ self.basename = path
3974+ self.dir = dir
3975+ self._contents = []
3976+
3977+ def write(self, text):
3978+ self._contents.append(text)
3979+
3980+ def writelines(self, lines):
3981+ self._contents.extend(lines)
3982+
3983+ def read(self):
3984+ return ''.join(self._contents)
3985+
3986+ def readlines(self):
3987+ return ''.join(self._contents).splitlines(True)
3988
3989=== modified file 'tests/formats.py'
3990--- tests/formats.py 2014-02-03 17:03:46 +0000
3991+++ tests/formats.py 2014-08-19 17:10:33 +0000
3992@@ -72,8 +72,7 @@
3993 # Dumper
3994 wanted = self.getReferenceData()
3995 reftree = tests.new_parsetree_from_xml(self.reference_xml)
3996- linker = StubLinker()
3997- linker.set_base(Dir('tests/data/formats'))
3998+ linker = StubLinker(Dir('tests/data/formats'))
3999 dumper = self.format.Dumper(linker=linker)
4000 result = ''.join(dumper.dump(reftree))
4001 #~ print '\n' + '>'*80 + '\n' + result + '\n' + '<'*80 + '\n'
4002@@ -663,58 +662,15 @@
4003 wanted = r'$\backslash$foo \$ \% \^{} $\backslash$\% bar \textless{} \textgreater{}'
4004 self.assertEqual(format.Dumper.encode_text(input), wanted)
4005
4006- def testExport(self):
4007- '''test the export of a wiki page to latex'''
4008- with LatexLoggingFilter():
4009- format = get_format('LaTeX')
4010- testpage = tests.WikiTestData.get('Test:wiki')
4011- tree = get_format('wiki').Parser().parse(testpage)
4012- output = format.Dumper(linker=StubLinker()).dump(tree)
4013- #~ print '>>>\n' + ''.join(output) + '<<<'
4014- self.assertTrue('\chapter{Foo Bar}\n' in output)
4015-
4016- # Test template_options.document_type
4017- input = r'''
4018-[% options.document_type = 'book' -%]
4019-\title{[% page.basename %]}
4020-
4021-\begin{document}
4022-\maketitle
4023-\tableofcontents
4024-[% page.body %]
4025-\end{document}
4026-'''
4027- wanted = r'''
4028-\title{FooBar}
4029-
4030-\begin{document}
4031-\maketitle
4032-\tableofcontents
4033-\textbf{foo bar !}
4034-
4035-
4036-
4037-\chapter{Heading 2}
4038-
4039-duss
4040-
4041-
4042-\end{document}
4043-'''
4044-
4045- notebook = tests.new_notebook()
4046- page = notebook.get_page(Path('FooBar'))
4047- page.parse('wiki', '''\
4048-====== Page Heading ======
4049-**foo bar !**
4050-
4051-===== Heading 2 =====
4052-duss
4053-''')
4054-
4055- template = Template(input, 'latex', linker=StubLinker())
4056- result = template.process(notebook, page)
4057- self.assertEqual(''.join(result), wanted)
4058+
4059+class StubFile(object):
4060+
4061+ def __init__(self, path, text):
4062+ self.path = path
4063+ self.text = text
4064+
4065+ def read(self):
4066+ return self.text
4067
4068
4069 class TestOldParseTreeBuilder(tests.TestCase):
4070
4071=== modified file 'tests/gui.py'
4072--- tests/gui.py 2014-01-16 20:30:53 +0000
4073+++ tests/gui.py 2014-08-19 17:10:33 +0000
4074@@ -249,7 +249,7 @@
4075 'X-Zim-ExecTool': 'foo %u',
4076 'X-Zim-ReadOnly': False,
4077 'X-Zim-ShowInToolBar': False,
4078-
4079+ 'X-Zim-ReplaceSelection': False,
4080 }
4081 dialog.manager.create(**properties)
4082 dialog.listview.refresh()
4083@@ -263,7 +263,7 @@
4084 'X-Zim-ExecTool': 'foo %u',
4085 'X-Zim-ReadOnly': False,
4086 'X-Zim-ShowInToolBar': False,
4087-
4088+ 'X-Zim-ReplaceSelection': False,
4089 }
4090 dialog.form.update(input)
4091 output = dialog.assert_response_ok()
4092
4093=== modified file 'tests/parsing.py'
4094--- tests/parsing.py 2014-01-04 16:43:23 +0000
4095+++ tests/parsing.py 2014-08-19 17:10:33 +0000
4096@@ -136,56 +136,99 @@
4097 class TestSimpleTreeBuilder(tests.TestCase):
4098
4099 def runTest(self):
4100- builder = SimpleTreeBuilder(merge_text=False)
4101-
4102- builder.start('root', {})
4103- builder.text('foo')
4104- builder.text('bar')
4105- builder.append('dus', {}, 'ja')
4106- builder.text('foo')
4107- builder.text('bar')
4108- builder.append('br', {})
4109- builder.text('foo')
4110- builder.text('bar')
4111- builder.end('root')
4112-
4113- root = builder.get_root()
4114- self.assertEqual(root, [
4115- ('root', {}, [
4116- 'foo', 'bar',
4117- ('dus', {}, ['ja']),
4118- 'foo', 'bar',
4119- ('br', {}, []),
4120- 'foo', 'bar',
4121- ]
4122- )
4123- ])
4124-
4125-
4126- builder = SimpleTreeBuilder()
4127-
4128- builder.start('root', {})
4129- builder.text('foo')
4130- builder.text('bar')
4131- builder.append('dus', {}, 'ja')
4132- builder.text('foo')
4133- builder.text('bar')
4134- builder.append('br', {})
4135- builder.text('foo')
4136- builder.text('bar')
4137- builder.end('root')
4138-
4139- root = builder.get_root()
4140- self.assertEqual(root, [
4141- ('root', {}, [
4142- 'foobar',
4143- ('dus', {}, ['ja']),
4144- 'foobar',
4145- ('br', {}, []),
4146- 'foobar',
4147- ]
4148- )
4149- ])
4150+ E = SimpleTreeElement
4151+
4152+ builder = SimpleTreeBuilder()
4153+
4154+ builder.start('root', {})
4155+ builder.text('foo')
4156+ builder.text('bar')
4157+ builder.append('dus', {}, 'ja')
4158+ builder.text('foo')
4159+ builder.text('bar')
4160+ builder.append('br', {})
4161+ builder.text('foo')
4162+ builder.text('bar')
4163+ builder.end('root')
4164+
4165+ root = builder.get_root()
4166+ self.assertEqual(root, [
4167+ E('root', {}, [
4168+ 'foo', 'bar',
4169+ E('dus', {}, ['ja']),
4170+ 'foo', 'bar',
4171+ E('br', {}, []),
4172+ 'foo', 'bar',
4173+ ]
4174+ )
4175+ ])
4176+
4177+
4178+ realbuilder = SimpleTreeBuilder()
4179+ builder = BuilderTextBuffer(realbuilder)
4180+
4181+ builder.start('root', {})
4182+ builder.text('foo')
4183+ builder.text('bar')
4184+ builder.append('dus', {}, 'ja')
4185+ builder.text('foo')
4186+ builder.text('bar')
4187+ builder.append('br', {})
4188+ builder.text('foo')
4189+ builder.text('bar')
4190+ builder.end('root')
4191+
4192+ root = realbuilder.get_root()
4193+ self.assertEqual(root, [
4194+ E('root', {}, [
4195+ 'foobar',
4196+ E('dus', {}, ['ja']),
4197+ 'foobar',
4198+ E('br', {}, []),
4199+ 'foobar',
4200+ ]
4201+ )
4202+ ])
4203+
4204+
4205+
4206+class TestBuilderTextBuffer(tests.TestCase):
4207+
4208+ def runTest(self):
4209+ builder = SimpleTreeBuilder()
4210+ buffer = BuilderTextBuffer(builder)
4211+
4212+ buffer.start('FOO')
4213+ buffer.text('aaa\n')
4214+ buffer.text('bbb\n')
4215+ buffer.text('ccc\n')
4216+ self.assertEqual(buffer.get_text(), 'aaa\nbbb\nccc\n')
4217+
4218+ buffer.append('BAR')
4219+ self.assertEqual(buffer.get_text(), '')
4220+
4221+ buffer.text('qqq\n')
4222+ self.assertEqual(buffer.get_text(), 'qqq\n')
4223+ buffer.clear_text()
4224+
4225+ buffer.text('qqq\n')
4226+ self.assertEqual(buffer.get_text(), 'qqq\n')
4227+ buffer.set_text('ddd\n')
4228+ self.assertEqual(buffer.get_text(), 'ddd\n')
4229+
4230+ buffer.text('')
4231+ buffer.text('eee')
4232+ buffer.end('FOO')
4233+
4234+ E = SimpleTreeElement
4235+ self.assertEqual(builder.get_root(), [
4236+ E('FOO', None, [
4237+ u'aaa\nbbb\nccc\n',
4238+ E('BAR', None, []),
4239+ u'ddd\neee',
4240+ ])
4241+ ])
4242+
4243
4244
4245 class TestParser(tests.TestCase):
4246
4247=== modified file 'tests/tags.py'
4248--- tests/tags.py 2014-01-16 20:30:53 +0000
4249+++ tests/tags.py 2014-08-19 17:10:33 +0000
4250@@ -161,7 +161,7 @@
4251 self.assertTrue(path in treestore.get_treepaths(indexpath))
4252
4253 page = self.notebook.get_page(indexpath)
4254- self.assertEqual(treestore.get_value(iter, NAME_COL), page.basename)
4255+ self.assertIn(treestore.get_value(iter, NAME_COL), (page.basename, page.name))
4256 self.assertEqual(treestore.get_value(iter, PATH_COL), page)
4257 if page.hascontent or page.haschildren:
4258 self.assertEqual(treestore.get_value(iter, EMPTY_COL), False)
4259
4260=== modified file 'tests/templates.py'
4261--- tests/templates.py 2013-12-27 12:17:17 +0000
4262+++ tests/templates.py 2014-08-19 17:10:33 +0000
4263@@ -1,295 +1,702 @@
4264 # -*- coding: utf-8 -*-
4265
4266-# Copyright 2008 Jaap Karssenberg <jaap.karssenberg@gmail.com>
4267+# Copyright 2008-2014 Jaap Karssenberg <jaap.karssenberg@gmail.com>
4268
4269 '''Test cases for the zim.templates module.'''
4270
4271+from __future__ import with_statement
4272+
4273 import tests
4274
4275-import os
4276
4277-import zim
4278-from zim.errors import Error
4279 from zim.templates import *
4280-from zim.templates import GenericTemplate, \
4281- TemplateParam, TemplateDict, TemplateFunction, PageProxy
4282-from zim.notebook import Notebook, Path
4283-import zim.formats
4284-from zim.parsing import link_type
4285-
4286-
4287-class TestTemplateParam(tests.TestCase):
4288-
4289- def runTest(self):
4290- param = TemplateParam('xxx.yyy.zzz')
4291- self.assertEquals(param.path, ['xxx', 'yyy'])
4292- self.assertEquals(param.key, 'zzz')
4293-
4294- self.assertRaises(Error, TemplateParam, '_settings.foo')
4295- self.assertRaises(Error, TemplateParam, 'xxx._yyy.zzz')
4296- self.assertRaises(Error, TemplateParam, 'xxx.y-y.zzz')
4297-
4298-
4299-class TestTemplateDict(tests.TestCase):
4300-
4301- def runTest(self):
4302- data = {'foo': {'bar': {'baz': '123'}}, 'xyz':'check'}
4303- dict = TemplateDict(data)
4304-
4305- param = TemplateParam('foo.bar.baz')
4306- self.assertEquals(dict[param], '123')
4307- dict[param] = 'FOO'
4308- self.assertEquals(dict[param], 'FOO')
4309- self.assertEquals(data['foo']['bar']['baz'], '123')
4310-
4311-
4312-class TestGenericTemplate(tests.TestCase):
4313-
4314-# def setUp(self):
4315-# self.template = ...
4316-
4317- def testSyntax(self):
4318- '''Test Template processing simple statements without page'''
4319- input = '''
4320-[%- SET test = "foo" -%]
4321-[%- SET true = "true" -%]
4322-[%- SET false = "" -%]
4323----
4324-<b>[% test %]</b>
4325-<i>[% some_none_existing_parameter %]</i>
4326-<u>[% upper('foo') %]</u>
4327----
4328-[% IF true %]OK[% ELSE %]NOK[% END %]
4329-[% IF false -%]
4330-OK
4331-[%- ELSE -%]
4332-NOK
4333-[%- END %]
4334----
4335-[% FOREACH name = [ 'foo', 'bar', 'baz' ] -%]
4336- NAME = [% GET name %]
4337-[% END -%]
4338----
4339-[% numbers = ['1', '2', '3'] -%]
4340-[% FOREACH n IN numbers %][% n %]...[% END %]
4341----
4342-'''
4343-
4344- wantedresult = u'''\
4345----
4346-<b>foo</b>
4347-<i></i>
4348-<u>FOO</u>
4349----
4350-OK
4351-NOK
4352----
4353- NAME = foo
4354- NAME = bar
4355- NAME = baz
4356----
4357-1...2...3...
4358----
4359-'''
4360- tmpl = GenericTemplate(input)
4361- #~ import pprint
4362- #~ pprint.pprint( tmpl.tokens )
4363- dict = { 'upper': TemplateFunction(lambda d, *a: a[0].upper()) }
4364- result = tmpl.process(dict)
4365- #~ print test.getvalue()
4366- self.assertEqual(result, wantedresult.splitlines(True))
4367-
4368- def testRaise(self):
4369- '''Test Template invalid syntax raises TemplateError'''
4370- input = 'foo[% ELSE %]bar'
4371- self.assertRaises(TemplateSyntaxError, GenericTemplate, input)
4372-
4373- input = 'foo[% FOREACH foo = ("1", "2", "3") %]bar'
4374- self.assertRaises(TemplateSyntaxError, GenericTemplate, input)
4375-
4376- input = 'foo[% `echo /etc/passwd` %]bar'
4377- self.assertRaises(TemplateSyntaxError, GenericTemplate, input)
4378-
4379- input = 'foo[% duss("ja") %]bar'
4380- templ = GenericTemplate(input)
4381- self.assertRaises(TemplateProcessError, templ.process, {})
4382-
4383-
4384-class TestTemplateSet(tests.TestCase):
4385-
4386- def runTest(self):
4387- '''Load all shipped templates for syntax check'''
4388- for dir, dirs, files in os.walk('./data/templates'):
4389- format = os.path.basename(dir)
4390- if format == 'templates':
4391- continue # skip top level dir
4392- files = [f for f in files if not f.startswith('.') and not '~' in f]
4393- files.sort()
4394- self.assertTrue(len(files) > 0)
4395- templates = list_templates(format)
4396- for file in files:
4397- self.assertIn(file, [t[1] for t in templates])
4398-
4399- file = os.path.join(dir, file)
4400- input = open(file).readlines()
4401- if format == 'plugins':
4402- tmpl = GenericTemplate(input)
4403- else:
4404- tmpl = Template(input, format)
4405- # Syntax errors will be raised during init
4406- # TODO parameter check for these templates
4407- # ... run them with raise instead of param = None
4408-
4409-
4410-class TestPageProxy(tests.TestCase):
4411-
4412- def runTest(self):
4413- notebook = tests.new_notebook()
4414- page = notebook.get_page(Path('FooBar'))
4415-
4416- page.parse('wiki', '''\
4417-====== Page Heading ======
4418-**foo bar !**
4419-''')
4420- self.assertTrue(len(page.dump('html', linker=StubLinker())) > 0)
4421- proxy = PageProxy(Notebook(), page, zim.formats.get_format('html'), StubLinker(), {})
4422- self.assertEqual(proxy.name, page.name)
4423- self.assertEqual(proxy.namespace, page.namespace)
4424- self.assertEqual(proxy.basename, page.basename)
4425- self.assertTrue(isinstance(proxy.properties, dict))
4426- self.assertTrue(len(proxy.body) > 0)
4427- # TODO add othermethods
4428+
4429+from zim.templates.parser import *
4430+from zim.templates.expression import *
4431+from zim.templates.expressionparser import *
4432+
4433+from zim.templates.processor import *
4434+
4435+from zim.parser import SimpleTreeElement, SimpleTreeBuilder, BuilderTextBuffer
4436+
4437+E = SimpleTreeElement
4438+
4439+
4440+class TestExpressionParser(tests.TestCase):
4441+
4442+ def runTest(self):
4443+ ## Test atoms
4444+ p = ExpressionParser()
4445+ for text, wanted in (
4446+ ('True', ExpressionLiteral(True)),
4447+ ('False', ExpressionLiteral(False)),
4448+ ('None', ExpressionLiteral(None)),
4449+ ('"foo\\tbar"', ExpressionLiteral("foo\tbar")),
4450+ ('123', ExpressionLiteral(123)),
4451+ ('1.2', ExpressionLiteral(1.2)),
4452+ ('1E+3', ExpressionLiteral(1E+3)),
4453+ ('x', ExpressionParameter('x')),
4454+ ('foo.bar', ExpressionParameter('foo.bar')),
4455+ ):
4456+ self.assertEqual(p.parse(text), wanted)
4457+
4458+ ## Test compound expressions
4459+ p = ExpressionParser()
4460+ for text, wanted in (
4461+ ('x or y', ExpressionOperator(
4462+ operator.or_,
4463+ ExpressionParameter('x'),
4464+ ExpressionParameter('y')
4465+ )),
4466+ ('x == y', ExpressionOperator(
4467+ operator.eq,
4468+ ExpressionParameter('x'),
4469+ ExpressionParameter('y')
4470+ )),
4471+ ('not x', ExpressionUnaryOperator(
4472+ operator.not_,
4473+ ExpressionParameter('x')
4474+ )),
4475+ ('[1, a, True]', ExpressionList([
4476+ ExpressionLiteral(1),
4477+ ExpressionParameter('a'),
4478+ ExpressionLiteral(True),
4479+ ])),
4480+ ('[[1, a], [True, False]]', ExpressionList([
4481+ ExpressionList([
4482+ ExpressionLiteral(1),
4483+ ExpressionParameter('a'),
4484+ ]),
4485+ ExpressionList([
4486+ ExpressionLiteral(True),
4487+ ExpressionLiteral(False),
4488+ ])
4489+ ])),
4490+ ('func(1, a)', ExpressionFunctionCall(
4491+ ExpressionParameter('func'),
4492+ ExpressionList([
4493+ ExpressionLiteral(1),
4494+ ExpressionParameter('a'),
4495+ ])
4496+ )),
4497+ ('func([1, a])', ExpressionFunctionCall(
4498+ ExpressionParameter('func'),
4499+ ExpressionList([
4500+ ExpressionList([
4501+ ExpressionLiteral(1),
4502+ ExpressionParameter('a'),
4503+ ])
4504+ ])
4505+ )),
4506+ ('func(1, func(a))', ExpressionFunctionCall(
4507+ ExpressionParameter('func'),
4508+ ExpressionList([
4509+ ExpressionLiteral(1),
4510+ ExpressionFunctionCall(
4511+ ExpressionParameter('func'),
4512+ ExpressionList([
4513+ ExpressionParameter('a'),
4514+ ])
4515+ )
4516+ ])
4517+ )),
4518+ ('[func(1, a), x == y]', ExpressionList([
4519+ ExpressionFunctionCall(
4520+ ExpressionParameter('func'),
4521+ ExpressionList([
4522+ ExpressionLiteral(1),
4523+ ExpressionParameter('a'),
4524+ ])
4525+ ),
4526+ ExpressionOperator(
4527+ operator.eq,
4528+ ExpressionParameter('x'),
4529+ ExpressionParameter('y')
4530+ )
4531+ ])),
4532+ ):
4533+ self.assertEqual(p.parse(text), wanted)
4534+
4535+
4536+ ## Test operator precedence
4537+ expr = ExpressionParser().parse('a or b and not c < d and f or x')
4538+ # Read as: '(a or ((b and ((not (c < d)) and f)) or x))'
4539+ wanted = ExpressionOperator(
4540+ operator.or_,
4541+ ExpressionParameter('a'),
4542+ ExpressionOperator(
4543+ operator.or_,
4544+ ExpressionOperator(
4545+ operator.and_,
4546+ ExpressionParameter('b'),
4547+ ExpressionOperator(
4548+ operator.and_,
4549+ ExpressionUnaryOperator(
4550+ operator.not_,
4551+ ExpressionOperator(
4552+ operator.lt,
4553+ ExpressionParameter('c'),
4554+ ExpressionParameter('d')
4555+ )
4556+ ),
4557+ ExpressionParameter('f')
4558+ )
4559+ ),
4560+ ExpressionParameter('x')
4561+ )
4562+ )
4563+ #~ print '\nEXPRESSION:', expr
4564+ self.assertEqual(expr, wanted)
4565+
4566+ ## Invalid syntaxes
4567+ p = ExpressionParser()
4568+ for t in (
4569+ 'x > y > z', # chaining comparison operators not allowed
4570+ 'x > not y', # 'not' has higher precendence, can not appear here
4571+ 'not not x', # double operator
4572+ 'x and and y', # double operator
4573+ '[x,,y]', # double "," - missing element
4574+ '(1,2)', # Tuple not supported
4575+ '1 2', # Two expressions, instead of one
4576+ '1, 2', # Two expressions, instead of one
4577+ '1.2.3', # Invalid literal
4578+ '<>', # just an operator
4579+ '', # empty expression has no meaning
4580+ ):
4581+ self.assertRaises(ExpressionSyntaxError, p.parse, t)
4582+ # TODO check for meaningfull error messages for these
4583+
4584+ # TODO any edge cases ?
4585+
4586+
4587+class TestExpression(tests.TestCase):
4588+
4589+ def runTest(self):
4590+ expr = ExpressionList([
4591+ ExpressionLiteral('foooo'),
4592+ ExpressionParameter('foo'),
4593+ ExpressionParameter('a.b'),
4594+ ExpressionOperator(
4595+ operator.le,
4596+ ExpressionParameter('n'),
4597+ ExpressionLiteral(2)
4598+ ),
4599+ ExpressionFunctionCall(
4600+ ExpressionParameter('addone'),
4601+ ExpressionList([
4602+ ExpressionParameter('n')
4603+ ])
4604+ ),
4605+ ])
4606+
4607+ result = expr( {
4608+ 'foo': 'FOO',
4609+ 'a': {
4610+ 'b': 'BAR'
4611+ },
4612+ 'n': 1,
4613+ 'addone': ExpressionFunction(lambda a: a+1)
4614+ } )
4615+
4616+ wanted = ['foooo', 'FOO', 'BAR', True, 2]
4617+ self.assertEqual(result, wanted)
4618+
4619+
4620+class TestExpressionFunctionCall(tests.TestCase):
4621+
4622+ def runTest(self):
4623+ class Foo(object):
4624+
4625+ def __init__(self, prefix):
4626+ self.prefix = prefix
4627+
4628+ @ExpressionFunction
4629+ def string(self, string):
4630+ return self.prefix + string
4631+
4632+ # Test ExpressionFunction works as decorator (bound method)
4633+ foo = Foo('FOO')
4634+ self.assertIsInstance(foo.string, ExpressionFunction)
4635+ self.assertEqual(foo.string('bar'), 'FOObar')
4636+
4637+ # Test get builtin from dict
4638+ mydict = {
4639+ 'len': ExpressionFunction(lambda o: len(o)),
4640+ 'mylist': ['a', 'b', 'c'],
4641+ }
4642+ args = ExpressionList([ExpressionParameter('mylist')])
4643+ var = ExpressionParameter('len')
4644+ func = ExpressionFunctionCall(var, args)
4645+ self.assertEqual(func(mydict), 3)
4646+
4647+ # Test get object method from attr
4648+ mydict = {'foo': foo}
4649+ args = ExpressionList([ExpressionLiteral('BAR')])
4650+ var = ExpressionParameter('foo.string')
4651+ func = ExpressionFunctionCall(var, args)
4652+ self.assertEqual(func(mydict), 'FOOBAR')
4653+
4654+ # Test implicit types
4655+ mydict = {
4656+ 'somedict': {'a': 'AAA', 'b': 'BBB', 'c': 'CCC'},
4657+ 'somelist': ['x', 'y', 'z'],
4658+ 'somestring': 'FOOBAR',
4659+ }
4660+ args = ExpressionList() # empty args
4661+ for name, wanted in (
4662+ ('somedict.sorted', ['a', 'b', 'c']),
4663+ ('somelist.len', 3),
4664+ ('somestring.lower', 'foobar'),
4665+ ('somedict.b.lower', 'bbb'),
4666+ ('somelist.1.upper', 'Y'),
4667+ ):
4668+ var = ExpressionParameter(name)
4669+ func = ExpressionFunctionCall(var, args)
4670+ self.assertEqual(func(mydict), wanted)
4671+
4672+
4673+class TestExpressionObjects(tests.TestCase):
4674+
4675+ def runTest(self):
4676+ # Test proper object type for attributes
4677+ for obj in (
4678+ ExpressionStringObject('foo'),
4679+ ExpressionDictObject({'foo': 'bar'}),
4680+ ExpressionListObject(['a', 'b', 'c']),
4681+ ):
4682+ for name in obj._fmethods:
4683+ self.assertTrue(hasattr(obj, name))
4684+ function = getattr(obj, name)
4685+ self.assertIsInstance(function, ExpressionFunction)
4686+
4687+ # Test getitem, iter, len, str
4688+ # and one or two functions of each type
4689+ data = {'a': 'b', 'c': 'd', 'e': 'f'}
4690+ mydict = ExpressionDictObject(data)
4691+ self.assertEqual(mydict['c'], data['c'])
4692+ self.assertEqual(list(mydict), list(data))
4693+ self.assertEqual(len(mydict), len(data))
4694+ self.assertEqual(str(mydict), str(data))
4695+ self.assertEqual(mydict.get('c'), data.get('c'))
4696+
4697+ mylist = ExpressionListObject(['a', 'b', 'c'])
4698+ self.assertEqual(mylist[1], 'b')
4699+ self.assertEqual(mylist.get(1), 'b')
4700+ self.assertIsNone(mylist.get(5))
4701+
4702+ mystring = ExpressionStringObject('foo')
4703+ self.assertEqual(mystring.upper(), "FOO")
4704+
4705+
4706+
4707+
4708+class TestTemplateBuilderTextBuffer(tests.TestCase):
4709+
4710+ def runTest(self):
4711+ builder = SimpleTreeBuilder()
4712+ buffer = TemplateBuilderTextBuffer(builder)
4713+
4714+ buffer.start('FOO')
4715+ buffer.text('foo\n\t\t')
4716+
4717+ buffer.rstrip()
4718+ buffer.append('BAR')
4719+ buffer.lstrip()
4720+
4721+ buffer.text(' \n\n\t\tdus\n\n')
4722+
4723+ buffer.rstrip()
4724+ buffer.append('BAR')
4725+ buffer.lstrip()
4726+ buffer.text('\n')
4727+
4728+ buffer.end('FOO')
4729+ result = builder.get_root()
4730+ #~ print result
4731+
4732+ self.assertEqual(result, [
4733+ E('FOO', None, [
4734+ u'foo',
4735+ E('BAR', None, []),
4736+ u'\n\t\tdus\n',
4737+ E('BAR', None, []),
4738+ ])
4739+ ])
4740+
4741+
4742+class TestTemplateParser(tests.TestCase):
4743+
4744+ # Include all elements recognized by parser and various forms
4745+ # of whitespace stripping, no need to excersize all expressions
4746+ # - ExpressionParser is tested separately
4747+
4748+ TEMPLATE = '''\
4749+[% foo %]
4750+[% GET foo %]
4751+
4752+[% bar = "test" %]
4753+[% SET bar = "test" %]
4754+
4755+ <!--[% IF foo %]-->
4756+ DO SOMETHING
4757+ <!--[% ELIF foo -%]-->
4758+ SOMETHING ELSE
4759+ [%- ELSE %]
4760+ YET SOMETHING ELSE
4761+ [% END %]
4762+
4763+Switch: [% IF foo %]AAA[% ELSE %]BBB[% END %]
4764+
4765+ [% BLOCK bar -%]
4766+ BAR
4767+[% END %]
4768+
4769+[% FOR a IN b %]
4770+ AAA
4771+[% END %]
4772+
4773+[% FOREACH a IN b %]
4774+ AAA
4775+[% END %]
4776+
4777+[% FOREACH a = b %]
4778+ AAA
4779+[% END %]
4780+
4781+<!--[% BLOCK foo %]-->
4782+ FOO
4783+<!--[% END %]-->
4784+'''
4785+
4786+ WANTED = [
4787+ E('TEMPLATE', None, [
4788+ E('GET', {'expr': ExpressionParameter('foo')}, []),
4789+ '\n', # whitespace around GET remains intact
4790+ E('GET', {'expr': ExpressionParameter('foo')}, []),
4791+ '\n',
4792+ E('SET', {
4793+ 'var': ExpressionParameter('bar'),
4794+ 'expr': ExpressionLiteral('test')
4795+ }, []), # no whitespace here - SET chomps
4796+ E('SET', {
4797+ 'var': ExpressionParameter('bar'),
4798+ 'expr': ExpressionLiteral('test')
4799+ }, []),
4800+ '\n', # only one "\n" here!
4801+ # no indenting before block level items like IF
4802+ E('IF', {'expr': ExpressionParameter('foo')}, [
4803+ '\tDO SOMETHING\n' # indenting intact
4804+ ]),
4805+ E('ELIF', {'expr': ExpressionParameter('foo')}, [
4806+ 'SOMETHING ELSE' # stripped on both sides
4807+ ]),
4808+ E('ELSE', None, [
4809+ '\tYET SOMETHING ELSE\n' # indenting intact
4810+ ]),
4811+ '\nSwitch:\t',
4812+ E('IF', {'expr': ExpressionParameter('foo')}, [
4813+ 'AAA'
4814+ ]),
4815+ E('ELSE', None, [
4816+ 'BBB'
4817+ ]),
4818+ '\n\n', # two "\n" here because IF .. ELSE is inline
4819+ '\n', # another empty line after block is taken out
4820+ # 3 times same loop by different syntax
4821+ E('FOR', {
4822+ 'var': ExpressionParameter('a'),
4823+ 'expr': ExpressionParameter('b'),
4824+ }, [
4825+ '\tAAA\n'
4826+ ]),
4827+ '\n',
4828+ E('FOR', {
4829+ 'var': ExpressionParameter('a'),
4830+ 'expr': ExpressionParameter('b'),
4831+ }, [
4832+ '\tAAA\n'
4833+ ]),
4834+ '\n',
4835+ E('FOR', {
4836+ 'var': ExpressionParameter('a'),
4837+ 'expr': ExpressionParameter('b'),
4838+ }, [
4839+ '\tAAA\n'
4840+ ]),
4841+ '\n',
4842+ ]),
4843+ E('BLOCK', {'name': 'bar'}, ['BAR\n']),
4844+ # indenting before "[% BLOCK .." and before "BAR" both gone
4845+ E('BLOCK', {'name': 'foo'}, ['\tFOO\n']),
4846+ # indenting intact
4847+ ]
4848+
4849+ def runTest(self):
4850+ parser = TemplateParser()
4851+
4852+ root = parser.parse(self.TEMPLATE)
4853+ #~ print root
4854+ self.assertEqual(root, self.WANTED)
4855+
4856+ # TODO Test exceptions
4857+ # - invalid expression
4858+ # - lower case keyword
4859+ # - invalide sequence IF / ELSE
4860+
4861+
4862+class TestTemplateContextDict(tests.TestCase):
4863+
4864+ def runTest(self):
4865+ data = {'a': 'AAA', 'b': 'BBB', 'c': 'CCC'}
4866+ context = TemplateContextDict(data)
4867+ for name in context._fmethods:
4868+ func = getattr(context, name)
4869+ self.assertIsInstance(func, ExpressionFunction)
4870+
4871+ # make sure we can use as regular dict
4872+ context['d'] = 'DDD'
4873+ self.assertEqual(context.pop('d'), 'DDD')
4874+
4875+
4876+class TestTemplateLoopState(tests.TestCase):
4877+
4878+ def runTest(self):
4879+ items = ['aaa', 'bbb', 'ccc']
4880+
4881+ loop = TemplateLoopState(len(items), None)
4882+
4883+ myiter = MovingWindowIter(items)
4884+ for i, stateitems in enumerate(myiter):
4885+ loop._update(i, myiter)
4886+
4887+ self.assertEqual(loop.size, 3)
4888+ self.assertEqual(loop.max, 2)
4889+ self.assertEqual(loop.prev, None if i == 0 else items[i-1])
4890+ self.assertEqual(loop.current, items[i])
4891+ self.assertEqual(loop.next, None if i == 2 else items[i+1])
4892+ self.assertEqual(loop.index, i)
4893+ self.assertEqual(loop.count, i+1)
4894+ self.assertEqual(loop.first, True if i == 0 else False)
4895+ self.assertEqual(loop.last, True if i == 2 else False)
4896+ self.assertEqual(loop.parity, 'odd' if i % 2 else 'even')
4897+ self.assertEqual(loop.even, False if i % 2 else True)
4898+ self.assertEqual(loop.odd, True if i % 2 else False)
4899+
4900+ self.assertEqual(i, 2)
4901+
4902+
4903+class TestTemplateProcessor(tests.TestCase):
4904+
4905+ def testGetSet(self):
4906+ # test 'GET', 'SET'
4907+ processor = TemplateProcessor([
4908+ E('TEMPLATE', None, [
4909+ E('SET', {
4910+ 'var': ExpressionParameter('aaa.bbb'),
4911+ 'expr': ExpressionLiteral('foo')
4912+ }),
4913+ E('GET', {'expr': ExpressionParameter('aaa.bbb')}),
4914+ ])
4915+ ])
4916+
4917+ output = []
4918+ context = TemplateContextDict({'aaa': TemplateContextDict({})})
4919+ processor.process(output, context)
4920+ self.assertEqual(output, ['foo'])
4921+
4922+ output = []
4923+ context = TemplateContextDict({'aaa': {}})
4924+ with self.assertRaises(AssertionError):
4925+ processor.process(output, context)
4926+
4927+
4928+ def testIfElifElse(self):
4929+ # test 'IF', 'ELIF', 'ELSE',
4930+ processor = TemplateProcessor([
4931+ E('TEMPLATE', None, [
4932+ E('IF', {'expr': ExpressionParameter('a')}, ['A']),
4933+ E('ELIF', {'expr': ExpressionParameter('b')}, ['B']),
4934+ E('ELIF', {'expr': ExpressionParameter('c')}, ['C']),
4935+ E('ELSE', {}, ['D']),
4936+ ])
4937+ ])
4938+
4939+ for context, wanted in (
4940+ ({'a': True}, ['A']),
4941+ ({'a': False, 'b': True}, ['B']),
4942+ ({'a': False, 'b': False, 'c': True}, ['C']),
4943+ ({'a': False, 'b': False, 'c': False}, ['D']),
4944+ ):
4945+ lines = []
4946+ processor.process(lines, TemplateContextDict(context))
4947+ self.assertEqual(lines, wanted)
4948+
4949+ def testFor(self):
4950+ # test 'FOR'
4951+ processor = TemplateProcessor([
4952+ E('TEMPLATE', None, [
4953+ E('FOR', {
4954+ 'var': ExpressionParameter('iter'),
4955+ 'expr': ExpressionParameter('items'),
4956+ }, [
4957+ E('GET', {'expr': ExpressionParameter('loop.count')}),
4958+ ': ',
4959+ E('GET', {'expr': ExpressionParameter('iter')}),
4960+ '\n',
4961+ ])
4962+ ])
4963+ ])
4964+
4965+ context = {'items': ['aaa', 'bbb', 'ccc']}
4966+
4967+ lines = []
4968+ processor.process(lines, TemplateContextDict(context))
4969+ self.assertEqual(''.join(lines), '1: aaa\n2: bbb\n3: ccc\n')
4970+
4971+ def testInclude(self):
4972+ # test 'INCLUDE',
4973+ processor = TemplateProcessor([
4974+ E('TEMPLATE', None, [
4975+ E('INCLUDE', {'expr': ExpressionParameter('foo')}),
4976+ E('INCLUDE', {'expr': ExpressionParameter('foo')}),
4977+ E('INCLUDE', {'expr': ExpressionParameter('foo')}),
4978+ ]),
4979+ E('BLOCK', {'name': 'foo'}, 'FOO\n'),
4980+ ])
4981+
4982+ lines = []
4983+ processor.process(lines, TemplateContextDict({'foo': 'foo'}))
4984+ self.assertEqual(''.join(lines), 'FOO\nFOO\nFOO\n')
4985+
4986+
4987+
4988+class TestTemplateList(tests.TestCase):
4989+
4990+ def runTest(self):
4991+ categories = list_template_categories()
4992+ self.assertIn('html', categories)
4993+ self.assertIn('wiki', categories)
4994+
4995+ for cat in categories:
4996+ templates = list_templates(cat)
4997+ #~ print '>>', cat, templates
4998+ self.assertGreater(len(templates), 0)
4999+ for name, filename in templates:
5000+ template = get_template(cat, name)
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches