2018-01-30 16:48:16 +00:00
import os
import logging
import importlib
2018-03-19 16:26:10 +00:00
import subprocess
2018-01-30 16:48:16 +00:00
import django
2018-02-09 18:46:45 +00:00
import json
2018-01-30 16:48:16 +00:00
from django . conf . urls import url
2018-02-09 18:46:45 +00:00
from functools import reduce
2018-07-26 19:14:48 +00:00
from string import Template
from django . http import HttpResponse
2018-02-09 18:46:45 +00:00
2018-07-27 18:18:03 +00:00
from app . models import Setting
2018-02-09 18:46:45 +00:00
from webodm import settings
2018-01-30 16:48:16 +00:00
logger = logging . getLogger ( ' app.logger ' )
def register_plugins ( ) :
for plugin in get_active_plugins ( ) :
2018-03-19 16:26:10 +00:00
# Check for package.json in public directory
# and run npm install if needed
if plugin . path_exists ( " public/package.json " ) and not plugin . path_exists ( " public/node_modules " ) :
2018-07-26 19:14:48 +00:00
logger . info ( " Running npm install for {} " . format ( plugin ) )
2018-03-19 16:26:10 +00:00
subprocess . call ( [ ' npm ' , ' install ' ] , cwd = plugin . get_path ( " public " ) )
2018-07-26 19:14:48 +00:00
# Check if we need to generate a webpack.config.js
if len ( plugin . build_jsx_components ( ) ) > 0 and plugin . path_exists ( ' public ' ) :
logger . info ( " Generating webpack.config.js for {} " . format ( plugin ) )
build_paths = map ( lambda p : os . path . join ( plugin . get_path ( ' public ' ) , p ) , plugin . build_jsx_components ( ) )
paths_ok = not ( False in map ( lambda p : os . path . exists , build_paths ) )
if paths_ok :
wpc_path = os . path . join ( settings . BASE_DIR , ' app ' , ' plugins ' , ' templates ' , ' webpack.config.js.tmpl ' )
with open ( wpc_path ) as f :
tmpl = Template ( f . read ( ) )
# Create entry configuration
entry = { }
for e in plugin . build_jsx_components ( ) :
entry [ os . path . splitext ( os . path . basename ( e ) ) [ 0 ] ] = [ os . path . join ( ' . ' , e ) ]
wpc_content = tmpl . substitute ( {
' entry_json ' : json . dumps ( entry )
} )
with open ( plugin . get_path ( ' public/webpack.config.js ' ) , ' w ' ) as f :
f . write ( wpc_content )
else :
logger . warning ( " Cannot generate webpack.config.js for {} , a path is missing: {} " . format ( plugin , ' ' . join ( build_paths ) ) )
2018-03-19 20:46:28 +00:00
# Check for webpack.config.js (if we need to build it)
if plugin . path_exists ( " public/webpack.config.js " ) and not plugin . path_exists ( " public/build " ) :
logger . info ( " Running webpack for {} " . format ( plugin . get_name ( ) ) )
2018-07-25 21:54:41 +00:00
subprocess . call ( [ ' webpack-cli ' ] , cwd = plugin . get_path ( " public " ) )
2018-03-19 20:46:28 +00:00
2018-01-30 16:48:16 +00:00
plugin . register ( )
logger . info ( " Registered {} " . format ( plugin ) )
2018-03-19 16:26:10 +00:00
def get_app_url_patterns ( ) :
2018-01-30 16:48:16 +00:00
"""
2018-03-19 16:26:10 +00:00
@return the patterns to expose the / public directory of each plugin ( if needed ) and
each mount point
2018-01-30 16:48:16 +00:00
"""
url_patterns = [ ]
for plugin in get_active_plugins ( ) :
2018-03-19 16:26:10 +00:00
for mount_point in plugin . app_mount_points ( ) :
2018-02-23 22:48:32 +00:00
url_patterns . append ( url ( ' ^plugins/ {} / {} ' . format ( plugin . get_name ( ) , mount_point . url ) ,
mount_point . view ,
* mount_point . args ,
* * mount_point . kwargs ) )
2018-03-19 16:26:10 +00:00
if plugin . path_exists ( " public " ) :
2018-01-30 16:48:16 +00:00
url_patterns . append ( url ( ' ^plugins/ {} /(.*) ' . format ( plugin . get_name ( ) ) ,
django . views . static . serve ,
{ ' document_root ' : plugin . get_path ( " public " ) } ) )
2018-02-23 22:48:32 +00:00
2018-03-19 16:26:10 +00:00
return url_patterns
def get_api_url_patterns ( ) :
"""
@return the patterns to expose the plugin API mount points ( if any )
"""
url_patterns = [ ]
for plugin in get_active_plugins ( ) :
for mount_point in plugin . api_mount_points ( ) :
url_patterns . append ( url ( ' ^plugins/ {} / {} ' . format ( plugin . get_name ( ) , mount_point . url ) ,
mount_point . view ,
* mount_point . args ,
* * mount_point . kwargs ) )
2018-02-23 22:48:32 +00:00
2018-01-30 16:48:16 +00:00
return url_patterns
2018-03-19 18:21:01 +00:00
2018-01-30 16:48:16 +00:00
plugins = None
def get_active_plugins ( ) :
# Cache plugins search
global plugins
if plugins != None : return plugins
plugins = [ ]
plugins_path = get_plugins_path ( )
for dir in [ d for d in os . listdir ( plugins_path ) if os . path . isdir ( plugins_path ) ] :
# Each plugin must have a manifest.json and a plugin.py
plugin_path = os . path . join ( plugins_path , dir )
manifest_path = os . path . join ( plugin_path , " manifest.json " )
pluginpy_path = os . path . join ( plugin_path , " plugin.py " )
disabled_path = os . path . join ( plugin_path , " disabled " )
2018-03-02 16:30:16 +00:00
# Do not load test plugin unless we're in test mode
if os . path . basename ( plugin_path ) == ' test ' and not settings . TESTING :
continue
2018-07-26 21:55:20 +00:00
# Ignore .gitignore
if os . path . basename ( plugin_path ) == ' .gitignore ' :
continue
2018-01-30 16:48:16 +00:00
if not os . path . isfile ( manifest_path ) or not os . path . isfile ( pluginpy_path ) :
logger . warning ( " Found invalid plugin in {} " . format ( plugin_path ) )
continue
# Plugins that have a "disabled" file are disabled
if os . path . isfile ( disabled_path ) :
continue
2018-02-09 18:46:45 +00:00
# Read manifest
with open ( manifest_path ) as manifest_file :
manifest = json . load ( manifest_file )
if ' webodmMinVersion ' in manifest :
min_version = manifest [ ' webodmMinVersion ' ]
if versionToInt ( min_version ) > versionToInt ( settings . VERSION ) :
logger . warning ( " In {} webodmMinVersion is set to {} but WebODM version is {} . Plugin will not be loaded. Update WebODM. " . format ( manifest_path , min_version , settings . VERSION ) )
continue
2018-01-30 16:48:16 +00:00
# Instantiate the plugin
try :
module = importlib . import_module ( " plugins. {} " . format ( dir ) )
cls = getattr ( module , " Plugin " )
plugins . append ( cls ( ) )
except Exception as e :
logger . warning ( " Failed to instantiate plugin {} : {} " . format ( dir , e ) )
return plugins
2018-03-19 18:21:01 +00:00
2018-03-19 16:26:10 +00:00
def get_plugin_by_name ( name ) :
plugins = get_active_plugins ( )
res = list ( filter ( lambda p : p . get_name ( ) == name , plugins ) )
return res [ 0 ] if res else None
2018-01-30 16:48:16 +00:00
def get_plugins_path ( ) :
current_path = os . path . dirname ( os . path . realpath ( __file__ ) )
return os . path . abspath ( os . path . join ( current_path , " .. " , " .. " , " plugins " ) )
2018-02-09 18:46:45 +00:00
2018-07-26 19:14:48 +00:00
def get_dynamic_script_handler ( script_path , callback = None , * * kwargs ) :
def handleRequest ( request ) :
if callback is not None :
template_params = callback ( request , * * kwargs )
if not template_params :
return HttpResponse ( " " )
else :
template_params = kwargs
with open ( script_path ) as f :
tmpl = Template ( f . read ( ) )
2018-07-30 15:55:46 +00:00
try :
return HttpResponse ( tmpl . substitute ( template_params ) )
except TypeError as e :
return HttpResponse ( " Template substitution failed with params: {} . {} " . format ( str ( template_params ) , e ) )
2018-07-26 19:14:48 +00:00
return handleRequest
2018-07-27 18:18:03 +00:00
def get_site_settings ( ) :
return Setting . objects . first ( )
2018-02-09 18:46:45 +00:00
def versionToInt ( version ) :
"""
Converts a WebODM version string ( major . minor . build ) to a integer value
for comparison
>> > versionToInt ( " 1.2.3 " )
100203
>> > versionToInt ( " 1 " )
100000
>> > versionToInt ( " 1.2.3.4 " )
100203
>> > versionToInt ( " wrong " )
- 1
"""
try :
return sum ( [ reduce ( lambda mult , ver : mult * ver , i ) for i in zip ( [ 100000 , 100 , 1 ] , map ( int , version . split ( " . " ) ) ) ] )
except :
return - 1