├── AUTHORS ├── LICENSE ├── MANIFEST.in ├── README ├── setup.py └── templatesadmin ├── __init__.py ├── edithooks ├── __init__.py ├── dotbackupfiles.py ├── gitcommit.py └── hgcommit.py ├── forms.py ├── templates └── templatesadmin │ ├── base.html │ ├── edit.html │ └── overview.html ├── templatetags ├── __init__.py └── templatesadmin_tags.py ├── urls.py └── views.py /AUTHORS: -------------------------------------------------------------------------------- 1 | Martin Mahner 2 | Filip Noetzel 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2,07-2009, Martin Mahner 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name django-templatesadmin nor the names of its contributors 13 | may be used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include MANIFEST.in 3 | include README.rst 4 | recursive-include templatesadmin/templates * 5 | recursive-include templatesadmin/locale * 6 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | .. warning:: Not in active development. Probably not functional in latest 2 | Django version. If you like to take over the project please 3 | contact me. 4 | 5 | =============== 6 | Templates Admin 7 | =============== 8 | 9 | Templates Admin is a tiny, nifty application for your Django_ project to edit 10 | your templates, that are stored on your disk, via an admin interface. 11 | 12 | Originally this app was inspired by dbtemplates_. 13 | 14 | .. _Django: http://www.djangoproject.com/ 15 | .. _dbtemplates: http://code.google.com/p/django-dbtemplates/ 16 | 17 | Installation: 18 | ============= 19 | 20 | 1. Put ``templatesadmin`` into your INSTALLED_APPS setting. 21 | 22 | 2. Add this line to your urlconf but **before** your admin include:: 23 | 24 | (r'^admin/templatesadmin/', include('templatesadmin.urls')), 25 | 26 | 3. Create a group ``TemplateAdmins`` and put all users in there, who should been 27 | able to edit templates. You don't need to grant any permissions to that group. 28 | Just call it ``TemplateAdmins``. 29 | 30 | Keep in mind that also Superusers (*is_admin* flag) must belong to this group, if 31 | they should been able to edit templates. The group name is case-sensitive! 32 | 33 | 4. Point your webbrowser to ``http://localhost/admin/templatesadmin/`` and start 34 | editing. 35 | 36 | Optional Settings: 37 | ================== 38 | 39 | There are some settings that you can override in your ``settings.py``: 40 | 41 | 1. ``TEMPLATESADMIN_GROUP``: The name of your group of your TemplatesAdmin 42 | Users. 43 | 44 | Default: ``TemplateAdmins`` 45 | 46 | 2. ``TEMPLATESADMIN_VALID_FILE_EXTENSIONS``: A tuple of file-extensions (without 47 | the leading dot) that are editable by TemplatesAdmin. 48 | 49 | Default:: 50 | 51 | TEMPLATESADMIN_VALID_FILE_EXTENSIONS = ( 52 | 'html', 53 | 'htm', 54 | 'txt', 55 | 'css', 56 | 'backup' 57 | ) 58 | 59 | 3. ``TEMPLATESADMIN_TEMPLATE_DIRS``: A tuple of directories you want your users 60 | to edit, instead of all templates. 61 | 62 | Default: All user-defined and application template-dirs. 63 | 64 | 4. ``TEMPLATESADMIN_HIDE_READONLY``: A boolean to wether enable or disable 65 | displaying of read-only templates. 66 | 67 | Default: ``False`` 68 | 69 | 5. ``TEMPLATESADMIN_EDITHOOKS``: A tuple of callables edithooks. Edithooks are 70 | a way to interact with changes made on a template. Think of a plugin system. 71 | 72 | There are two builtin edithooks: 73 | 74 | - ``dotbackupfiles.DotBackupFilesHook``: Creates a copy of the original file 75 | before overwriting, naming it ``.backup``. 76 | - ``gitcommit.GitCommitHook``: Commits your templates after saving via git 77 | version control. 78 | - ``hgcommit.HgCommitHook``: Creates a `mercurial 79 | `_ commit after saving. 80 | 81 | You can define your own edithooks, see above hooks as example. 82 | 83 | Default:: 84 | 85 | TEMPLATESADMIN_EDITHOOKS = ( 86 | 'templatesadmin.edithooks.dotbackupfiles.DotBackupFilesHook', 87 | ) 88 | 89 | 90 | LICENSE: 91 | ======== 92 | 93 | This application is licensed under the ``Beerware License``. 94 | See ``LICENSE`` for details. 95 | 96 | Changelog: 97 | ========== 98 | 99 | **v0.7 (2012-11-20)** 100 | 101 | * Added missing permission check for an Admin view. 102 | 103 | **v0.6 (2009-09-08)** 104 | 105 | * Published under a proper BSD license. 106 | * The templates now inherits from the Django templates to provide a better 107 | look and feel. 108 | * A lot of overall improvements from typo fixing to better permission handling. 109 | Thanks to peritus and rlaager. 110 | 111 | **v0.5.5 (2009-02-13)** 112 | 113 | * Documented that there is a edithook for mercurial repositories. 114 | * Bugfix in GitCommitHook: Allow non-ascii characters. 115 | 116 | **v0.5.4 (2009-02-13)** 117 | 118 | * Fixed missing templatetags in pypi release. 119 | 120 | **v0.5.3 (2009-02-03)** 121 | 122 | * Edit-Views now have an optional argument "base_form" to overwrite the default form. 123 | * Removed shorten-path functions. They didn't work under some conditions. 124 | * List of templates in the admin overview are shorter. 125 | 126 | **v0.5.2 (2008-12-12)** 127 | 128 | * Added a edithook for dealing with mercurial repositories. Thank you v.oostveen! (Issue3_) 129 | * Fixed handling of newline characters at the end of the file, which causes to 130 | delete the last character. (Issue4_) 131 | 132 | .. _Issue3: http://code.google.com/p/django-templatesadmin/issues/detail?id=3 133 | .. _Issue4: http://code.google.com/p/django-templatesadmin/issues/detail?id=4 134 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from distutils.core import setup 3 | from distutils.command.install import INSTALL_SCHEMES 4 | 5 | app_name = 'templatesadmin' 6 | version = __import__(app_name).__version__ 7 | 8 | # Tell distutils to put the data_files in platform-specific installation 9 | # locations. See here for an explanation: 10 | # http://groups.google.com/group/comp.lang.python/browse_thread/thread/35ec7b2fed36eaec/2105ee4d9e8042cb 11 | for scheme in INSTALL_SCHEMES.values(): 12 | scheme['data'] = scheme['purelib'] 13 | 14 | # Compile the list of packages available, because distutils doesn't have 15 | # an easy way to do this. 16 | packages, data_files = [], [] 17 | root_dir = os.path.dirname(__file__) 18 | if root_dir: 19 | os.chdir(root_dir) 20 | 21 | for dirpath, dirnames, filenames in os.walk(app_name): 22 | # Ignore dirnames that start with '.' 23 | for i, dirname in enumerate(dirnames): 24 | if dirname.startswith('.'): del dirnames[i] 25 | if '__init__.py' in filenames: 26 | pkg = dirpath.replace(os.path.sep, '.') 27 | if os.path.altsep: 28 | pkg = pkg.replace(os.path.altsep, '.') 29 | packages.append(pkg) 30 | elif filenames: 31 | prefix = dirpath[len(app_name)+1:] # Strip "app_name/" or "app_name\" 32 | for f in filenames: 33 | data_files.append(os.path.join(prefix, f)) 34 | 35 | setup(name='django-'+app_name, 36 | version=version, 37 | description='A Django app to make minor changes to your templates on the fly.', 38 | long_description=open('README').read(), 39 | author='Martin Mahner', 40 | author_email='martin@mahner.org', 41 | url='http://github.com/bartTC/django-templatesadmin/', 42 | package_dir={app_name: app_name}, 43 | packages=packages, 44 | package_data={app_name: data_files}, 45 | classifiers=['Development Status :: 4 - Beta', 46 | 'Environment :: Web Environment', 47 | 'Intended Audience :: Developers', 48 | 'License :: OSI Approved :: BSD License', 49 | 'Operating System :: OS Independent', 50 | 'Programming Language :: Python', 51 | 'Topic :: Utilities'], 52 | ) 53 | -------------------------------------------------------------------------------- /templatesadmin/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.7' 2 | 3 | class TemplatesAdminException(Exception): 4 | pass 5 | -------------------------------------------------------------------------------- /templatesadmin/edithooks/__init__.py: -------------------------------------------------------------------------------- 1 | from templatesadmin.forms import TemplateForm 2 | 3 | class TemplatesAdminHook(object): 4 | ''' 5 | Hook baseclass 6 | ''' 7 | 8 | @classmethod 9 | def pre_save(cls, request, form, template_path): 10 | pass 11 | 12 | @classmethod 13 | def post_save(cls, request, form, template_path): 14 | pass 15 | 16 | @classmethod 17 | def contribute_to_form(cls, form, template_path): 18 | return form 19 | -------------------------------------------------------------------------------- /templatesadmin/edithooks/dotbackupfiles.py: -------------------------------------------------------------------------------- 1 | from shutil import copy 2 | 3 | from django import forms 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | from templatesadmin.edithooks import TemplatesAdminHook 7 | from templatesadmin import TemplatesAdminException 8 | 9 | class DotBackupFilesHook(TemplatesAdminHook): 10 | ''' 11 | Backup File before saving 12 | ''' 13 | 14 | @classmethod 15 | def pre_save(cls, request, form, template_path): 16 | backup = form.cleaned_data['backup'] 17 | 18 | if not backup: 19 | return None 20 | 21 | try: 22 | copy(template_path, '%s.backup' % template_path) 23 | except IOError, e: 24 | raise TemplatesAdminException( 25 | _(u'Backup Template "%(path)s" has not been saved! Reason: %(errormsg)s' % { 26 | 'path': template_path, 27 | 'errormsg': e 28 | }) 29 | ) 30 | 31 | return "Backup \'%s.backup\' has been saved." % template_path 32 | 33 | @classmethod 34 | def contribute_to_form(cls, template_path): 35 | return dict(backup=forms.BooleanField( 36 | label = _('Backup file before saving?'), 37 | required = False, 38 | )) 39 | -------------------------------------------------------------------------------- /templatesadmin/edithooks/gitcommit.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.utils.translation import ugettext_lazy as _ 3 | from templatesadmin import TemplatesAdminException 4 | from templatesadmin.edithooks import TemplatesAdminHook 5 | 6 | import subprocess 7 | import os 8 | 9 | class GitCommitHook(TemplatesAdminHook): 10 | ''' 11 | Commit to git after saving 12 | ''' 13 | 14 | @classmethod 15 | def post_save(cls, request, form, template_path): 16 | dir, file = os.path.dirname(template_path) + "/", os.path.basename(template_path) 17 | 18 | if request.user.first_name and request.user.last_name: 19 | author = "%s %s" % (request.user.first_name, request.user.last_name) 20 | else: 21 | author = request.user.username 22 | 23 | message = form.cleaned_data['commitmessage'] or '--' 24 | 25 | command = ( 26 | 'GIT_COMMITTER_NAME="%(author)s" GIT_COMMITER_EMAIL="%(email)s" ' 27 | 'GIT_AUTHOR_NAME="%(author)s" GIT_AUTHOR_EMAIL="%(email)s" ' 28 | 'git commit -F - -- %(file)s' 29 | ) % { 30 | 'file': template_path, 31 | 'author': author, 32 | 'email': request.user.email, 33 | } 34 | 35 | # Stolen from gitpython's git/cmd.py 36 | proc = subprocess.Popen( 37 | args=command, 38 | shell=True, 39 | cwd=dir, 40 | stdin=subprocess.PIPE, 41 | stdout=subprocess.PIPE, 42 | stderr=subprocess.PIPE, 43 | ) 44 | 45 | try: 46 | proc.stdin.write(message.encode('utf-8')) 47 | proc.stdin.close() 48 | stderr_value = proc.stderr.read() 49 | stdout_value = proc.stdout.read() 50 | status = proc.wait() 51 | finally: 52 | proc.stderr.close() 53 | 54 | if status != 0: 55 | raise TemplatesAdminException("Error while executing %s: %s" % (command, stderr_value.rstrip(), )) 56 | 57 | return stdout_value.rstrip() 58 | 59 | @classmethod 60 | def contribute_to_form(cls, template_path): 61 | return dict(commitmessage=forms.CharField( 62 | widget=forms.TextInput(attrs={'size':'100'}), 63 | label = _('Change message'), 64 | required = False, 65 | )) 66 | -------------------------------------------------------------------------------- /templatesadmin/edithooks/hgcommit.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.conf import settings 3 | from django.utils.translation import ugettext_lazy as _ 4 | from templatesadmin import TemplatesAdminException 5 | from templatesadmin.edithooks import TemplatesAdminHook 6 | 7 | from mercurial import hg, ui, match 8 | import os 9 | 10 | 11 | TEMPLATESADMIN_HG_ROOT = getattr( 12 | settings, 13 | 'TEMPLATESADMIN_HG_ROOT', 14 | None 15 | ) 16 | 17 | 18 | class HgCommitHook(TemplatesAdminHook): 19 | ''' 20 | Commit to git after saving 21 | ''' 22 | 23 | @classmethod 24 | def post_save(cls, request, form, template_path): 25 | dir = os.path.dirname(template_path) + os.sep 26 | file = os.path.basename(template_path) 27 | 28 | if request.user.first_name and request.user.last_name: 29 | author = "%s %s" % (request.user.first_name, request.user.last_name) 30 | else: 31 | author = request.user.username 32 | 33 | message = form.cleaned_data['commitmessage'] or '--' 34 | 35 | path = TEMPLATESADMIN_HG_ROOT 36 | if path is None: 37 | for template_dir in settings.TEMPLATE_DIRS: 38 | if dir.startswith(template_dir): 39 | if path is None or len(templare_dir)>len(path): 40 | path = template_dir 41 | if path is None: 42 | raise TemplatesAdminException("Could not find template base directory") 43 | uio = ui.ui() 44 | uio.setconfig('ui', 'interactive', False) 45 | uio.setconfig('ui', 'report_untrusted', False) 46 | uio.setconfig('ui', 'quiet', True) 47 | repo = hg.repository(uio, path=path) 48 | filter = match.match(repo.root, dir, [file]) 49 | repo.commit(match=filter, text=message, user="%s <%s>" % (author, request.user.email)) 50 | 51 | return "Template '%s' was committed succesfully into mercurial repository." % file 52 | 53 | @classmethod 54 | def contribute_to_form(cls, template_path): 55 | return dict(commitmessage=forms.CharField( 56 | widget=forms.TextInput(attrs={'size':'100'}), 57 | label = _('Change message'), 58 | required = False, 59 | )) 60 | -------------------------------------------------------------------------------- /templatesadmin/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | class TemplateForm(forms.Form): 4 | content = forms.CharField( 5 | widget=forms.Textarea() 6 | ) 7 | -------------------------------------------------------------------------------- /templatesadmin/templates/templatesadmin/base.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | 3 | {% block extrahead %} 4 | 21 | {% endblock %} 22 | 23 | -------------------------------------------------------------------------------- /templatesadmin/templates/templatesadmin/edit.html: -------------------------------------------------------------------------------- 1 | {% extends "templatesadmin/base.html" %} 2 | 3 | {% load i18n admin_modify adminmedia %} 4 | {% load templatesadmin_tags %} 5 | 6 | {% block extrahead %} 7 | {{ block.super }} 8 | {{ media }} 9 | {% endblock %} 10 | 11 | {% block extrastyle %} 12 | {{ block.super }} 13 | 14 | 22 | {% endblock %} 23 | 24 | {% block breadcrumbs %} 25 | 30 | {% endblock %} 31 | 32 | {% block title %} 33 | {% trans "Edit Template:" %} {{ template_path|shortenfilepath }} 34 | {% endblock %} 35 | 36 | {% block content_title %} 37 |

