2020-04-02 18:29:27 +00:00
import os
import tempfile
import zipfile
import shutil
2019-03-19 19:19:23 +00:00
from django . conf . urls import url
2016-08-10 20:23:17 +00:00
from django . contrib import admin
2019-07-09 22:08:28 +00:00
from django . contrib import messages
2019-03-19 19:19:23 +00:00
from django . http import HttpResponseRedirect
from django . urls import reverse
from django . utils . html import format_html
2016-10-04 20:36:08 +00:00
from guardian . admin import GuardedModelAdmin
2017-07-21 20:48:01 +00:00
2018-07-30 16:44:16 +00:00
from app . models import PluginDatum
2017-07-21 20:48:01 +00:00
from app . models import Preset
2019-03-19 19:19:23 +00:00
from app . models import Plugin
2020-04-02 18:29:27 +00:00
from app . plugins import get_plugin_by_name , enable_plugin , disable_plugin , delete_plugin , valid_plugin , \
get_plugins_persistent_path , clear_plugins_cache , init_plugins
2017-11-06 18:45:14 +00:00
from . models import Project , Task , ImageUpload , Setting , Theme
2017-11-06 22:43:47 +00:00
from django import forms
from codemirror2 . widgets import CodeMirrorEditor
2020-04-02 18:29:27 +00:00
from webodm import settings
from django . core . files . uploadedfile import InMemoryUploadedFile
2020-12-18 17:32:44 +00:00
from django . utils . translation import gettext_lazy as _
2016-10-04 20:36:08 +00:00
2016-10-12 22:18:37 +00:00
admin . site . register ( Project , GuardedModelAdmin )
2017-07-10 17:32:36 +00:00
class TaskAdmin ( admin . ModelAdmin ) :
def has_add_permission ( self , request ) :
return False
admin . site . register ( Task , TaskAdmin )
2016-10-13 20:28:32 +00:00
class ImageUploadAdmin ( admin . ModelAdmin ) :
readonly_fields = ( ' image ' , )
admin . site . register ( ImageUpload , ImageUploadAdmin )
2017-07-21 20:48:01 +00:00
admin . site . register ( Preset , admin . ModelAdmin )
2017-11-06 18:45:14 +00:00
class SettingAdmin ( admin . ModelAdmin ) :
2017-11-06 22:43:47 +00:00
2017-11-06 18:45:14 +00:00
def has_add_permission ( self , request ) :
# if there's already an entry, do not allow adding
count = Setting . objects . all ( ) . count ( )
return count == 0
admin . site . register ( Setting , SettingAdmin )
2017-11-06 22:43:47 +00:00
class ThemeModelForm ( forms . ModelForm ) :
2020-12-18 17:32:44 +00:00
css = forms . CharField ( help_text = _ ( " Enter custom CSS " ) ,
label = _ ( " CSS " ) ,
2017-11-06 22:43:47 +00:00
required = False ,
widget = CodeMirrorEditor ( options = { ' mode ' : ' css ' , ' lineNumbers ' : True } ) )
2020-12-18 17:32:44 +00:00
html_before_header = forms . CharField ( help_text = _ ( " HTML that will be displayed above site header " ) ,
label = _ ( " HTML (before header) " ) ,
2017-11-06 22:43:47 +00:00
required = False ,
widget = CodeMirrorEditor ( options = { ' mode ' : ' xml ' , ' lineNumbers ' : True } ) )
2020-12-18 17:32:44 +00:00
html_after_header = forms . CharField ( help_text = _ ( " HTML that will be displayed after site header " ) ,
label = _ ( " HTML (after header) " ) ,
2017-11-06 22:43:47 +00:00
required = False ,
widget = CodeMirrorEditor ( options = { ' mode ' : ' xml ' , ' lineNumbers ' : True } ) )
2020-12-18 21:54:00 +00:00
html_after_body = forms . CharField ( help_text = _ ( " HTML that will be displayed after the body tag " ) ,
2020-12-18 17:32:44 +00:00
label = _ ( " HTML (after body) " ) ,
2017-11-06 22:43:47 +00:00
required = False ,
widget = CodeMirrorEditor ( options = { ' mode ' : ' xml ' , ' lineNumbers ' : True } ) )
2020-12-18 17:32:44 +00:00
html_footer = forms . CharField ( help_text = _ ( " HTML that will be displayed in the footer. You can also use the special tags such as {ORGANIZATION} and {YEAR} . " ) ,
label = _ ( " HTML (footer) " ) ,
2017-11-06 22:43:47 +00:00
required = False ,
widget = CodeMirrorEditor ( options = { ' mode ' : ' xml ' , ' lineNumbers ' : True } ) )
class Meta :
model = Theme
fields = ' __all__ '
class ThemeAdmin ( admin . ModelAdmin ) :
form = ThemeModelForm
2017-11-07 21:48:27 +00:00
2018-07-30 16:44:16 +00:00
admin . site . register ( Theme , ThemeAdmin )
2019-03-19 19:19:23 +00:00
admin . site . register ( PluginDatum , admin . ModelAdmin )
2018-07-30 16:44:16 +00:00
2019-03-19 19:19:23 +00:00
class PluginAdmin ( admin . ModelAdmin ) :
list_display = ( " name " , " description " , " version " , " author " , " enabled " , " plugin_actions " )
readonly_fields = ( " name " , )
2020-03-31 20:32:14 +00:00
change_list_template = " admin/change_list_plugin.html "
2019-03-19 19:19:23 +00:00
def has_add_permission ( self , request ) :
return False
def has_delete_permission ( self , request , obj = None ) :
return False
def description ( self , obj ) :
2019-03-29 17:12:43 +00:00
manifest = get_plugin_by_name ( obj . name , only_active = False , refresh_cache_if_none = True ) . get_manifest ( )
2019-03-19 19:19:23 +00:00
return manifest . get ( ' description ' , ' ' )
def version ( self , obj ) :
2019-03-29 17:12:43 +00:00
manifest = get_plugin_by_name ( obj . name , only_active = False , refresh_cache_if_none = True ) . get_manifest ( )
2019-03-19 19:19:23 +00:00
return manifest . get ( ' version ' , ' ' )
def author ( self , obj ) :
2019-03-29 17:12:43 +00:00
manifest = get_plugin_by_name ( obj . name , only_active = False , refresh_cache_if_none = True ) . get_manifest ( )
2019-03-19 19:19:23 +00:00
return manifest . get ( ' author ' , ' ' )
def get_urls ( self ) :
urls = super ( ) . get_urls ( )
custom_urls = [
url (
r ' ^(?P<plugin_name>.+)/enable/$ ' ,
self . admin_site . admin_view ( self . plugin_enable ) ,
name = ' plugin-enable ' ,
) ,
url (
r ' ^(?P<plugin_name>.+)/disable/$ ' ,
self . admin_site . admin_view ( self . plugin_disable ) ,
name = ' plugin-disable ' ,
) ,
2020-03-31 18:02:19 +00:00
url (
r ' ^(?P<plugin_name>.+)/delete/$ ' ,
self . admin_site . admin_view ( self . plugin_delete ) ,
name = ' plugin-delete ' ,
) ,
2020-03-31 20:32:14 +00:00
url (
r ' ^actions/upload/$ ' ,
self . admin_site . admin_view ( self . plugin_upload ) ,
name = ' plugin-upload ' ,
) ,
2019-03-19 19:19:23 +00:00
]
return custom_urls + urls
def plugin_enable ( self , request , plugin_name , * args , * * kwargs ) :
2019-07-09 22:08:28 +00:00
try :
enable_plugin ( plugin_name )
except Exception as e :
messages . warning ( request , " Cannot enable plugin {} : {} " . format ( plugin_name , str ( e ) ) )
2019-03-19 19:19:23 +00:00
return HttpResponseRedirect ( reverse ( ' admin:app_plugin_changelist ' ) )
def plugin_disable ( self , request , plugin_name , * args , * * kwargs ) :
2019-07-09 22:08:28 +00:00
try :
disable_plugin ( plugin_name )
except Exception as e :
messages . warning ( request , " Cannot disable plugin {} : {} " . format ( plugin_name , str ( e ) ) )
2019-03-19 19:19:23 +00:00
return HttpResponseRedirect ( reverse ( ' admin:app_plugin_changelist ' ) )
2020-03-31 18:02:19 +00:00
def plugin_delete ( self , request , plugin_name , * args , * * kwargs ) :
try :
delete_plugin ( plugin_name )
except Exception as e :
messages . warning ( request , " Cannot delete plugin {} : {} " . format ( plugin_name , str ( e ) ) )
return HttpResponseRedirect ( reverse ( ' admin:app_plugin_changelist ' ) )
2020-03-31 20:32:14 +00:00
def plugin_upload ( self , request , * args , * * kwargs ) :
2020-04-02 18:29:27 +00:00
file = request . FILES . get ( ' file ' )
if file is not None :
# Save to tmp dir
tmp_zip_path = tempfile . mktemp ( ' plugin.zip ' , dir = settings . MEDIA_TMP )
tmp_extract_path = tempfile . mkdtemp ( ' plugin ' , dir = settings . MEDIA_TMP )
try :
with open ( tmp_zip_path , ' wb+ ' ) as fd :
if isinstance ( file , InMemoryUploadedFile ) :
for chunk in file . chunks ( ) :
fd . write ( chunk )
else :
with open ( file . temporary_file_path ( ) , ' rb ' ) as f :
shutil . copyfileobj ( f , fd )
# Extract
with zipfile . ZipFile ( tmp_zip_path , " r " ) as zip_h :
zip_h . extractall ( tmp_extract_path )
# Validate
folders = os . listdir ( tmp_extract_path )
if len ( folders ) != 1 :
raise ValueError ( " The plugin has more than 1 root directory (it should have only one) " )
plugin_name = folders [ 0 ]
plugin_path = os . path . join ( tmp_extract_path , plugin_name )
if not valid_plugin ( plugin_path ) :
raise ValueError ( " This doesn ' t look like a plugin. Are plugin.py and manifest.json in the proper place? " )
if os . path . exists ( get_plugins_persistent_path ( plugin_name ) ) :
raise ValueError ( " A plugin with the name {} already exist. Please remove it before uploading one with the same name. " . format ( plugin_name ) )
# Move
shutil . move ( plugin_path , get_plugins_persistent_path ( ) )
# Initialize
clear_plugins_cache ( )
init_plugins ( )
messages . info ( request , " Plugin added successfully " )
except Exception as e :
messages . warning ( request , " Cannot load plugin: {} " . format ( str ( e ) ) )
if os . path . exists ( tmp_zip_path ) :
os . remove ( tmp_zip_path )
if os . path . exists ( tmp_extract_path ) :
shutil . rmtree ( tmp_extract_path )
else :
messages . error ( request , " You need to upload a zip file " )
2020-03-31 20:32:14 +00:00
return HttpResponseRedirect ( reverse ( ' admin:app_plugin_changelist ' ) )
2020-03-31 18:02:19 +00:00
2019-03-19 19:19:23 +00:00
def plugin_actions ( self , obj ) :
2020-03-31 18:02:19 +00:00
plugin = get_plugin_by_name ( obj . name , only_active = False )
2019-03-19 19:19:23 +00:00
return format_html (
' <a class= " button " href= " {} " {} >Disable</a> '
2020-03-31 18:02:19 +00:00
' <a class= " button " href= " {} " {} >Enable</a> '
+ ( ' <a class= " button " href= " {} " onclick= " return confirm( \' Are you sure you want to delete {} ? \' ) " ><i class= " fa fa-trash " ></i></a> ' if not plugin . is_persistent ( ) else ' ' )
,
2019-03-19 19:19:23 +00:00
reverse ( ' admin:plugin-disable ' , args = [ obj . pk ] ) if obj . enabled else ' # ' ,
' disabled ' if not obj . enabled else ' ' ,
reverse ( ' admin:plugin-enable ' , args = [ obj . pk ] ) if not obj . enabled else ' # ' ,
' disabled ' if obj . enabled else ' ' ,
2020-03-31 18:02:19 +00:00
reverse ( ' admin:plugin-delete ' , args = [ obj . pk ] ) ,
obj . name
2019-03-19 19:19:23 +00:00
)
plugin_actions . short_description = ' Actions '
plugin_actions . allow_tags = True
admin . site . register ( Plugin , PluginAdmin )