Implement the rest of SafeString::NestedString to override QString.
[grantlee:grantlee.git] / defaultfilters / strings.cpp
1 /*
2   This file is part of the Grantlee template system.
3
4   Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>
5
6   This library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Lesser General Public
8   License as published by the Free Software Foundation; either version
9   2 of the Licence, or (at your option) any later version.
10
11   This library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Library General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 #include "strings.h"
22
23 #include <QVariant>
24
25 #include "util_p.h"
26
27 QVariant AddSlashesFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
28 {
29   Q_UNUSED( argument )
30   Q_UNUSED( autoescape )
31   SafeString safeString = Util::getSafeString( input );
32   safeString.get().replace( '\\', "\\\\" ).get().replace( '\"', "\\\"" ).get().replace( '\'', "\\\'" );
33   return safeString;
34 }
35
36 QVariant CapFirstFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
37 {
38   Q_UNUSED( argument )
39   Q_UNUSED( autoescape )
40   SafeString safeString = Util::getSafeString( input );
41   if ( safeString.get().isEmpty() )
42     return QString();
43
44   return safeString.get().at( 0 ).toUpper() + safeString.get().right( safeString.get().size() - 1 );
45 }
46
47
48 EscapeJsFilter::EscapeJsFilter()
49 {
50   m_jsEscapes << QPair<QString, QString>( "\\", "\\x5C" )
51               << QPair<QString, QString>( "\'",  "\\x27" )
52               << QPair<QString, QString>( "\"",  "\\x22" )
53               << QPair<QString, QString>( ">", "\\x3E" )
54               << QPair<QString, QString>( "<", "\\x3C" )
55               << QPair<QString, QString>( "&", "\\x26" )
56               << QPair<QString, QString>( "=", "\\x3D" )
57               << QPair<QString, QString>( "-", "\\x2D" )
58               << QPair<QString, QString>( ";", "\\x3B" )
59               << QPair<QString, QString>( QChar( 0x2028 ), "\\u2028" )
60               << QPair<QString, QString>( QChar( 0x2029 ), "\\u2029" );
61
62   for( int i = 0; i < 32; ++i )
63   {
64     m_jsEscapes << QPair<QString, QString>( QChar( i ), "\\x" + QString( "%1" ).arg( i, 2, 16, QChar('0') ).toUpper() );
65   }
66 }
67
68 QVariant EscapeJsFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
69 {
70   Q_UNUSED( argument )
71   Q_UNUSED( autoescape )
72   QString retString = Util::getSafeString( input );
73
74   QListIterator<QPair<QString, QString> > it( m_jsEscapes );
75
76   while ( it.hasNext() ) {
77     QPair<QString, QString> escape = it.next();
78     retString = retString.replace( escape.first, escape.second );
79   }
80   return retString;
81 }
82
83 QVariant FixAmpersandsFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
84 {
85   Q_UNUSED( argument )
86   Q_UNUSED( autoescape )
87   SafeString safeString = Util::getSafeString( input );
88
89   QRegExp fixAmpersandsRegexp( "&(?!(\\w+|#\\d+);)" );
90
91   safeString.get().replace( fixAmpersandsRegexp, "&amp;" );
92
93   return safeString;
94 }
95
96 QVariant CutFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
97 {
98   Q_UNUSED( autoescape )
99   SafeString retString = Util::getSafeString( input );
100   SafeString argString = Util::getSafeString( argument );
101
102   bool inputSafe = retString.isSafe();
103
104   retString.get().remove( argString );
105
106   if ( inputSafe && argString.get() != ";" )
107     return Util::markSafe( retString );
108   else
109     return retString;
110 }
111
112 QVariant SafeFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
113 {
114   Q_UNUSED( argument )
115   Q_UNUSED( autoescape )
116   return Util::markSafe( Util::getSafeString( input ) );
117 }
118
119 QVariant LineNumbersFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
120 {
121   Q_UNUSED( argument )
122   SafeString safeString = Util::getSafeString( input );
123   QStringList lines = safeString.get().split( '\n' );
124   int width = QString::number( lines.size() ).size();
125
126   const bool shouldEscape = ( autoescape && !safeString.isSafe() );
127   for ( int i = 0; i < lines.size(); ++i ) {
128     lines[ i ] = QString( "%1. %2" ).arg( i + 1, width ).arg(
129                    shouldEscape ? QString( escape( lines.at( i ) ) ) : lines.at( i )
130                  );
131   }
132
133   return Util::markSafe( lines.join( QString( '\n' ) ) );
134 }
135
136 QVariant LowerFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
137 {
138   Q_UNUSED( argument )
139   Q_UNUSED( autoescape )
140   return Util::getSafeString( input ).get().toLower();
141 }
142
143 QVariant StringFormatFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
144 {
145   Q_UNUSED( autoescape )
146   SafeString a;
147   if ( Util::isSafeString( input ) )
148     a = Util::getSafeString( input );
149   else if ( input.type() == QVariant::List ) {
150     a = Util::toString( input.toList() );
151   }
152
153   return SafeString( Util::getSafeString( argument ).get().arg( a ), Util::getSafeString( input ).isSafe() );
154 }
155
156 QVariant TitleFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
157 {
158   Q_UNUSED( argument )
159   Q_UNUSED( autoescape )
160 //   QRegExp re( "\b([a-z])" );
161
162   QRegExp re( "([a-z])'([A-Z])" );
163
164   QString str = Util::getSafeString( input );
165
166   str.replace( re, QString( "\\1" ).toUpper() );
167
168   return str;
169 }
170
171 QVariant TruncateWordsFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
172 {
173   Q_UNUSED( autoescape )
174   SafeString s = Util::getSafeString( argument );
175
176   bool ok;
177   int numWords = s.get().toInt( &ok );
178
179   if ( !ok ) {
180     return input.toString();
181   }
182
183   QString inputString = Util::getSafeString( input );
184   QStringList words = inputString.split( ' ', QString::SkipEmptyParts );
185
186   if ( words.size() > numWords ) {
187     words = words.mid( 0, numWords );
188     if ( !words.at( words.size() - 1 ).endsWith( QLatin1String( "..." ) ) ) {
189       words << "...";
190     }
191   }
192   return words.join( QChar( ' ' ) );
193
194 }
195
196 QVariant UpperFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
197 {
198   Q_UNUSED( argument )
199   Q_UNUSED( autoescape )
200   return Util::getSafeString( input ).get().toUpper();
201 }
202
203 QVariant WordCountFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
204 {
205   Q_UNUSED( argument )
206   Q_UNUSED( autoescape )
207   return QString::number( Util::getSafeString( input ).get().split( ' ' ).size() );
208 }
209
210 QVariant LJustFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
211 {
212   Q_UNUSED( autoescape )
213   return Util::getSafeString( input ).get().leftJustified( Util::getSafeString( argument ).get().toInt() );
214 }
215
216 QVariant RJustFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
217 {
218   Q_UNUSED( autoescape )
219   return Util::getSafeString( input ).get().rightJustified( Util::getSafeString( argument ).get().toInt() );
220 }
221
222 QVariant CenterFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
223 {
224   Q_UNUSED( autoescape )
225   QString value = Util::getSafeString( input );
226   const int valueWidth = value.size();
227   const int width = Util::getSafeString( argument ).get().toInt();
228   const int totalPadding = width - valueWidth;
229   const int rightPadding = totalPadding >> 1;
230
231   return value.leftJustified( valueWidth + rightPadding ).rightJustified( width );
232 }
233
234 QVariant EscapeFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
235 {
236   Q_UNUSED( argument )
237   Q_UNUSED( autoescape )
238   return Util::markForEscaping( Util::getSafeString( input ) );
239 }
240
241 QVariant ForceEscapeFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
242 {
243   Q_UNUSED( argument )
244   Q_UNUSED( autoescape )
245   return Util::markSafe( escape( Util::getSafeString( input ) ) );
246 }
247
248 QVariant RemoveTagsFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
249 {
250   Q_UNUSED( autoescape )
251   QStringList tags = Util::getSafeString( argument ).get().split( ' ' );
252   QString tagRe = QString( "(%1)" ).arg( tags.join( "|" ) );
253   QRegExp startTag( QString( "<%1(/?>|(\\s+[^>]*>))" ).arg( tagRe ) );
254   QRegExp endTag( QString( "</%1>" ).arg( tagRe ) );
255
256   SafeString value = Util::getSafeString( input );
257   const bool safeInput = value.isSafe();
258   value.get().remove( startTag );
259   value.get().remove( endTag );
260   if ( safeInput )
261     return Util::markSafe( value );
262   return value;
263 }
264
265 QVariant StripTagsFilter::doFilter( const QVariant& input, const QVariant &argument, bool autoescape ) const
266 {
267   Q_UNUSED( argument )
268   Q_UNUSED( autoescape )
269   QRegExp tagRe( "<[^>]*>" );
270   tagRe.setMinimal( true );
271
272   QString value = Util::getSafeString( input );
273   value.remove( tagRe );
274   return value;
275 }
276
277 QVariant WordWrapFilter::doFilter( const QVariant& input, const QVariant& argument, bool autoescape ) const
278 {
279   Q_UNUSED( autoescape )
280   QString _input = Util::getSafeString( input );
281   int width = argument.toInt();
282   QStringList partList = _input.split( ' ', QString::SkipEmptyParts );
283   QString output = partList.takeFirst();
284   int pos = output.size() - output.lastIndexOf( '\n' ) - 1;
285   foreach( const QString &part, partList ) {
286     QStringList lines;
287     if ( part.contains( '\n' ) ) {
288       lines = part.split( '\n' );
289     } else {
290       lines.append( part );
291     }
292     pos += lines.first().size() + 1;
293     if ( pos > width ) {
294       output.append( '\n' );
295       pos += lines.last().size();
296     } else {
297       output.append( ' ' );
298       if ( lines.size() > 1 )
299         pos += lines.last().size();
300     }
301     output.append( part );
302   }
303   return output;
304 }
305
306 QVariant FloatFormatFilter::doFilter( const QVariant& input, const QVariant& argument, bool autoescape ) const
307 {
308   Q_UNUSED( autoescape )
309   double _input = Util::getSafeString( input ).get().toDouble();
310   int precision;
311   if ( argument.isValid() )
312     precision = Util::getSafeString( argument ).get().toInt();
313   else
314     precision = 1;
315
316   return QString::number( _input, 'f', precision );
317 }
318
319 QVariant SafeSequenceFilter::doFilter( const QVariant& input, const QVariant& argument, bool autoescape ) const
320 {
321   Q_UNUSED( argument )
322   Q_UNUSED( autoescape )
323   QVariantList list;
324   if ( input.type() == QVariant::List )
325     foreach( const QVariant &item, input.toList() )
326       list << Util::markSafe( Util::getSafeString( item ) );
327   return list;
328 }
329
330 QVariant LineBreaksFilter::doFilter( const QVariant& input, const QVariant& argument, bool autoescape ) const
331 {
332   Q_UNUSED( argument )
333   SafeString inputString = Util::getSafeString( input );
334   QRegExp re( "\n{2,}" );
335   QStringList output;
336
337   foreach( const QString &bit, inputString.get().split( re ) ) {
338     SafeString _bit = SafeString( bit, inputString.isSafe() );
339     if ( autoescape )
340       _bit = conditionalEscape( _bit );
341     _bit.get().replace( '\n', "<br />" );
342     output.append( QString( "<p>%1</p>" ).arg( _bit ) );
343   }
344   return Util::markSafe( output.join( "\n\n" ) );
345 }
346
347 QVariant LineBreaksBrFilter::doFilter( const QVariant& input, const QVariant& argument, bool autoescape ) const
348 {
349   Q_UNUSED( argument )
350   SafeString inputString = Util::getSafeString( input );
351   if ( autoescape && Util::isSafeString( input ) ) {
352     inputString = conditionalEscape( inputString );
353   }
354   return Util::markSafe( inputString.get().replace( '\n', "<br />" ) );
355 }
356
357 QVariant SlugifyFilter::doFilter( const QVariant& input, const QVariant& argument, bool autoescape ) const
358 {
359   Q_UNUSED( argument )
360   Q_UNUSED( autoescape )
361   QString inputString = Util::getSafeString( input );
362   inputString = inputString.normalized( QString::NormalizationForm_KD ).toAscii();
363   inputString = inputString.remove( QRegExp( "[^\\w\\s-]" ) ).trimmed().toLower();
364   return Util::markSafe( inputString.replace( QRegExp( "[-\\s]+" ), QChar( '-' ) ) );
365 }
366