File: extension.dox

package info (click to toggle)
grantlee5 5.1.0-2.1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 4,152 kB
  • sloc: cpp: 24,299; python: 413; sh: 108; ruby: 24; makefile: 20
file content (282 lines) | stat: -rw-r--r-- 11,465 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

namespace Grantlee
{

/**

  @page extension Extending the template system

  @section libraries Filter and tag Libraries

  As already noted, it is possible for application developers to create their own tags and filters. This feature is based on the QtPlugin system so that plugins can be loaded at run time.

  @note If you are already familiar with Django, you will know that this is not necessary on that system. That is because Django libraries are just python modules, which can behave like dynamically loaded plugins.

  @subsection filters Filters

  A filter takes an object and an optional argument and returns a string. To create your own filter, create a concrete subclass of Grantlee::Filter and implement the doFilter method.

  @code
    /// Outputs its input string twice.
    class TwiceFilter : public Grantlee::Filter
    {
      /* reimp */ QVariant dofilter(const QVariant &input, const QVariant &arg = QVariant(), bool autoescape = false) const
      {
        SafeString str = getSafeString(input);

        return str + str;
      }

      /* reimp */ bool isSafe() const { return true; } // see the Autoescaping section
    };

    ...

    Seeing double {{ name|twice }}?

    // Renders: Seeing double MikeMike?
  @endcode

  The argument to doFilter is a QVariant, so it may contain any of the types supported by %Grantlee.

  @code
    /// Outputs its input n times.
    class RepeatFilter : public Grantlee::Filter
    {
      QVariant dofilter(const QVariant &input, const QVariant &arg = QVariant(), bool autoescape = false) const
      {
        SafeString str = getSafeString(input);

        if ( arg.type() != QMetaType::Int )
          return str; // Fail gracefully.

        int times = arg.toInt();
        for (int i = 0; i < times ++i)
        {
          str.get().append(str);
        }
        return str;
      }

      bool isSafe() const { return true; }
    };

    ...

    Seeing more {{ name|repeat:"3" }}?

    // Renders: Seeing more NathalieNathalieNathalie?

    Seeing more {{ name|repeat:"four" }}?

    // Renders: Seeing more Otto? (failing gracefully)
  @endcode

  Note that the filter does not fail or throw an exception if the integer conversion fails. Filters should handle all errors gracefully. If an error occurs, return either the input, or an empty string. Whichever is more appropriate.

  @section autoescaping Autoescaping and safe-ness

  When implementing filters, it is necessary to consider whether string output from the template should be escaped by %Grantlee when rendering the template. %Grantlee features an autoescaping feature which ensures that a string which should only be escaped once is not escaped two or more times.

  @see @ref templates_safestring

  The filter interface contains two elements relevant to autoescaping. The first is the autoescape parameter to the Filter::doFilter method. The autoescape parameter indicates the current autoescaping state of the renderer, which can be manipulated in templates with the @gr_tag{autoescape} tag. Use of the autoescape parameter is rare. The second element of autoescaping in the Filter API is the Filter::isSafe method. This method can be reimplemented to indicate that a Filter is 'safe', that is given safe input, it produces safe output.

  @see http://docs.djangoproject.com/en/dev/howto/custom-template-tags/#filters-and-auto-escaping
  @see http://groups.google.com/group/django-users/browse_thread/thread/311f336d74e7b643

  @subsection tags Tags

  A tag can really do anything with a template. To create your own tag, create a concrete subclass of Grantlee::NodeFactory and implement the getNode method, and create a concrete subclass of Grantlee::Node and implement the render method.

  @note If you are familiar with Django you will recognise that defining a tag in Django involves creating a Node subclass (like %Grantlee), and a factory function where %Grantlee requires a factory class. This is because functions in python are objects, just like classes are, and dynamic typing allows easy creation of lists of those factory functions. In %Grantlee with statically-typed C++, we need to group the factories by interface (i.e, the Grantlee::AbstractNodeFactory interface).

  Tags can take arguments, advance the parser, create nodes, and generally have broad control over the parsing and rendering stages.

  Here is an example of a @gr_tag{current_time} tag which displays the current time.

  @code
    class CurrentTimeTag : public Grantlee::AbstractNodeFactory
    {
      Grantlee::Node *getNode(const QString &tagContent, Parser *p) const
      {
        // You almost always want to use smartSplit.
        QStringList parts = smartSplit(tagContent);
        parts.removeFirst(); // Not interested in the name of the tag.

        if (!parts.isEmpty())
          // The remaining parts are the arguments to the tag. If an incorrect number of arguments
          // is supplied, and exception should be thrown.
          throw Grantlee::Exception( TagSyntaxError, "current_time does not take any arguments" );

        return new CurrentTimeNode();
      }
    };

    class CurrentTimeNode : public Grantlee::Node
    {
      Q_OBJECT
    public:
      CurrentTimeNode(QObject *parent)
        : QObject(parent)
      {

      }

      void render( Grantlee::OutputStream *stream, Context *c) const
      {
        Q_UNUSED(c);
        return QDateTime::currentDateTime().toString();
      }
    };
  @endcode

  @see AbstractNodeFactory::smartSplit

  @note The current_time tag could be extended to format the time in a particular way. That is the behaviour of the @gr_tag{now} tag. See its documentation and implementation for details.

  Also, note that, nodeFactory may throw an execption at template compilation time, but like Filters, Nodes should handle errors gracefully in their render method, and should return an empty QString in most error cases.

  @subsection tags_with_end_tags Tags with end tags

  Often, tags will be not just one token in a template, but a start and end token such as range, spaceless, with, or a start, middle and end tokens, such as if and for.

  When constructing a Node, a NodeFactory can instruct the Parser to parse until any appropriate Token.

  @code
    text content
    {% if foo %}
      foo content
    {% else %}
      default content
    {% endif %}
    end content
  @endcode

  To implement such a tag the implementation of AbstractNodeFactory::getNode needs to parse until the optional intermediate tags and until the mandatory end tag, collecting the child nodes as it does so.

  @code
    Node* IfNodeFactory::getNode( const QString &tagContent, Parser *p ) const
    {
      QStringList parts = smartSplit( tagContent );
      parts.removeFirst(); // Remove "if"
      // Error handling etc.
      QList<FilterExpression> argsList = getFilterExpressionList( parts );
      IfNode *node = new IfNode( argsList, p );

      // Parse until an else or endif token
      NodeList trueList = p->parse( node, QStringList() << "else" << "endif" );
      node->setTrueList( trueList );
      NodeList falseList;

      Token nextToken = p->takeNextToken();
      if ( nextToken.content == "else" )
      {
        falseList = p->parse( node, "endif" );
        node->setFalseList( falseList );
        // skip past the endif tag
        p->removeNextToken();
      } // else empty falseList.

      return node;
    }
  @endcode

  There is no limit to the number of intermediate tokens you can use in your tags. For example, a better @gr_tag{if} tag might support multiple elif tags.

  @code
    text content
    {% if foo %}
      foo content
    {% elif bar %}
      bar content
    {% elif bat %}
      bat content
    {% else %}
      default content
    {% endif %}
    end content
  @endcode

  @section cpp_libraries C++ Libraries

  As already mentioned, it is neccessary to create a QtPlugin library to make your tags and filters available to %Grantlee. You need to implement TagLibraryInterface to return your custom node factories and filters. See the existing libraries in your %Grantlee distribution for full examples.

  @code
    #include <grantlee/taglibraryinterface.h>

    #include "mytag.h"
    #include "myfilter.h"

    class MyLibrary : public QObject, public Grantlee::TagLibraryInterface
    {
      Q_OBJECT
      Q_INTERFACES( Grantlee::TagLibraryInterface )
    public:
      MyLibrary(QObject *parent = 0)
        : QObject (parent)
      {
        m_nodeFactories.insert("mytag", new MyNodeFactory());

        m_filters.insert("myfilter", new MyFilter());
      }

      QHash<QString, Grantlee::AbstractNodeFactory*> nodeFactories(const QString &name = QString())
      {
        Q_UNUSED(name);
        return m_nodeFactories;
      }

      QHash<QString, Grantlee::Filter*> filters(const QString &name = QString())
      {
        Q_UNUSED(name);
        return m_filters;
      }
    };
  @endcode

  @section qtscript_libraries QtScript Libraries

  If you configure your application to use the grantlee_scriptabletags_library, it will be possible for you and theme writers to write tags and filters in QtScript instead of C++. Themers would have as much control as a C++ plugin writer over those steps of processing and rendering the template.

  Writing QtScript plugins is slightly different from writing C++ plugins, and is a bit more like writing Django plugins. Namely, in Javascript like python, functions are first-class objects, and javascript is dynamically typed. Additionally QtScript plugins are just text files, so they can easily be dynamically loaded at runtime. QtScript files must be UTF-8 encoded.

  Here is a complete QtScript library defining an @gr_tag{echo} tag which outputs its arguments:

  @code
    var EchoNode = function(content)
    {
      this.content = content;
      this.render = function(context)
      {
        return content.join(" ");
      };
    };

    function EchoNodeFactory(tagContent, parser)
    {
      var content = tagContent.split(" ");
      content = content.slice(1, content.length);
      return new Node("EchoNode", content);
    };
    EchoNodeFactory.tagName = "echo";
    Library.addFactory("EchoNodeFactory");
  @endcode

  Some things to note:
  - Library is a globally accessible object used to register Factories.
  - The addFactory method takes a string which is the name of an object, not the object itself.
  - The script factory function returns a %Node. The first argument to %Node is the name of the QtScript object in the library which defines the node. All additional arguments will be passed to the constructor of that node.
  - The %Node function must have a callable render property which takes a context argument.

  @todo \@section qtscript_diff Differences between C++ and QtScript library plugins.

  @subsection loaders Loaders

  As noted in @ref creating_templates, you will usually not create a Template directly, but retrieve it from an Engine instance. The Engine allows you to define where the templates are retrieved from when you request them by name.

  You can redefine the order of places in the filesystem which are searched for templates, and even define new ways to retrieve templates (i.e, not from the filesystem) by subclassing Grantlee::AbstractTemplateLoader and implementing the loadByName method. For existing loaders, see FileSystemTemplateLoader, InMemoryTemplateLoader, and AkonadiTemplateLoader.

*/

}