forked from daadu/django-admintool-command
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
341 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# -*- coding: utf-8 -*- | ||
__version__ = '0.1' # pragma: no cover |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from abc import ABC, abstractmethod | ||
|
||
from django import forms | ||
from django.core.management import BaseCommand | ||
|
||
|
||
class AdminCommand(BaseCommand, ABC): | ||
name = None | ||
template = "admintool_command/command.html" | ||
|
||
class Form(forms.Form): | ||
pass | ||
|
||
def init_context(self, request=None, **kwargs): | ||
return dict() | ||
|
||
@abstractmethod | ||
def get_command_arguments(self, forms_data): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from admin_tools.dashboard import modules | ||
from admin_tools.menu.items import MenuItem | ||
from admin_tools.utils import AppListElementMixin | ||
from django.conf import settings | ||
from django.urls import reverse | ||
from django.utils.safestring import mark_safe | ||
|
||
from admintool_command.utils import get_command_instance | ||
|
||
|
||
class AdminCommandMenu(MenuItem, AppListElementMixin): | ||
title = "Commands" | ||
|
||
def init_with_context(self, context): | ||
|
||
for app_name, commands in settings.ADMIN_TOOLS_COMMANDS.items(): | ||
children = [] | ||
for command in commands: | ||
instance = get_command_instance(app_name, command) | ||
|
||
title = mark_safe(instance.name if instance.name else "%s" % command) | ||
url = reverse("admintool_command:command", kwargs={"app_name": app_name, "command": command}) | ||
|
||
children.append(MenuItem(title=title, url=url)) | ||
self.children.append(MenuItem(app_name, children=children)) | ||
|
||
if not len(self.children): | ||
self.enabled = False | ||
|
||
def is_selected(self, request): | ||
return False | ||
|
||
|
||
class AdminCommandModule(modules.Group): | ||
title = "Commands" | ||
|
||
enabled = True | ||
display = "stacked" | ||
|
||
def init_with_context(self, context): | ||
for app_name, commands in settings.ADMIN_TOOLS_COMMANDS.items(): | ||
children = [] | ||
for command in commands: | ||
instance = get_command_instance(app_name, command) | ||
|
||
children.append({'title': mark_safe(instance.name if instance.name else command), | ||
'url': reverse("admintool_command:command", | ||
kwargs={"app_name": app_name, "command": command}) | ||
}) | ||
|
||
self.children.append(modules.LinkList(title=app_name, draggable=False, children=children)) | ||
|
||
def is_empty(self): | ||
return len(self.children) == 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""Models for the admintool_command app.""" |
48 changes: 48 additions & 0 deletions
48
build/lib/admintool_command/templates/admintool_command/command.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
{% extends "admin/base_site.html" %} | ||
{% load i18n admin_urls static admin_modify %} | ||
|
||
{% block extrahead %}{{ block.super }} | ||
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script> | ||
{{ media }} | ||
{% endblock %} | ||
|
||
{% block extrastyle %}{{ block.super }} | ||
<link rel="stylesheet" type="text/css" href="{% static " admin/css/forms.css" %}"> | ||
{% endblock %} | ||
|
||
{% block coltype %}colM{% endblock %} | ||
|
||
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %} | ||
|
||
|
||
{% block breadcrumbs %} | ||
<div class="breadcrumbs"> | ||
<a href="{% url 'admin:index' %}">Home</a> | ||
› commands › {{ app_name }} › {{ title }} | ||
</div> | ||
{% endblock %} | ||
|
||
{% block messages %} | ||
{% if messages %} | ||
<ul class="messagelist">{% for message in messages %} | ||
<li | ||
{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message|safe }}</li> | ||
{% endfor %} | ||
</ul> | ||
{% endif %} | ||
{% endblock messages %} | ||
|
||
{% block content %} | ||
<div id="content-main"> | ||
<form action="." method="POST"> | ||
{% csrf_token %} | ||
<table> | ||
{{ form.as_table }} | ||
<tr> | ||
<td><input type="submit" value="Run"/></td> | ||
</tr> | ||
</table> | ||
</form> | ||
</div> | ||
|
||
{% endblock %} |
Empty file.
12 changes: 12 additions & 0 deletions
12
build/lib/admintool_command/templatetags/admintool_command_tags.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
"""Templatetags for the admintool_command app.""" | ||
from django import template | ||
|
||
register = template.Library() | ||
|
||
# @register.filter | ||
# def lower(value): | ||
# """ | ||
# Converts a string into all lowercase | ||
# | ||
# """ | ||
# return value.lower() |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
"""Tests for the models of the admintool_command app.""" | ||
# from django.test import TestCase | ||
|
||
# from mixer.backend.django import mixer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
""" | ||
These settings are used by the ``manage.py`` command. | ||
With normal tests we want to use the fastest possible way which is an | ||
in-memory sqlite database but if you want to create South migrations you | ||
need a persistant database. | ||
Unfortunately there seems to be an issue with either South or syncdb so that | ||
defining two routers ("default" and "south") does not work. | ||
""" | ||
from distutils.version import StrictVersion | ||
|
||
import django | ||
|
||
from .test_settings import * # NOQA | ||
|
||
|
||
DATABASES = { | ||
'default': { | ||
'ENGINE': 'django.db.backends.sqlite3', | ||
'NAME': 'db.sqlite', | ||
} | ||
} | ||
|
||
django_version = django.get_version() | ||
if StrictVersion(django_version) < StrictVersion('1.7'): | ||
INSTALLED_APPS.append('south', ) |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
ERROR: 400 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
ERROR: 500 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
"""Settings that need to be set in order to run the tests.""" | ||
import os | ||
|
||
|
||
DEBUG = True | ||
SITE_ID = 1 | ||
|
||
APP_ROOT = os.path.abspath( | ||
os.path.join(os.path.dirname(__file__), '..')) | ||
|
||
|
||
DATABASES = { | ||
'default': { | ||
'ENGINE': 'django.db.backends.sqlite3', | ||
'NAME': ':memory:', | ||
} | ||
} | ||
|
||
ROOT_URLCONF = 'admintool_command.tests.urls' | ||
|
||
STATIC_URL = '/static/' | ||
STATIC_ROOT = os.path.join(APP_ROOT, '../app_static') | ||
MEDIA_ROOT = os.path.join(APP_ROOT, '../app_media') | ||
STATICFILES_DIRS = ( | ||
os.path.join(APP_ROOT, 'static'), | ||
) | ||
|
||
TEMPLATES = [{ | ||
'BACKEND': 'django.template.backends.django.DjangoTemplates', | ||
'APP_DIRS': True, | ||
'DIRS': [os.path.join(APP_ROOT, 'tests/test_app/templates')], | ||
'OPTIONS': { | ||
'context_processors': ( | ||
'django.contrib.auth.context_processors.auth', | ||
'django.template.context_processors.request', | ||
'django.contrib.messages.context_processors.messages', | ||
) | ||
} | ||
}] | ||
|
||
EXTERNAL_APPS = [ | ||
'django.contrib.admin', | ||
'django.contrib.admindocs', | ||
'django.contrib.auth', | ||
'django.contrib.contenttypes', | ||
'django.contrib.messages', | ||
'django.contrib.sessions', | ||
'django.contrib.staticfiles', | ||
'django.contrib.sitemaps', | ||
'django.contrib.sites', | ||
] | ||
|
||
INTERNAL_APPS = [ | ||
'admintool_command', | ||
'admintool_command.tests.test_app', | ||
] | ||
|
||
INSTALLED_APPS = EXTERNAL_APPS + INTERNAL_APPS | ||
|
||
MIDDLEWARE_CLASSES = [ | ||
'django.contrib.sessions.middleware.SessionMiddleware', | ||
'django.middleware.common.CommonMiddleware', | ||
'django.contrib.auth.middleware.AuthenticationMiddleware', | ||
'django.middleware.csrf.CsrfViewMiddleware', | ||
'django.contrib.messages.middleware.MessageMiddleware', | ||
] | ||
|
||
MIDDLEWARE = MIDDLEWARE_CLASSES | ||
|
||
SECRET_KEY = 'foobar' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
"""URLs to run the tests.""" | ||
from compat import include, url | ||
from django.contrib import admin | ||
|
||
|
||
admin.autodiscover() | ||
|
||
urlpatterns = [ | ||
url(r'^admin/', admin.site.urls), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from django.urls import path | ||
|
||
from .views import AppCommandView | ||
|
||
app_name = 'admintool_command' | ||
urlpatterns = [ | ||
path(r'<str:app_name>/<str:command>/', AppCommandView.as_view(), name="command"), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
def get_full_class_name(app_name, command): | ||
return "%s.management.commands.%s.Command" % (app_name, command) | ||
|
||
|
||
def get_command_instance(app_name, command): | ||
full_class_name = get_full_class_name(app_name, command) | ||
module_name = full_class_name.split(".") | ||
class_name = module_name.pop() | ||
|
||
from importlib import import_module | ||
_module = import_module(".".join(module_name)) | ||
_class = getattr(_module, class_name) | ||
|
||
return _class() | ||
|
||
|
||
def colourstrip(data): | ||
while True: | ||
find = data.find('\x1b') | ||
if find <= -1: | ||
break | ||
data = data[:find] + data[find + 5:] | ||
find = data.find('\x1b') | ||
data = data[:find] + data[find+4:] | ||
data = data.replace("[31m", "") | ||
return data.strip() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import traceback | ||
from django.contrib.auth.mixins import UserPassesTestMixin | ||
from django.contrib.messages.storage.base import Message | ||
from django.core.management import call_command | ||
from django.http import HttpResponse | ||
from django.shortcuts import render | ||
from django.utils.safestring import mark_safe | ||
from django.views import View | ||
from io import StringIO | ||
|
||
from admintool_command.utils import get_command_instance, colourstrip | ||
|
||
|
||
class AppCommandView(UserPassesTestMixin, View): | ||
def test_func(self): | ||
return self.request.user and self.request.user.is_superuser | ||
|
||
def get(self, request, app_name, command): | ||
instance = get_command_instance(app_name, command) | ||
# handle from AdminCommand if has view handler | ||
if hasattr(instance, "view"): | ||
return instance.view(request=request, app_name=app_name, command=command) | ||
|
||
context = instance.init_context(request=request, app_name=app_name, command=command) | ||
context["title"] = mark_safe(instance.name if instance.name else "%s" % command) | ||
context["app_name"] = app_name | ||
context["form"] = instance.Form() | ||
return HttpResponse(render(request, instance.template, context=context)) | ||
|
||
def post(self, request, app_name, command): | ||
instance = get_command_instance(app_name, command) | ||
# handle from AdminCommand if has view handler | ||
if hasattr(instance, "view"): | ||
return instance.view(request=request, app_name=app_name, command=command) | ||
|
||
form = instance.Form(request.POST) | ||
context = instance.init_context(request=request, app_name=app_name, command=command) | ||
context["title"] = mark_safe(instance.name if instance.name else "%s" % command) | ||
context["app_name"] = app_name | ||
context["form"] = form | ||
if form.is_valid(): | ||
args, options = instance.get_command_arguments(forms_data=form.cleaned_data) | ||
output = StringIO() | ||
extra_tags = "" | ||
try: | ||
call_command(command, stdout=output, no_color=True, *args, **options) | ||
except Exception as e: | ||
output.write(traceback.format_exc()) | ||
output.seek(0) | ||
extra_tags = "error" | ||
message = colourstrip(output.getvalue().replace("\n", '<br/>')) if output.getvalue() else "Done" | ||
context["messages"] = [ | ||
Message(level=0, extra_tags=extra_tags, message=message)] | ||
return HttpResponse(render(request, instance.template, context=context)) | ||
context["messages"] = [Message(level=0, extra_tags="error", message="Invalid form data")] | ||
return HttpResponse(render(request, instance.template, context=context)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters