Compare commits

..

11 Commits

Author SHA1 Message Date
Fabio Caccamo c07a846374 Updated README requirements and added badges 2017-03-29 16:32:52 +02:00
Fabio Caccamo f0126fb407 Updated version 2017-03-29 16:24:22 +02:00
Fabio Caccamo 869ae626da Added tox and travis 2017-03-29 16:22:34 +02:00
Fabio Caccamo a6bdb2b36d Updated setup.py 2017-03-29 16:22:09 +02:00
Fabio Caccamo aa18e19625 Added tests 2017-03-29 16:21:41 +02:00
Fabio Caccamo a35f8d0fb7 Dropped django < 1.7 support 2017-03-29 16:21:02 +02:00
Fabio Caccamo d29515c4ae Code refactoring 2017-03-29 16:20:07 +02:00
Fabio Caccamo 00f60c222d Fixed README syntax 2017-03-21 17:12:11 +01:00
Fabio Caccamo ff2d05e44c Fixed changelist filter header style 2017-02-16 11:30:44 +01:00
Fabio Caccamo 6e5cbf3a2c Fixed tools button focus color 2017-02-16 11:29:56 +01:00
Fabio Caccamo 55b365b2e4 Updated version 2017-02-09 16:25:25 +01:00
13 changed files with 271 additions and 129 deletions
+22
View File
@@ -0,0 +1,22 @@
language: python
python: 3.5
env:
- TOX_ENV=py27-17
- TOX_ENV=py27-18
- TOX_ENV=py27-19
- TOX_ENV=py27-110
- TOX_ENV=py34-17
- TOX_ENV=py34-18
- TOX_ENV=py34-19
- TOX_ENV=py34-110
- TOX_ENV=py35-18
- TOX_ENV=py35-19
- TOX_ENV=py35-110
#- TOX_ENV=py36-18
#- TOX_ENV=py36-19
#- TOX_ENV=py36-110
install:
- pip install tox
script:
- tox -e $TOX_ENV
sudo: false
+18 -12
View File
@@ -1,13 +1,19 @@
#django-admin-interface [![Build Status](https://travis-ci.org/fabiocaccamo/django-admin-interface.svg?branch=master)](https://travis-ci.org/fabiocaccamo/django-admin-interface)
[![codecov](https://codecov.io/gh/fabiocaccamo/django-admin-interface/branch/master/graph/badge.svg)](https://codecov.io/gh/fabiocaccamo/django-admin-interface)
[![PyPI version](https://badge.fury.io/py/django-admin-interface.svg)](https://badge.fury.io/py/django-admin-interface)
[![Py versions](https://img.shields.io/pypi/pyversions/django-admin-interface.svg)](https://img.shields.io/pypi/pyversions/django-admin-interface.svg)
[![License](https://img.shields.io/pypi/l/django-admin-interface.svg)](https://img.shields.io/pypi/l/django-admin-interface.svg)
# django-admin-interface
django-admin-interface is a modern **responsive flat admin interface customizable by the admin itself**. django-admin-interface is a modern **responsive flat admin interface customizable by the admin itself**.
You can use the builtin **django-theme** or create your own and **customize** **title, logo and colors**. You can use the builtin **django-theme** or create your own and **customize** **title, logo and colors**.
##Requirements ## Requirements
- Python 2.6, Python 2.7, Python 3.4 - Python 2.7, 3.4, 3.5, 3.6
- Django 1.6.5 through Django 1.10 - Django 1.7, 1.8, 1.9, 1.10
##Installation ## Installation
- Run `pip install django-admin-interface` - Run `pip install django-admin-interface`
- Add `admin_interface`, `flat_responsive`, `flat` and `colorfield` to `settings.INSTALLED_APPS` **before** `django.contrib.admin` - Add `admin_interface`, `flat_responsive`, `flat` and `colorfield` to `settings.INSTALLED_APPS` **before** `django.contrib.admin`
```python ```python
@@ -26,23 +32,23 @@ INSTALLED_APPS = (
- Run ``python manage.py collectstatic`` - Run ``python manage.py collectstatic``
- Restart your application server - Restart your application server
##Screenshots ## Screenshots
######Admin login ###### Admin login
![django-admin-interface_login](https://cloud.githubusercontent.com/assets/1035294/11240233/55c8d4ba-8df1-11e5-9568-00fdc987ede8.gif) ![django-admin-interface_login](https://cloud.githubusercontent.com/assets/1035294/11240233/55c8d4ba-8df1-11e5-9568-00fdc987ede8.gif)
--- ---
######Admin dashboard ###### Admin dashboard
![django-admin-interface_dashboard](https://cloud.githubusercontent.com/assets/1035294/11240239/627c0362-8df1-11e5-81fa-216366a5d8da.gif) ![django-admin-interface_dashboard](https://cloud.githubusercontent.com/assets/1035294/11240239/627c0362-8df1-11e5-81fa-216366a5d8da.gif)
--- ---
######Admin themes management ###### Admin themes management
![django-admin-interface_themes_management](https://cloud.githubusercontent.com/assets/1035294/11240245/6cd1c342-8df1-11e5-928b-f22217474d3d.gif) ![django-admin-interface_themes_management](https://cloud.githubusercontent.com/assets/1035294/11240245/6cd1c342-8df1-11e5-928b-f22217474d3d.gif)
--- ---
######Admin theme customization ###### Admin theme customization
![django-admin-interface_theme_customization](https://cloud.githubusercontent.com/assets/1035294/11240250/7350d942-8df1-11e5-9b28-f2f54c333cdc.gif) ![django-admin-interface_theme_customization](https://cloud.githubusercontent.com/assets/1035294/11240250/7350d942-8df1-11e5-9b28-f2f54c333cdc.gif)
####Thanks #### Thanks
- [django-flat-theme](https://github.com/elky/django-flat-theme/) - [django-flat-theme](https://github.com/elky/django-flat-theme/)
- [django-flat-responsive](https://github.com/elky/django-flat-responsive) - [django-flat-responsive](https://github.com/elky/django-flat-responsive)
- [django-colorfield](https://github.com/jaredly/django-colorfield/) - [django-colorfield](https://github.com/jaredly/django-colorfield/)
##License ## License
Released under [MIT License](LICENSE). Released under [MIT License](LICENSE).
+23 -19
View File
@@ -13,30 +13,35 @@ class Theme(models.Model):
@staticmethod @staticmethod
def post_migrate_handler(sender, **kwargs): def post_migrate_handler(sender, **kwargs):
Theme.get_or_create_default_theme() Theme.get_active_theme()
@staticmethod @staticmethod
def post_delete_handler(instance, **kwargs): def post_delete_handler(instance, **kwargs):
Theme.get_or_create_default_theme() Theme.get_active_theme()
@staticmethod @staticmethod
def post_save_handler(instance, created, **kwargs): def post_save_handler(instance, created, **kwargs):
instance.set_active(instance.active) Theme.get_active_theme()
Theme.get_or_create_default_theme()
@staticmethod @staticmethod
def get_or_create_default_theme(): def get_active_theme():
obj_active = (True if len(list(Theme.objects.filter(active = True))) == 0 else False) #get or create default theme and enforce default logo if deleted
obj, obj_created = Theme.objects.get_or_create(pk = '1', defaults = { 'active':obj_active }) default_obj_active = (Theme.objects.filter( active = True ).count() == 0)
default_obj, default_obj_created = Theme.objects.get_or_create(pk = '1', defaults = { 'active':default_obj_active })
if not obj.logo: if not default_obj_created and default_obj_active:
obj.set_default_logo() default_obj.set_active()
if not obj_created and obj_active: if not default_obj.logo:
obj.set_active(True) default_obj.set_default_logo()
objs_active_count = Theme.objects.filter( active = True ).count()
obj = Theme.objects.filter( active = True ).last()
obj.set_active()
return obj
return (obj, obj_created, )
name = models.CharField( max_length = 50, default = 'Django' ) name = models.CharField( max_length = 50, default = 'Django' )
active = models.BooleanField( default = True ) active = models.BooleanField( default = True )
@@ -74,15 +79,13 @@ class Theme(models.Model):
list_filter_dropdown = models.BooleanField( default = False ) list_filter_dropdown = models.BooleanField( default = False )
def set_active(self):
def set_active(self, value): Theme.objects.exclude( pk = self.pk ).update( active = False )
if value:
Theme.objects.exclude(pk = self.pk).update(active = False)
Theme.objects.filter(pk = self.pk).update(active = True)
else:
Theme.objects.filter(pk = self.pk).update(active = False)
if not self.active:
self.active = True
self.save()
def set_default_logo(self): def set_default_logo(self):
@@ -109,3 +112,4 @@ class Theme(models.Model):
post_delete.connect(Theme.post_delete_handler, sender = Theme) post_delete.connect(Theme.post_delete_handler, sender = Theme)
post_save.connect(Theme.post_save_handler, sender = Theme) post_save.connect(Theme.post_save_handler, sender = Theme)
@@ -1,82 +0,0 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Theme'
db.create_table(u'admin_interface_theme', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(default='Django', max_length=50)),
('active', self.gf('django.db.models.fields.BooleanField')(default=True)),
('title', self.gf('django.db.models.fields.CharField')(default='Django administration', max_length=50, blank=True)),
('title_visible', self.gf('django.db.models.fields.BooleanField')(default=True)),
('logo', self.gf('django.db.models.fields.files.FileField')(max_length=100, blank=True)),
('logo_visible', self.gf('django.db.models.fields.BooleanField')(default=True)),
('css_header_background_color', self.gf('colorfield.fields.ColorField')(default='#0C4B33', max_length=10, blank=True)),
('css_header_title_color', self.gf('colorfield.fields.ColorField')(default='#F5DD5D', max_length=10, blank=True)),
('css_header_text_color', self.gf('colorfield.fields.ColorField')(default='#44B78B', max_length=10, blank=True)),
('css_header_link_color', self.gf('colorfield.fields.ColorField')(default='#FFFFFF', max_length=10, blank=True)),
('css_header_link_hover_color', self.gf('colorfield.fields.ColorField')(default='#C9F0DD', max_length=10, blank=True)),
('css_module_background_color', self.gf('colorfield.fields.ColorField')(default='#44B78B', max_length=10, blank=True)),
('css_module_text_color', self.gf('colorfield.fields.ColorField')(default='#FFFFFF', max_length=10, blank=True)),
('css_module_link_color', self.gf('colorfield.fields.ColorField')(default='#FFFFFF', max_length=10, blank=True)),
('css_module_link_hover_color', self.gf('colorfield.fields.ColorField')(default='#C9F0DD', max_length=10, blank=True)),
('css_module_rounded_corners', self.gf('django.db.models.fields.BooleanField')(default=True)),
('css_generic_link_color', self.gf('colorfield.fields.ColorField')(default='#0C3C26', max_length=10, blank=True)),
('css_generic_link_hover_color', self.gf('colorfield.fields.ColorField')(default='#156641', max_length=10, blank=True)),
('css_save_button_background_color', self.gf('colorfield.fields.ColorField')(default='#0C4B33', max_length=10, blank=True)),
('css_save_button_background_hover_color', self.gf('colorfield.fields.ColorField')(default='#0C3C26', max_length=10, blank=True)),
('css_save_button_text_color', self.gf('colorfield.fields.ColorField')(default='#FFFFFF', max_length=10, blank=True)),
('css_delete_button_background_color', self.gf('colorfield.fields.ColorField')(default='#BA2121', max_length=10, blank=True)),
('css_delete_button_background_hover_color', self.gf('colorfield.fields.ColorField')(default='#A41515', max_length=10, blank=True)),
('css_delete_button_text_color', self.gf('colorfield.fields.ColorField')(default='#FFFFFF', max_length=10, blank=True)),
('css', self.gf('django.db.models.fields.TextField')(blank=True)),
('list_filter_dropdown', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal('admin_interface', ['Theme'])
def backwards(self, orm):
# Deleting model 'Theme'
db.delete_table(u'admin_interface_theme')
models = {
'admin_interface.theme': {
'Meta': {'object_name': 'Theme'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'css': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'css_delete_button_background_color': ('colorfield.fields.ColorField', [], {'default': "'#BA2121'", 'max_length': '10', 'blank': 'True'}),
'css_delete_button_background_hover_color': ('colorfield.fields.ColorField', [], {'default': "'#A41515'", 'max_length': '10', 'blank': 'True'}),
'css_delete_button_text_color': ('colorfield.fields.ColorField', [], {'default': "'#FFFFFF'", 'max_length': '10', 'blank': 'True'}),
'css_generic_link_color': ('colorfield.fields.ColorField', [], {'default': "'#0C3C26'", 'max_length': '10', 'blank': 'True'}),
'css_generic_link_hover_color': ('colorfield.fields.ColorField', [], {'default': "'#156641'", 'max_length': '10', 'blank': 'True'}),
'css_header_background_color': ('colorfield.fields.ColorField', [], {'default': "'#0C4B33'", 'max_length': '10', 'blank': 'True'}),
'css_header_link_color': ('colorfield.fields.ColorField', [], {'default': "'#FFFFFF'", 'max_length': '10', 'blank': 'True'}),
'css_header_link_hover_color': ('colorfield.fields.ColorField', [], {'default': "'#C9F0DD'", 'max_length': '10', 'blank': 'True'}),
'css_header_text_color': ('colorfield.fields.ColorField', [], {'default': "'#44B78B'", 'max_length': '10', 'blank': 'True'}),
'css_header_title_color': ('colorfield.fields.ColorField', [], {'default': "'#F5DD5D'", 'max_length': '10', 'blank': 'True'}),
'css_module_background_color': ('colorfield.fields.ColorField', [], {'default': "'#44B78B'", 'max_length': '10', 'blank': 'True'}),
'css_module_link_color': ('colorfield.fields.ColorField', [], {'default': "'#FFFFFF'", 'max_length': '10', 'blank': 'True'}),
'css_module_link_hover_color': ('colorfield.fields.ColorField', [], {'default': "'#C9F0DD'", 'max_length': '10', 'blank': 'True'}),
'css_module_rounded_corners': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'css_module_text_color': ('colorfield.fields.ColorField', [], {'default': "'#FFFFFF'", 'max_length': '10', 'blank': 'True'}),
'css_save_button_background_color': ('colorfield.fields.ColorField', [], {'default': "'#0C4B33'", 'max_length': '10', 'blank': 'True'}),
'css_save_button_background_hover_color': ('colorfield.fields.ColorField', [], {'default': "'#0C3C26'", 'max_length': '10', 'blank': 'True'}),
'css_save_button_text_color': ('colorfield.fields.ColorField', [], {'default': "'#FFFFFF'", 'max_length': '10', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'list_filter_dropdown': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'logo': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
'logo_visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'default': "'Django'", 'max_length': '50'}),
'title': ('django.db.models.fields.CharField', [], {'default': "'Django administration'", 'max_length': '50', 'blank': 'True'}),
'title_visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
}
}
complete_apps = ['admin_interface']
@@ -259,7 +259,9 @@
} }
/* top-right buttons color on hover -> just a lighten grey */ /* top-right buttons color on hover -> just a lighten grey */
.object-tools a:focus,
.object-tools a:hover, .object-tools a:hover,
.object-tools li:focus a,
.object-tools li:hover a { .object-tools li:hover a {
background-color:#AAAAAA; background-color:#AAAAAA;
} }
@@ -341,6 +343,11 @@
border-bottom-right-radius:4px; border-bottom-right-radius:4px;
} }
.module.filtered #changelist-filter h2 {
font-size: 11px;
padding: 10px 15px;
}
.module.filtered #changelist-filter select { .module.filtered #changelist-filter select {
background-color:#ffffff; background-color:#ffffff;
} }
@@ -11,14 +11,5 @@ register = template.Library()
@register.assignment_tag(takes_context = True) @register.assignment_tag(takes_context = True)
def get_admin_interface_theme(context): def get_admin_interface_theme(context):
obj_qs = Theme.objects.filter(active = True)[:1] return Theme.get_active_theme()
obj_ls = list(obj_qs)
obj = None
if len(obj_ls):
obj = obj_ls[0]
else:
obj = Theme.get_or_create_default_theme()
return obj
+1 -1
View File
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
__version__ = '0.2.0' __version__ = '0.3.0'
Executable
+21
View File
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
import os
import sys
import django
from django.conf import settings
from django.test.utils import get_runner
def runtests():
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings'
django.setup()
TestRunner = get_runner(settings)
test_runner = TestRunner()
failures = test_runner.run_tests(['tests'])
sys.exit(bool(failures))
if __name__ == '__main__':
runtests()
+26 -5
View File
@@ -1,14 +1,13 @@
#!/usr/bin/env python #!/usr/bin/env python
from setuptools import find_packages, setup from setuptools import find_packages, setup
exec(open('admin_interface/version.py').read()) exec(open('admin_interface/version.py').read())
setup( setup(
name='django-admin-interface', name='django-admin-interface',
packages=find_packages(), packages=find_packages(exclude=['contrib', 'docs', 'tests*']),
include_package_data=True, include_package_data=True,
license='MIT License',
version=__version__, version=__version__,
description='django-admin-interface is a modern responsive flat admin interface customizable by the admin itself.', description='django-admin-interface is a modern responsive flat admin interface customizable by the admin itself.',
author='Fabio Caccamo', author='Fabio Caccamo',
@@ -16,11 +15,33 @@ setup(
url='https://github.com/fabiocaccamo/django-admin-interface', url='https://github.com/fabiocaccamo/django-admin-interface',
download_url='https://github.com/fabiocaccamo/django-admin-interface/archive/%s.tar.gz' % __version__, download_url='https://github.com/fabiocaccamo/django-admin-interface/archive/%s.tar.gz' % __version__,
keywords=['django', 'admin', 'interface', 'responsive', 'flat', 'theme', 'custom', 'ui'], keywords=['django', 'admin', 'interface', 'responsive', 'flat', 'theme', 'custom', 'ui'],
requires=['django(>=1.7)'],
install_requires=[ install_requires=[
'django-colorfield==0.1.12', 'django-colorfield==0.1.12',
'django-flat-theme==1.1.4', 'django-flat-theme==1.1.4',
'django-flat-responsive==1.2.0' 'django-flat-responsive==1.3.0'
], ],
classifiers=[] classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Framework :: Django',
'Framework :: Django :: 1.7',
'Framework :: Django :: 1.8',
'Framework :: Django :: 1.9',
'Framework :: Django :: 1.10',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Software Development :: Build Tools',
],
license='MIT',
test_suite='runtests.runtests'
) )
+50
View File
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
import django
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = 'django-admin-interface'
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = [
'admin_interface',
'flat_responsive',
'flat', #if django version < 1.9
'colorfield',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
]
MIDDLEWARE_CLASSES = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
]
TEMPLATES = [{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {},
},]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
MEDIA_ROOT = os.path.join(BASE_DIR, 'admin_interface/public/media/')
MEDIA_URL = '/media/'
STATIC_ROOT = os.path.join(BASE_DIR, 'admin_interface/public/static/')
STATIC_URL = '/static/'
+83
View File
@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
from django.conf import settings
from django.test import TestCase
from django.template import Context, Template
import os
import shutil
from admin_interface.models import Theme
class AdminInterfaceTestCase(TestCase):
def setUp(self):
pass
def tearDown(self):
shutil.rmtree(settings.MEDIA_ROOT, ignore_errors=True)
pass
def __render_template(self, string, context=None):
context = context or {}
context = Context(context)
return Template(string).render(context)
def __test_active_theme(self):
theme = Theme.get_active_theme()
print( theme )
self.assertTrue(theme != None)
self.assertTrue(theme.active)
self.assertEqual(Theme.objects.filter( active = True ).count(), 1);
def test_default_theme_created_if_no_themes(self):
Theme.objects.all().delete()
self.__test_active_theme()
def test_default_theme_created_if_all_themes_deleted(self):
Theme.objects.all().delete()
self.__test_active_theme()
def test_default_theme_activated_on_save_if_no_active_themes(self):
Theme.objects.all().delete()
theme = Theme.get_active_theme()
theme.active = False
theme.save()
self.__test_active_theme()
def test_default_theme_activated_after_update_if_no_active_themes(self):
Theme.objects.all().delete()
Theme.objects.all().update( active = False )
self.__test_active_theme()
def test_default_theme_activated_on_active_theme_deleted(self):
Theme.objects.all().delete()
theme_1 = Theme.objects.create( name = 'Custom 1', active = True )
theme_2 = Theme.objects.create( name = 'Custom 2', active = True )
theme_3 = Theme.objects.create( name = 'Custom 3', active = True )
Theme.objects.filter( pk = Theme.get_active_theme().pk ).delete()
self.assertEqual( Theme.get_active_theme().pk, 1 )
def test_last_theme_activated_on_multiple_themes_created(self):
Theme.objects.all().delete()
theme_1 = Theme.objects.create( name = 'Custom 1', active = True )
theme_2 = Theme.objects.create( name = 'Custom 2', active = True )
theme_3 = Theme.objects.create( name = 'Custom 3', active = True )
self.assertEqual( Theme.get_active_theme().pk, theme_3.pk )
self.__test_active_theme()
def test_templatetags(self):
Theme.objects.all().delete()
rendered = self.__render_template('{% load admin_interface_tags %}{% get_admin_interface_theme as theme %}{{ theme.name }}')
self.assertEqual(rendered, 'Django')
+19
View File
@@ -0,0 +1,19 @@
[tox]
envlist =
py27-{17,18,19,110},
py34-{17,18,19,110},
py35-{18,19,110},
py36-{18,19,110},
[testenv]
passenv = CI TRAVIS TRAVIS_*
deps =
17: Django >= 1.7, < 1.8
18: Django >= 1.8, < 1.9
19: Django >= 1.9, < 1.10
110: Django >= 1.10, < 1.11
coverage
codecov
commands =
coverage run --append --source=admin_interface setup.py test
coverage report -m
codecov