Changeset 864
- Timestamp:
- 10/14/05 10:25:09 (3 years ago)
- Files:
-
- django/branches/new-admin/django/conf/admin_templates/admin_change_form.html (modified) (5 diffs)
- django/branches/new-admin/django/conf/global_settings.py (modified) (1 diff)
- django/branches/new-admin/django/conf/project_template/settings/main.py (modified) (1 diff)
- django/branches/new-admin/django/core/db/backends/mysql.py (modified) (1 diff)
- django/branches/new-admin/django/core/db/backends/postgresql.py (modified) (2 diffs)
- django/branches/new-admin/django/core/defaultfilters.py (modified) (1 diff)
- django/branches/new-admin/django/core/meta/fields.py (modified) (4 diffs)
- django/branches/new-admin/django/core/meta/__init__.py (modified) (2 diffs)
- django/branches/new-admin/django/core/template.py (modified) (10 diffs)
- django/branches/new-admin/django/views/admin/main.py (modified) (2 diffs)
- django/branches/new-admin/docs/design_philosophies.txt (copied) (copied from django/trunk/docs/design_philosophies.txt)
- django/branches/new-admin/docs/templates.txt (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/new-admin/django/conf/admin_templates/admin_change_form.html
r854 r864 3 3 {% load adminmedia %} 4 4 {% block extrahead %} 5 {% for js in javascript_imports %} 6 {% include_admin_script js %} 7 {% endfor %} 5 {% for js in javascript_imports %}{% include_admin_script js %}{% endfor %} 8 6 {% endblock %} 9 7 {% block coltype %}{{ coltype }}{% endblock %} … … 16 14 </div> 17 15 {% endif %}{% endblock %} 18 19 16 {% block content %}<div id="content-main"> 20 {% if change %} 21 {% if not is_popup %} 22 <ul class="object-tools"><li><a href="history/" class="historylink">History</a></li> 23 {% if has_absolute_url %} 24 <li><a href="/r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">View on site</a></li> 25 {% endif%} 26 </ul> 27 {% endif %} 28 {% endif %} 29 17 {% if change %}{% if not is_popup %} 18 <ul class="object-tools"><li><a href="history/" class="historylink">History</a></li> 19 {% if has_absolute_url %}<li><a href="/r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">View on site</a></li>{% endif%} 20 </ul> 21 {% endif %}{% endif %} 30 22 <form {{ form_enc_attrib }} action='{{ form_url }}' method="post"> 31 32 23 {% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %} 33 34 {% if save_on_top %} 35 {% submit_row %} 36 {% endif %} 37 38 {% if form.error_dict %} 39 <p class="errornote">Please correct the error{{ form.error_dict.items|pluralize }} below.</p> 40 {% endif %} 24 {% if save_on_top %}{% submit_row %}{% endif %} 25 {% if form.error_dict %}<p class="errornote">Please correct the error{{ form.error_dict.items|pluralize }} below.</p>{% endif %} 41 26 <b> 42 27 </b> 43 28 {% for bound_field_set in bound_field_sets %} 44 29 <fieldset class="module aligned {{ bound_field_set.classes }}"> 45 {% if bound_field_set.name %} 46 <h2>{{bound_field_set.name }}</h2> 47 {% endif %} 30 {% if bound_field_set.name %}<h2>{{bound_field_set.name }}</h2>{% endif %} 48 31 {% for bound_field_line in bound_field_set %} 49 32 {% admin_field_line bound_field_line %} … … 54 37 </fieldset> 55 38 {% endfor %} 56 57 39 {% if change %} 58 40 {% if ordered_objects %} … … 64 46 {% endif %} 65 47 {% endif %} 66 67 68 {% for relation in inline_related_objects %} 69 {% edit_inline relation %} 70 {% endfor %} 48 {% for related_object in inline_related_objects %}{% edit_inline related_object %}{% endfor %} 71 49 72 50 {% submit_row %} … … 89 67 <span id="handlep{% firstof ordered_object_names %}">{{ object|truncatewords:"5" }}</span> 90 68 </li> 91 {% endfor%}69 {% endfor%} 92 70 {% endif %} 93 71 {% endif %} django/branches/new-admin/django/conf/global_settings.py
r819 r864 50 50 DATABASE_USER = '' 51 51 DATABASE_PASSWORD = '' 52 DATABASE_HOST = '' # Set to empty string for localhost 52 DATABASE_HOST = '' # Set to empty string for localhost. 53 DATABASE_PORT = '' # Set to empty string for default. 53 54 54 55 # Host for sending e-mail. django/branches/new-admin/django/conf/project_template/settings/main.py
r512 r864 16 16 DATABASE_PASSWORD = '' # Not used with sqlite3. 17 17 DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. 18 DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. 18 19 19 20 SITE_ID = 1 django/branches/new-admin/django/core/db/backends/mysql.py
r854 r864 54 54 55 55 def cursor(self): 56 from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_P ASSWORD, DEBUG56 from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PORT, DATABASE_PASSWORD, DEBUG 57 57 if self.connection is None: 58 self.connection = Database.connect(user=DATABASE_USER, db=DATABASE_NAME, 59 passwd=DATABASE_PASSWORD, host=DATABASE_HOST, conv=django_conversions) 58 kwargs = { 59 'user': DATABASE_USER, 60 'db': DATABASE_NAME, 61 'passwd': DATABASE_PASSWORD, 62 'host': DATABASE_HOST, 63 'conv': django_conversions, 64 } 65 if DATABASE_PORT: 66 kwargs['port'] = DATABASE_PORT 67 self.connection = Database.connect(**kwargs) 60 68 if DEBUG: 61 69 return base.CursorDebugWrapper(MysqlDebugWrapper(self.connection.cursor()), self) django/branches/new-admin/django/core/db/backends/postgresql.py
r854 r864 16 16 17 17 def cursor(self): 18 from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_P ASSWORD, DEBUG, TIME_ZONE18 from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PORT, DATABASE_PASSWORD, DEBUG, TIME_ZONE 19 19 if self.connection is None: 20 20 if DATABASE_NAME == '': … … 28 28 if DATABASE_HOST: 29 29 conn_string += " host=%s" % DATABASE_HOST 30 if DATABASE_PORT: 31 conn_string += " port=%s" % DATABASE_PORT 30 32 self.connection = Database.connect(conn_string) 31 33 self.connection.set_isolation_level(1) # make transactions transparent to all cursors django/branches/new-admin/django/core/defaultfilters.py
r666 r864 333 333 "If value is unavailable, use given default" 334 334 return value or arg 335 336 def default_if_none(value, arg): 337 "If value is None, use given default" 338 if value is None: 339 return arg 340 return value 335 341 336 342 def divisibleby(value, arg): django/branches/new-admin/django/core/meta/fields.py
r854 r864 805 805 806 806 807 808 807 class BoundField(object): 809 808 def __init__(self, field, field_mapping, original): … … 839 838 840 839 class FieldLine(object): 841 def __init__(self, linespec, field_locator_func):840 def __init__(self, field_locator_func, linespec): 842 841 if isinstance(linespec, basestring): 843 842 self.fields = [field_locator_func(linespec)] … … 872 871 873 872 class FieldSet(object): 874 def __init__(self, name, classes, field_l ines):873 def __init__(self, name, classes, field_locator_func, line_specs): 875 874 self.name = name 876 self.field_lines = field_lines875 self.field_lines = [FieldLine(field_locator_func, line_spec) for line_spec in line_specs] 877 876 self.classes = classes 878 877 … … 948 947 classes = fs_options.get('classes', None) 949 948 line_specs = fs_options['fields'] 950 field_lines = [FieldLine(line_spec, opts.get_field) for line_spec in line_specs] 951 new_fieldset_list.append(FieldSet(name, classes, field_lines) ) 949 new_fieldset_list.append(FieldSet(name, classes, opts.get_field, line_specs) ) 952 950 return new_fieldset_list 953 951 952 django/branches/new-admin/django/core/meta/__init__.py
r854 r864 208 208 209 209 def get_follow(self, override=None): 210 if override: 211 over = override.copy() 212 elif self.edit_inline: 213 over = {} 210 if isinstance(override, bool): 211 if override: 212 over = {} 213 else: 214 return None 214 215 else: 215 return None 216 if override: 217 over = override.copy() 218 elif self.edit_inline: 219 over = {} 220 else: 221 return None 216 222 217 223 over[self.field.name] = False … … 1755 1761 1756 1762 def manipulator_get_inline_related_objects_wrapped(opts, klass, add, change, self): 1757 return opts.get_inline_related_objects_wrapped() 1763 return opts.get_inline_related_objects_wrapped() 1758 1764 1759 1765 def manipulator_flatten_data(opts, klass, add, change, self): django/branches/new-admin/django/core/template.py
r817 r864 56 56 """ 57 57 import re 58 from django.conf.settings import DEFAULT_CHARSET 58 from django.conf.settings import DEFAULT_CHARSET, DEBUG 59 59 60 60 __all__ = ('Template','Context','compile_string') … … 78 78 UNKNOWN_SOURCE="<unknown source>" 79 79 80 81 80 #match starts of lines 82 81 newline_re = re.compile("^", re.M); … … 85 84 tag_re = re.compile('(%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), 86 85 re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END))) 87 88 86 89 87 # global dict used by register_tag; maps custom tags to callback functions … … 126 124 def compile_string(template_string, filename): 127 125 "Compiles template_string into NodeList ready for rendering" 128 tokens = tokenize(template_string, filename) 129 parser = Parser(tokens) 126 if DEBUG: 127 lexer_factory = DebugLexer 128 parser_factory = DebugParser 129 else: 130 lexer_factory = Lexer 131 parser_factory = Parser 132 133 lexer = lexer_factory(template_string, filename) 134 parser = parser_factory(lexer.tokenize()) 130 135 return parser.parse() 131 136 … … 177 182 178 183 class Token: 179 def __init__(self, token_type, contents , source):184 def __init__(self, token_type, contents): 180 185 "The token_type must be TOKEN_TEXT, TOKEN_VAR or TOKEN_BLOCK" 181 186 self.token_type, self.contents = token_type, contents 182 self.source = source183 187 184 188 def __str__(self): 185 return '<%s token: "%s..." from %s, line %d>' % (189 return '<%s token: "%s...">' % ( 186 190 {TOKEN_TEXT:'Text', TOKEN_VAR:'Var', TOKEN_BLOCK:'Block'}[self.token_type], 187 self.contents[:20].replace('\n', ''), 188 self.source[0], self.source[1] 191 self.contents[:20].replace('\n', '') 189 192 ) 190 193 191 def find_linebreaks(template_string): 192 for match in newline_re.finditer(template_string): 193 yield match.start() 194 195 def tokenize(template_string, filename): 196 "Return a list of tokens from a given template_string" 194 class Lexer(object): 195 def __init__(self, template_string, filename): 196 self.template_string = template_string 197 self.filename = filename 197 198 198 token_tups = [] 199 upto = 0 200 line = 1 201 #TODO:Py2.4 generator expression 202 linebreaks = find_linebreaks(template_string) 203 next_linebreak = linebreaks.next() 204 try: 205 for match in tag_re.finditer(template_string): 206 start, end = match.span() 207 if start > upto: 208 token_tups.append( (template_string[upto:start], line) ) 209 upto = start 199 def tokenize(self): 200 "Return a list of tokens from a given template_string" 201 bits = filter(None, tag_re.split(self.template_string)) 202 return map(self.create_token, bits) 203 204 def create_token(self,token_string): 205 "Convert the given token string into a new Token object and return it" 206 if token_string.startswith(VARIABLE_TAG_START): 207 token = Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip()) 208 elif token_string.startswith(BLOCK_TAG_START): 209 token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip()) 210 else: 211 token = Token(TOKEN_TEXT, token_string) 212 return token 213 214 class DebugLexer(Lexer): 215 def __init__(self, template_string, filename): 216 super(DebugLexer,self).__init__(template_string, filename) 217 218 def find_linebreaks(self, template_string): 219 for match in newline_re.finditer(template_string): 220 yield match.start() 221 222 def tokenize(self): 223 "Return a list of tokens from a given template_string" 224 225 token_tups = [] 226 upto = 0 227 line = 1 228 #TODO:Py2.4 generator expression 229 linebreaks = self.find_linebreaks(self.template_string) 230 next_linebreak = linebreaks.next() 231 try: 232 for match in tag_re.finditer(self.template_string): 233 start, end = match.span() 234 if start > upto: 235 token_tups.append( (self.template_string[upto:start], line) ) 236 upto = start 237 238 while next_linebreak <= upto: 239 next_linebreak = linebreaks.next() 240 line += 1 210 241 242 token_tups.append( (self.template_string[start:end], line) ) 243 upto = end 244 211 245 while next_linebreak <= upto: 212 246 next_linebreak = linebreaks.next() 213 247 line += 1 214 215 token_tups.append( (template_string[start:end], line) ) 216 upto = end 217 218 while next_linebreak <= upto: 219 next_linebreak = linebreaks.next() 220 line += 1 221 except StopIteration: 222 pass 223 224 last_bit = template_string[upto:] 225 if len(last_bit): 226 token_tups.append( (last_bit, line) ) 227 228 return [ create_token(tok, (filename, line)) for tok, line in token_tups] 229 230 def create_token(token_string, source): 231 "Convert the given token string into a new Token object and return it" 232 if token_string.startswith(VARIABLE_TAG_START): 233 return Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip(), source) 234 elif token_string.startswith(BLOCK_TAG_START): 235 return Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip(), source) 236 else: 237 return Token(TOKEN_TEXT, token_string, source) 238 239 240 class Parser: 248 except StopIteration: 249 pass 250 251 last_bit = self.template_string[upto:] 252 if len(last_bit): 253 token_tups.append( (last_bit, line) ) 254 255 return [ self.create_token(tok, (self.filename, line)) for tok, line in token_tups] 256 257 258 def create_token(self, token_string, source): 259 token = super(DebugLexer, self).create_token(token_string) 260 token.source = source 261 return token 262 263 264 class Parser(object): 241 265 def __init__(self, tokens): 242 266 self.tokens = tokens 243 self.command_stack = []244 267 245 268 def parse(self, parse_until=[]): … … 248 271 token = self.next_token() 249 272 if token.token_type == TOKEN_TEXT: 250 nodelist.append(TextNode(token.contents), token)273 self.extend_nodelist(nodelist, TextNode(token.contents), token) 251 274 elif token.token_type == TOKEN_VAR: 252 275 if not token.contents: 253 raise TemplateSyntaxError, "Empty variable tag at %s, line %d" % (token.source[0], token.source[1])254 nodelist.append(VariableNode(token.contents), token)276 self.empty_variable(token) 277 self.extend_nodelist(nodelist, VariableNode(token.contents), token) 255 278 elif token.token_type == TOKEN_BLOCK: 256 279 if token.contents in parse_until: … … 261 284 command = token.contents.split()[0] 262 285 except IndexError: 263 raise TemplateSyntaxError, "Empty block tag" 286 self.empty_block_tag(token) 287 288 # execute callback function for this tag and append resulting node 289 self.enter_command(command, token); 264 290 try: 265 # execute callback function for this tag and append resulting node 266 self.command_stack.append( (command, token.source) ) 267 nodelist.append(registered_tags[command](self, token), token) 268 self.command_stack.pop() 291 compile_func = registered_tags[command] 269 292 except KeyError: 270 raise TemplateSyntaxError, "Invalid block tag: '%s' at %s, line %d" % (command, token.source[0], token.source[1]) 293 self.invalid_block_tag(token, command) 294 295 self.extend_nodelist(nodelist, compile_func(self, token), token) 296 self.exit_command(); 297 271 298 if parse_until: 272 (command, (file,line)) = self.command_stack.pop() 273 msg = "Unclosed tag '%s' starting at %s, line %d. Looking for one of: %s " % \ 274 (command, file, line, ', '.join(parse_until) ) 275 raise TemplateSyntaxError, msg 299 self.unclosed_block_tag(token) 300 276 301 return nodelist 277 302 303 def extend_nodelist(self, nodelist, node, token): 304 nodelist.append(node) 305 306 def enter_command(self, command, token): 307 pass 308 309 def exit_command(self): 310 pass 311 312 def empty_variable(self, token): 313 raise TemplateSyntaxError, "Empty variable tag" 314 315 def empty_block_tag(self, token): 316 raise TemplateSyntaxError, "Empty block tag" 317 318 def invalid_block_tag(self, token, command): 319 raise TemplateSyntaxError, "Invalid block tag: %s" % (command) 320 321 def unclosed_block_tag(self, token): 322 raise TemplateSyntaxError, "Unclosed tags: %s " % ', '.join(parse_until) 323 278 324 def next_token(self): 279 325 return self.tokens.pop(0) … … 284 330 def delete_first_token(self): 285 331 del self.tokens[0] 332 333 334 class DebugParser(Parser): 335 def __init__(self, lexer): 336 super(DebugParser, self).__init__(lexer) 337 self.command_stack = [] 338 339 def enter_command(self, command, token): 340 self.command_stack.append( (command, token.source) ) 341 342 def exit_command(self): 343 self.command_stack.pop() 344 345 def format_source(self, source): 346 return "at %s, line %d" % source 347 348 def extend_nodelist(self, nodelist, node, token): 349 node.source = token.source 350 super(DebugParser, self).extend_nodelist(nodelist, node, token) 351 352 def empty_variable(self, token): 353 raise TemplateSyntaxError, "Empty variable tag %s" % self.format_source(token.source) 354 355 def empty_block_tag(self, token): 356 raise TemplateSyntaxError, "Empty block tag %s" % self.format_source(token.source) 357 358 def invalid_block_tag(self, token, command): 359 raise TemplateSyntaxError, "Invalid block tag: '%s' %s" % (command, self.format_source(token.source)) 360 361 def unclosed_block_tag(self, token): 362 (command, (file,line)) = self.command_stack.pop() 363 msg = "Unclosed tag '%s' starting at %s, line %d. Looking for one of: %s " % \ 364 (command, file, line, ', '.join(parse_until) ) 365 raise TemplateSyntaxError, msg 286 366 287 367 class FilterParser: … … 485 565 nodes.extend(self.nodelist.get_nodes_by_type(nodetype)) 486 566 return nodes 487 488 567 489 568 class NodeList(list): … … 503 582 nodes.extend(node.get_nodes_by_type(nodetype)) 504 583 return nodes 505 506 def append(self, node, token = None):507 if token:508 node.source = token.source509 super(NodeList, self).append(node)510 584 511 585 class TextNode(Node): django/branches/new-admin/django/views/admin/main.py
r854 r864 635 635 636 636 extra_context = { 637 'add': add, 637 'add': add, 638 638 'change': change, 639 639 'first_form_field_id': first_form_field.get_id(), … … 805 805 for rel_opts, rel_field in inline_related_objects: 806 806 if rel_opts.order_with_respect_to and rel_opts.order_with_respect_to.rel and rel_opts.order_with_respect_to.rel.to == opts: 807 orig_list = getattr(manipulator.original_object, 'get_%s_list' % opts.get_rel_object_method_name(rel_opts, rel_field))() 807 808 form.order_objects.extend(orig_list) 808 809 django/branches/new-admin/docs/templates.txt
r741 r864 377 377 ``forloop.counter`` The current iteration of the loop (1-indexed) 378 378 ``forloop.counter0`` The current iteration of the loop (0-indexed) 379 ``forloop.revcounter`` The number of iterations from the end of the 379 ``forloop.revcounter`` The number of iterations from the end of the 380 380 loop (1-indexed) 381 ``forloop.revcounter0`` The number of iterations from the end of the 381 ``forloop.revcounter0`` The number of iterations from the end of the 382 382 loop (0-indexed) 383 383 ``forloop.first`` True if this is the first time through the loop … … 570 570 571 571 ``add`` 572 Adds the arg to the value 572 Adds the arg to the value. 573 573 574 574 ``addslashes`` 575 Adds slashes - useful for passing strings to JavaScript, for example.575 Adds slashes. Useful for passing strings to JavaScript, for example. 576 576 577 577 ``capfirst`` 578 Capitalizes the first character of the value 578 Capitalizes the first character of the value. 579 579 580 580 ``center`` 581 Centers the value in a field of a given width 581 Centers the value in a field of a given width. 582 582 583 583 ``cut`` 584 Removes all values of arg from the given string 584 Removes all values of arg from the given string. 585 585 586 586 ``date`` 587 Formats a date according to the given format (same as the ``now`` tag) 587 Formats a date according to the given format (same as the ``now`` tag). 588 588 589 589 ``default`` 590 If value is unavailable, use given default 590 If value is unavailable, use given default. 591 592 ``default_if_none`` 593 If value is ``None``, use given default. 591 594 592 595 ``dictsort`` … … 599 602 600 603 ``divisibleby`` 601 Returns true if the value is divisible by the argument 604 Returns true if the value is divisible by the argument. 602 605 603 606 ``escape`` 604 Escapes a string's HTML 607 Escapes a string's HTML. 605 608 606 609 ``filesizeformat`` … … 609 612 610 613 ``first`` 611 Returns the first item in a list 614 Returns the first item in a list. 612 615 613 616 ``fix_ampersands`` 614 Replaces ampersands with ``&`` entities 617 Replaces ampersands with ``&`` entities. 615 618 616 619 ``floatformat`` 617 620 Displays a floating point number as 34.2 (with one decimal places) - but 618 only if there's a point to be displayed 621 only if there's a point to be displayed. 619 622 620 623 ``get_digit`` … … 625 628 626 629 ``join`` 627 Joins a list with a string, like Python's ``str.join(list)`` 630 Joins a list with a string, like Python's ``str.join(list)``. 628 631 629 632 ``length`` 630 Returns the length of the value - useful for lists633 Returns the length of the value. Useful for lists. 631 634 632 635 ``length_is`` 633 Returns a boolean of whether the value's length is the argument 636 Returns a boolean of whether the value's length is the argument. 634 637 635 638 ``linebreaks`` 636 Converts newlines into <p> and <br />s 639 Converts newlines into <p> and <br />s. 637 640 638 641 ``linebreaksbr`` 639 Converts newlines into <br />s 642 Converts newlines into <br />s. 640 643 641 644 ``linenumbers`` 642 Displays text with line numbers 645 Displays text with line numbers. 643 646 644 647 ``ljust`` 645 Left-aligns the value in a field of a given width 648 Left-aligns the value in a field of a given width. 646 649 647 650 **Argument:** field size 648 651 649 652 ``lower`` 650 Converts a string into all lowercase 653 Converts a string into all lowercase. 651 654 652 655 ``make_list`` … … 655 658 656 659 ``phone2numeric`` 657 Takes a phone number and converts it in to its numerical equivalent 660 Takes a phone number and converts it in to its numerical equivalent. 658 661 659 662 ``pluralize`` 660 Returns 's' if the value is not 1, for '1 vote' vs. '2 votes' 663 Returns 's' if the value is not 1, for '1 vote' vs. '2 votes'. 661 664 662 665 ``pprint`` 663 A wrapper around pprint.pprint -- for debugging, really 666 A wrapper around pprint.pprint -- for debugging, really. 664 667 665 668 ``random`` 666 Returns a random item from the list 669 Returns a random item from the list. 667 670 668 671 ``removetags`` 669 Removes a space separated list of [X]HTML tags from the output 672 Removes a space separated list of [X]HTML tags from the output. 670 673 671 674 ``rjust`` 672 Right-aligns the value in a field of a given width 675 Right-aligns the value in a field of a given width. 673 676 674 677 **Argument:** field size … … 697 700 698 701 ``striptags`` 699 Strips all [X]HTML tags 702 Strips all [X]HTML tags. 700 703 701 704 ``time`` … … 703 706 704 707 ``timesince`` 705 Formats a date as the time since that date (i.e. "4 days, 6 hours") 708 Formats a date as the time since that date (i.e. "4 days, 6 hours"). 706 709 707 710 ``title`` 708 Converts a string into titlecase 711 Converts a string into titlecase. 709 712 710 713 ``truncatewords`` 711 Truncates a string after a certain number of words 714 Truncates a string after a certain number of words. 712 715 713 716 **Argument:** Number of words to truncate after … … 734 737 735 738 ``upper`` 736 Converts a string into all uppercase 739 Converts a string into all uppercase. 737 740 738 741 ``urlencode`` 739 Escapes a value for use in a URL 742 Escapes a value for use in a URL. 740 743 741 744 ``urlize`` 742 Converts URLs in plain text into clickable links 745 Converts URLs in plain text into clickable links. 743 746 744 747 ``urlizetrunc`` 745 Converts URLs into clickable links, truncating URLs to the given character limit 746 747 **Argument:** Length to truncate URLs to. 748 Converts URLs into clickable links, truncating URLs to the given character 749 limit. 750 751 **Argument:** Length to truncate URLs to 748 752 749 753 ``wordcount`` 750 Returns the number of words 754 Returns the number of words. 751 755 752 756 ``wordwrap`` 753 Wraps words at specified line length 754 755 **Argument:** number of words to wrap the text at.757 Wraps words at specified line length. 758 759 **Argument:** number of words at which to wrap the text 756 760 757 761 ``yesno``