{% trans "Edit Template:" %} {{ template_path|shortenfilepath }}

38 | {% endblock %} 39 | 40 | {% block content %} 41 |
42 |
43 | {% csrf_token %} 44 |
45 | {{ form.errors }} 46 | {{ form.content }} 47 |
48 |
49 | {% if template_writeable %} 50 | {% for field in form %} 51 | {% ifnotequal field.label "Content" %} 52 |

53 | {{ field.label }} {{ field }} 54 |

55 | {% if forloop.last %} {% endif %} 56 |
57 | {% endifnotequal %} 58 | {% endfor %} 59 | {% else %} 60 | {% trans "This template is not writeable." %} 61 |
62 | {% endif %} 63 |
64 |
65 |
66 | {% endblock %} 67 | -------------------------------------------------------------------------------- /templatesadmin/templates/templatesadmin/overview.html: -------------------------------------------------------------------------------- 1 | {% extends "templatesadmin/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load templatesadmin_tags %} 5 | 6 | {% block breadcrumbs %} 7 | 11 | {% endblock %} 12 | 13 | {% block title %}Templatesadmin Overview{% endblock %} 14 | 15 | {% block content_title %} 16 |

{% trans "Templatesadmin Overview" %}

17 | {% endblock %} 18 | 19 | {% block content %} 20 | {% for template in template_dict %} 21 | {% ifchanged template.rootpath %} 22 | {% if not forloop.first %} 23 | 24 | 25 | 26 | {% endif %} 27 |
28 | 29 |

{{ template.rootpath }}

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {% endifchanged %} 39 | 40 | 43 | 44 | 45 | {% endfor %} 46 | 47 |
{% trans "File" %}{% trans "Modified" %}
41 | {% if not template.writeable %}{% trans "(read only)" %} {% endif %} 42 | {{ template.abspath|shortenfilepath }}{{ template.modified|date:_("DATETIME_FORMAT") }}
48 |
49 | {% endblock %} 50 | -------------------------------------------------------------------------------- /templatesadmin/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-templatesadmin/6b4b30f3bb76ee15edf0b8d177af4d3904418d5b/templatesadmin/templatetags/__init__.py -------------------------------------------------------------------------------- /templatesadmin/templatetags/templatesadmin_tags.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | from django.template import Library 3 | 4 | register = Library() 5 | 6 | @register.filter 7 | def shortenfilepath(path, num_dirs=2, pathsep='/'): 8 | splitted = path.split(path)[1] 9 | return pathsep.join(path.split(pathsep)[-num_dirs:]) 10 | -------------------------------------------------------------------------------- /templatesadmin/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls.defaults import * 2 | 3 | urlpatterns = patterns('', 4 | url(r'^$', 'templatesadmin.views.listing', name='templatesadmin-overview'), 5 | url(r'^edit(?P.*)/$', 'templatesadmin.views.modify', name='templatesadmin-edit'), 6 | ) 7 | -------------------------------------------------------------------------------- /templatesadmin/views.py: -------------------------------------------------------------------------------- 1 | import os 2 | import codecs 3 | from datetime import datetime 4 | from stat import ST_MTIME, ST_CTIME 5 | from re import search 6 | 7 | from django.conf import settings 8 | from django.contrib.auth.decorators import login_required, user_passes_test 9 | from django.core.exceptions import ImproperlyConfigured 10 | from django.core.exceptions import ObjectDoesNotExist 11 | from django.core.urlresolvers import reverse 12 | from django.http import HttpResponseRedirect 13 | from django.shortcuts import render_to_response 14 | from django.template import RequestContext 15 | from django.template.loaders.app_directories import app_template_dirs 16 | from django.utils.translation import ugettext as _ 17 | from django.views.decorators.cache import never_cache 18 | 19 | from templatesadmin.forms import TemplateForm 20 | from templatesadmin import TemplatesAdminException 21 | 22 | # Default settings that may be overriden by global settings (settings.py) 23 | TEMPLATESADMIN_VALID_FILE_EXTENSIONS = getattr( 24 | settings, 25 | 'TEMPLATESADMIN_VALID_FILE_EXTENSIONS', 26 | ('html', 'htm', 'txt', 'css', 'backup',) 27 | ) 28 | 29 | TEMPLATESADMIN_GROUP = getattr( 30 | settings, 31 | 'TEMPLATESADMIN_GROUP', 32 | 'TemplateAdmins' 33 | ) 34 | 35 | TEMPLATESADMIN_EDITHOOKS = getattr( 36 | settings, 37 | 'TEMPLATESADMIN_EDITHOOKS', 38 | ('templatesadmin.edithooks.dotbackupfiles.DotBackupFilesHook', ) 39 | ) 40 | 41 | TEMPLATESADMIN_HIDE_READONLY = getattr( 42 | settings, 43 | 'TEMPLATESADMIN_HIDE_READONLY', 44 | False 45 | ) 46 | 47 | if str == type(TEMPLATESADMIN_EDITHOOKS): 48 | TEMPLATESADMIN_EDITHOOKS = (TEMPLATESADMIN_EDITHOOKS,) 49 | 50 | _hooks = [] 51 | 52 | for path in TEMPLATESADMIN_EDITHOOKS: 53 | # inspired by django.template.context.get_standard_processors 54 | i = path.rfind('.') 55 | module, attr = path[:i], path[i+1:] 56 | try: 57 | mod = __import__(module, {}, {}, [attr]) 58 | except ImportError, e: 59 | raise ImproperlyConfigured('Error importing edithook module %s: "%s"' % (module, e)) 60 | try: 61 | func = getattr(mod, attr) 62 | except AttributeError: 63 | raise ImproperlyConfigured('Module "%s" does not define a "%s" callable request processor' % (module, attr)) 64 | 65 | _hooks.append(func) 66 | 67 | TEMPLATESADMIN_EDITHOOKS = tuple(_hooks) 68 | 69 | _fixpath = lambda path: os.path.abspath(os.path.normpath(path)) 70 | 71 | TEMPLATESADMIN_TEMPLATE_DIRS = getattr( 72 | settings, 73 | 'TEMPLATESADMIN_TEMPLATE_DIRS', [ 74 | d for d in list(settings.TEMPLATE_DIRS) + \ 75 | list(app_template_dirs) if os.path.isdir(d) 76 | ] 77 | ) 78 | 79 | TEMPLATESADMIN_TEMPLATE_DIRS = [_fixpath(dir) for dir in TEMPLATESADMIN_TEMPLATE_DIRS] 80 | 81 | def user_in_templatesadmin_group(user): 82 | try: 83 | user.groups.get(name=TEMPLATESADMIN_GROUP) 84 | return True 85 | except ObjectDoesNotExist: 86 | return False 87 | 88 | @never_cache 89 | @user_passes_test(lambda u: user_in_templatesadmin_group(u)) 90 | @login_required 91 | def listing(request, 92 | template_name='templatesadmin/overview.html', 93 | available_template_dirs=TEMPLATESADMIN_TEMPLATE_DIRS): 94 | 95 | template_dict = [] 96 | for templatedir in available_template_dirs: 97 | for root, dirs, files in os.walk(templatedir): 98 | for f in sorted([f for f in files if f.rsplit('.')[-1] \ 99 | in TEMPLATESADMIN_VALID_FILE_EXTENSIONS]): 100 | full_path = os.path.join(root, f) 101 | l = { 102 | 'templatedir': templatedir, 103 | 'rootpath': root, 104 | 'abspath': full_path, 105 | 'modified': datetime.fromtimestamp(os.stat(full_path)[ST_MTIME]), 106 | 'created': datetime.fromtimestamp(os.stat(full_path)[ST_CTIME]), 107 | 'writeable': os.access(full_path, os.W_OK) 108 | } 109 | 110 | # Do not fetch non-writeable templates if settings set. 111 | if (TEMPLATESADMIN_HIDE_READONLY == True and \ 112 | l['writeable'] == True) or \ 113 | TEMPLATESADMIN_HIDE_READONLY == False: 114 | try: 115 | template_dict += (l,) 116 | except KeyError: 117 | template_dict = (l,) 118 | 119 | template_context = { 120 | 'messages': request.user.get_and_delete_messages(), 121 | 'template_dict': template_dict, 122 | 'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX, 123 | } 124 | 125 | return render_to_response(template_name, template_context, 126 | RequestContext(request)) 127 | @never_cache 128 | @user_passes_test(lambda u: user_in_templatesadmin_group(u)) 129 | @login_required 130 | def modify(request, 131 | path, 132 | template_name='templatesadmin/edit.html', 133 | base_form=TemplateForm, 134 | available_template_dirs=TEMPLATESADMIN_TEMPLATE_DIRS): 135 | 136 | template_path = _fixpath(path) 137 | 138 | # Check if file is within template-dirs 139 | if not any([template_path.startswith(templatedir) for templatedir in available_template_dirs]): 140 | request.user.message_set.create(message=_('Sorry, that file is not available for editing.')) 141 | return HttpResponseRedirect(reverse('templatesadmin-overview')) 142 | 143 | if request.method == 'POST': 144 | formclass = base_form 145 | for hook in TEMPLATESADMIN_EDITHOOKS: 146 | formclass.base_fields.update(hook.contribute_to_form(template_path)) 147 | 148 | form = formclass(request.POST) 149 | if form.is_valid(): 150 | content = form.cleaned_data['content'] 151 | 152 | try: 153 | for hook in TEMPLATESADMIN_EDITHOOKS: 154 | pre_save_notice = hook.pre_save(request, form, template_path) 155 | if pre_save_notice: 156 | request.user.message_set.create(message=pre_save_notice) 157 | except TemplatesAdminException, e: 158 | request.user.message_set.create(message=e.message) 159 | return HttpResponseRedirect(request.build_absolute_uri()) 160 | 161 | # Save the template 162 | try: 163 | f = open(template_path, 'r') 164 | file_content = f.read() 165 | f.close() 166 | 167 | # browser tend to strip newlines from