ProjectDjango/Project.py

Sun, 16 Feb 2014 17:06:27 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 16 Feb 2014 17:06:27 +0100
changeset 40
b793fc09d732
parent 38
3ce20c97fca9
child 47
19da3417eb3d
permissions
-rw-r--r--

Adjusted the Django info routines to show info about Django version related to current project.

# -*- coding: utf-8 -*-

# Copyright (c) 2013 - 2014 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the Django project support.
"""

from __future__ import unicode_literals    # __IGNORE_WARNING__
try:
    str = unicode    # __IGNORE_WARNING__
except (NameError):
    pass

import sys
import os
import re

from PyQt4.QtCore import QObject, QTimer, QUrl, QFileInfo
from PyQt4.QtGui import QMenu, QInputDialog, QLineEdit, QDesktopServices, \
    QDialog
from PyQt4.QtCore import QProcess as QProcessPyQt

from E5Gui.E5Application import e5App
from E5Gui import E5MessageBox, E5FileDialog
from E5Gui.E5Action import E5Action

from Globals import isWindowsPlatform

from .DjangoDialog import DjangoDialog

import Preferences
import Utilities
import UI.PixmapCache


class DjangoNoSiteSelectedException(Exception):
    """
    Exception thrown to signal, that there is no current site.
    """
    pass


class QProcess(QProcessPyQt):
    """
    Class transforming the call arguments in case of gnome-terminal.
    """
    def start(self, cmd, args=[], mode=QProcessPyQt.ReadWrite):
        """
        Static method to start the given program (cmd) in a new process, if
        none is already running, passing the command line arguments in args.
        
        @param cmd start the given program cmd (string)
        @keyparam args list of parameters (list of strings)
        @keyparam mode access mode (QIODevice.OpenMode)
        """
        if cmd.endswith('gnome-terminal') and args[0] == '-e':
            args = ['-e', ' '.join(args[1:])]
        
        super(QProcess, self).start(cmd, args, mode)
    
    @staticmethod
    def startDetached(cmd, args=[], path=''):
        """
        Static method to start the given program (cmd) in a new process, if
        none is already running, passing the command line arguments in args.
        
        @param cmd start the given program cmd (string)
        @keyparam args list of parameters (list of strings)
        @keyparam path new working directory (string)
        @return tuple of successful start and process id (boolean, integer)
        """
        if cmd.endswith('gnome-terminal') and args[0] == '-e':
            args = ['-e', ' '.join(args[1:])]
        
        return QProcessPyQt.startDetached(cmd, args, path)


class Project(QObject):
    """
    Class implementing the Django project support.
    """
    RecentApplicationsKey = "Django/RecentApplications"
    
    def __init__(self, plugin, parent=None):
        """
        Constructor
        
        @param plugin reference to the plugin object
        @param parent parent (QObject)
        """
        super(Project, self).__init__(parent)
        
        self.__plugin = plugin
        self.__ui = parent
        self.__e5project = e5App().getObject("Project")
        self.__hooksInstalled = False
        
        self.__menus = {}   # dictionary with references to menus
        
        self.__serverProc = None
        self.__testServerProc = None
        
        self.__recentApplications = []
        self.__loadRecentApplications()
    
    def initActions(self):
        """
        Public method to define the Django actions.
        """
        self.actions = []
    
        self.selectSiteAct = E5Action(
            self.trUtf8('Current Project'),
            "",
            0, 0,
            self, 'django_current_project')
        self.selectSiteAct.setStatusTip(self.trUtf8(
            'Selects the current project'))
        self.selectSiteAct.setWhatsThis(self.trUtf8(
            """<b>Current Project</b>"""
            """<p>Selects the current project. Used for multi-project """
            """Django projects to switch between the projects.</p>"""
        ))
        self.selectSiteAct.triggered[()].connect(self.__selectSite)
        self.actions.append(self.selectSiteAct)
        self.__setCurrentSite(None)
        
        ##############################
        ## start actions below      ##
        ##############################
        
        self.startProjectAct = E5Action(
            self.trUtf8('Start Project'),
            self.trUtf8('Start &Project'),
            0, 0,
            self, 'django_start_project')
        self.startProjectAct.setStatusTip(self.trUtf8(
            'Starts a new Django project'))
        self.startProjectAct.setWhatsThis(self.trUtf8(
            """<b>Start Project</b>"""
            """<p>Starts a new Django project using "django-admin.py"""
            """ startproject".</p>"""
        ))
        self.startProjectAct.triggered[()].connect(self.__startProject)
        self.actions.append(self.startProjectAct)
        
        self.startGlobalApplicationAct = E5Action(
            self.trUtf8('Start Application (global)'),
            self.trUtf8('Start Application (&global)'),
            0, 0,
            self, 'django_start_global_application')
        self.startGlobalApplicationAct.setStatusTip(self.trUtf8(
            'Starts a new global Django application'))
        self.startGlobalApplicationAct.setWhatsThis(self.trUtf8(
            """<b>Start Application (global)</b>"""
            """<p>Starts a new global Django application using"""
            """ "django-admin.py startapp".</p>"""
        ))
        self.startGlobalApplicationAct.triggered[()].connect(
            self.__startGlobalApplication)
        self.actions.append(self.startGlobalApplicationAct)
        
        self.startLocalApplicationAct = E5Action(
            self.trUtf8('Start Application (local)'),
            self.trUtf8('Start Application (&local)'),
            0, 0,
            self, 'django_start_local_application')
        self.startLocalApplicationAct.setStatusTip(self.trUtf8(
            'Starts a new local Django application'))
        self.startLocalApplicationAct.setWhatsThis(self.trUtf8(
            """<b>Start Application (local)</b>"""
            """<p>Starts a new local Django application using"""
            """ "manage.py startapp".</p>"""
        ))
        self.startLocalApplicationAct.triggered[()].connect(
            self.__startLocalApplication)
        self.actions.append(self.startLocalApplicationAct)
        
        ##############################
        ## run actions below        ##
        ##############################
        
        self.runServerAct = E5Action(
            self.trUtf8('Run Server'),
            self.trUtf8('Run &Server'),
            0, 0,
            self, 'django_run_server')
        self.runServerAct.setStatusTip(self.trUtf8(
            'Starts the Django Web server'))
        self.runServerAct.setWhatsThis(self.trUtf8(
            """<b>Run Server</b>"""
            """<p>Starts the Django Web server using "manage.py"""
            """ runserver".</p>"""
        ))
        self.runServerAct.triggered[()].connect(self.__runServer)
        self.actions.append(self.runServerAct)
        
        self.runBrowserAct = E5Action(
            self.trUtf8('Run Web-Browser'),
            self.trUtf8('Run &Web-Browser'),
            0, 0,
            self, 'django_run_browser')
        self.runBrowserAct.setStatusTip(self.trUtf8(
            'Starts the default Web-Browser with the URL of the Django Web'
            ' server'))
        self.runBrowserAct.setWhatsThis(self.trUtf8(
            """<b>Run Web-Browser</b>"""
            """<p>Starts the default Web-Browser with the URL of the """
            """Django Web server.</p>"""
        ))
        self.runBrowserAct.triggered[()].connect(self.__runBrowser)
        self.actions.append(self.runBrowserAct)
        
        ##############################
        ## caching actions below    ##
        ##############################
        
        self.createCacheTableAct = E5Action(
            self.trUtf8('Create Cache Tables'),
            self.trUtf8('C&reate Cache Tables'),
            0, 0,
            self, 'django_create_cache_tables')
        self.createCacheTableAct.setStatusTip(self.trUtf8(
            'Creates the tables needed to use the SQL cache backend'))
        self.createCacheTableAct.setWhatsThis(self.trUtf8(
            """<b>Create Cache Tables</b>"""
            """<p>Creates the tables needed to use the SQL cache"""
            """ backend.</p>"""
        ))
        self.createCacheTableAct.triggered[()].connect(
            self.__createCacheTables)
        self.actions.append(self.createCacheTableAct)
        
        ##############################
        ## help action below        ##
        ##############################
        
        self.helpAct = E5Action(
            self.trUtf8('Help'),
            self.trUtf8('&Help'),
            0, 0,
            self, 'django_help')
        self.helpAct.setStatusTip(self.trUtf8(
            'Shows the Django help index'))
        self.helpAct.setWhatsThis(self.trUtf8(
            """<b>Help</b>"""
            """<p>Shows the Django help index page.</p>"""
        ))
        self.helpAct.triggered[()].connect(self.__showHelpIndex)
        self.actions.append(self.helpAct)
        
        ##############################
        ## about action below       ##
        ##############################
        
        self.aboutDjangoAct = E5Action(
            self.trUtf8('About Django'),
            self.trUtf8('About D&jango'),
            0, 0,
            self, 'django_about')
        self.aboutDjangoAct.setStatusTip(self.trUtf8(
            'Shows some information about Django'))
        self.aboutDjangoAct.setWhatsThis(self.trUtf8(
            """<b>About Django</b>"""
            """<p>Shows some information about Django.</p>"""
        ))
        self.aboutDjangoAct.triggered[()].connect(self.__djangoInfo)
        self.actions.append(self.aboutDjangoAct)
        
        self.__initDatabaseActions()
        self.__initDatabaseSqlActions()
        self.__initToolsActions()
        self.__initTestingActions()
        self.__initAuthorizationActions()
        self.__initSessionActions()

    def __initDatabaseActions(self):
        """
        Private method to define the database related actions.
        """
        self.syncDatabaseAct = E5Action(
            self.trUtf8('Synchronize'),
            self.trUtf8('&Synchronize'),
            0, 0,
            self, 'django_database_syncdb')
        self.syncDatabaseAct.setStatusTip(self.trUtf8(
            'Synchronizes the database'))
        self.syncDatabaseAct.setWhatsThis(self.trUtf8(
            """<b>Synchronize</b>"""
            """<p>Synchronizes the database.</p>"""
        ))
        self.syncDatabaseAct.triggered[()].connect(self.__databaseSynchronize)
        self.actions.append(self.syncDatabaseAct)
        
        self.inspectDatabaseAct = E5Action(
            self.trUtf8('Introspect'),
            self.trUtf8('&Introspect'),
            0, 0,
            self, 'django_database_inspect')
        self.inspectDatabaseAct.setStatusTip(self.trUtf8(
            'Introspects the database tables and outputs a Django model'
            ' module'))
        self.inspectDatabaseAct.setWhatsThis(self.trUtf8(
            """<b>Introspect</b>"""
            """<p>Introspects the database tables and outputs a """
            """Django model module.</p>"""
        ))
        self.inspectDatabaseAct.triggered[()].connect(self.__databaseInspect)
        self.actions.append(self.inspectDatabaseAct)
        
        self.flushDatabaseAct = E5Action(
            self.trUtf8('Flush'),
            self.trUtf8('&Flush'),
            0, 0,
            self, 'django_database_flush')
        self.flushDatabaseAct.setStatusTip(self.trUtf8(
            'Returns all database tables to the state just after their'
            ' installation'))
        self.flushDatabaseAct.setWhatsThis(self.trUtf8(
            """<b>Flush</b>"""
            """<p>Returns all database tables to the state """
            """just after their installation.</p>"""
        ))
        self.flushDatabaseAct.triggered[()].connect(self.__databaseFlush)
        self.actions.append(self.flushDatabaseAct)
        
        self.databaseClientAct = E5Action(
            self.trUtf8('Start Client Console'),
            self.trUtf8('Start &Client Console'),
            0, 0,
            self, 'django_database_client')
        self.databaseClientAct.setStatusTip(self.trUtf8(
            'Starts a console window for the database client'))
        self.databaseClientAct.setWhatsThis(self.trUtf8(
            """<b>Start Client Console</b>"""
            """<p>Starts a console window for the database client.</p>"""
        ))
        self.databaseClientAct.triggered[()].connect(self.__runDatabaseClient)
        self.actions.append(self.databaseClientAct)
    
    def __initDatabaseSqlActions(self):
        """
        Private method to define the database SQL related actions.
        """
        self.databaseSqlCreateTablesAct = E5Action(
            self.trUtf8('Create Tables'),
            self.trUtf8('Create &Tables'),
            0, 0,
            self, 'django_database_sql_create_tables')
        self.databaseSqlCreateTablesAct.setStatusTip(self.trUtf8(
            'Prints the CREATE TABLE SQL statements for one or more'
            ' applications'))
        self.databaseSqlCreateTablesAct.setWhatsThis(self.trUtf8(
            """<b>Create Tables</b>"""
            """<p>Prints the CREATE TABLE SQL statements for one or """
            """more applications.</p>"""
        ))
        self.databaseSqlCreateTablesAct.triggered[()].connect(
            self.__databaseSqlCreateTables)
        self.actions.append(self.databaseSqlCreateTablesAct)
        
        self.databaseSqlCreateIndexesAct = E5Action(
            self.trUtf8('Create Indexes'),
            self.trUtf8('Create &Indexes'),
            0, 0,
            self, 'django_database_sql_create_indexes')
        self.databaseSqlCreateIndexesAct.setStatusTip(self.trUtf8(
            'Prints the CREATE INDEX SQL statements for one or more'
            ' applications'))
        self.databaseSqlCreateIndexesAct.setWhatsThis(self.trUtf8(
            """<b>Create Indexes</b>"""
            """<p>Prints the CREATE INDEX SQL statements for one or """
            """more applications.</p>"""
        ))
        self.databaseSqlCreateIndexesAct.triggered[()].connect(
            self.__databaseSqlCreateIndexes)
        self.actions.append(self.databaseSqlCreateIndexesAct)
        
        self.databaseSqlCreateEverythingAct = E5Action(
            self.trUtf8('Create Everything'),
            self.trUtf8('Create &Everything'),
            0, 0,
            self, 'django_database_sql_create_everything')
        self.databaseSqlCreateEverythingAct.setStatusTip(self.trUtf8(
            'Prints the CREATE ... SQL statements for one or more'
            ' applications'))
        self.databaseSqlCreateEverythingAct.setWhatsThis(self.trUtf8(
            """<b>Create Everything</b>"""
            """<p>Prints the CREATE TABLE, custom SQL and CREATE INDEX SQL """
            """statements for one or more applications.</p>"""
        ))
        self.databaseSqlCreateEverythingAct.triggered[()].connect(
            self.__databaseSqlCreateEverything)
        self.actions.append(self.databaseSqlCreateEverythingAct)
        
        self.databaseSqlCustomAct = E5Action(
            self.trUtf8('Custom Statements'),
            self.trUtf8('&Custom Statements'),
            0, 0,
            self, 'django_database_sql_custom')
        self.databaseSqlCustomAct.setStatusTip(self.trUtf8(
            'Prints the custom table modifying SQL statements for '
            'one or more applications'))
        self.databaseSqlCustomAct.setWhatsThis(self.trUtf8(
            """<b>Custom Statements</b>"""
            """<p>Prints the custom table modifying SQL statements """
            """for one or more applications.</p>"""
        ))
        self.databaseSqlCustomAct.triggered[()].connect(
            self.__databaseSqlCustom)
        self.actions.append(self.databaseSqlCustomAct)
        
        self.databaseSqlDropTablesAct = E5Action(
            self.trUtf8('Drop Tables'),
            self.trUtf8('&Drop Tables'),
            0, 0,
            self, 'django_database_sql_drop_tables')
        self.databaseSqlDropTablesAct.setStatusTip(self.trUtf8(
            'Prints the DROP TABLE SQL statements for '
            'one or more applications'))
        self.databaseSqlDropTablesAct.setWhatsThis(self.trUtf8(
            """<b>Drop Tables</b>"""
            """<p>Prints the DROP TABLE SQL statements """
            """for one or more applications.</p>"""
        ))
        self.databaseSqlDropTablesAct.triggered[()].connect(
            self.__databaseSqlDropTables)
        self.actions.append(self.databaseSqlDropTablesAct)
        
        self.databaseSqlFlushAct = E5Action(
            self.trUtf8('Flush Database'),
            self.trUtf8('&Flush Database'),
            0, 0,
            self, 'django_database_sql_flush_database')
        self.databaseSqlFlushAct.setStatusTip(self.trUtf8(
            'Prints a list of statements to return all database tables to the'
            ' state just after their installation'))
        self.databaseSqlFlushAct.setWhatsThis(self.trUtf8(
            """<b>Flush Database</b>"""
            """<p>Prints a list of statements to return all database tables"""
            """ to the state just after their installation.</p>"""
        ))
        self.databaseSqlFlushAct.triggered[()].connect(
            self.__databaseSqlFlushDatabase)
        self.actions.append(self.databaseSqlFlushAct)
        
        self.databaseSqlResetSeqAct = E5Action(
            self.trUtf8('Reset Sequences'),
            self.trUtf8('Reset &Sequences'),
            0, 0,
            self, 'django_database_sql_reset_sequences')
        self.databaseSqlResetSeqAct.setStatusTip(self.trUtf8(
            'Prints the SQL statements for resetting sequences for '
            'one or more applications'))
        self.databaseSqlResetSeqAct.setWhatsThis(self.trUtf8(
            """<b>Reset Sequences</b>"""
            """<p>Prints the SQL statements for resetting sequences for """
            """one or more applications.</p>"""
        ))
        self.databaseSqlResetSeqAct.triggered[()].connect(
            self.__databaseSqlResetSequences)
        self.actions.append(self.databaseSqlResetSeqAct)
    
    def __initToolsActions(self):
        """
        Private method to define the tool actions.
        """
        self.diffSettingsAct = E5Action(
            self.trUtf8('Diff Settings'),
            self.trUtf8('&Diff Settings'),
            0, 0,
            self, 'django_tools_diffsettings')
        self.diffSettingsAct.setStatusTip(self.trUtf8(
            'Shows the modification made to the settings'))
        self.diffSettingsAct.setWhatsThis(self.trUtf8(
            """<b>Diff Settings</b>"""
            """<p>Shows the modification made to the settings.</p>"""
        ))
        self.diffSettingsAct.triggered[()].connect(self.__diffSettings)
        self.actions.append(self.diffSettingsAct)
    
        self.cleanupAct = E5Action(
            self.trUtf8('Cleanup'),
            self.trUtf8('&Cleanup'),
            0, 0,
            self, 'django_tools_cleanup')
        self.cleanupAct.setStatusTip(self.trUtf8(
            'Cleans out old data from the database'))
        self.cleanupAct.setWhatsThis(self.trUtf8(
            """<b>Cleanup</b>"""
            """<p>Cleans out old data from the database.</p>"""
        ))
        self.cleanupAct.triggered[()].connect(self.__cleanup)
        self.actions.append(self.cleanupAct)
    
        self.validateAct = E5Action(
            self.trUtf8('Validate'),
            self.trUtf8('&Validate'),
            0, 0,
            self, 'django_tools_validate')
        self.validateAct.setStatusTip(self.trUtf8(
            'Validates all installed models'))
        self.validateAct.setWhatsThis(self.trUtf8(
            """<b>Validate</b>"""
            """<p>Validates all installed models.</p>"""
        ))
        self.validateAct.triggered[()].connect(self.__validate)
        self.actions.append(self.validateAct)
    
        self.runPythonShellAct = E5Action(
            self.trUtf8('Start Python Console'),
            self.trUtf8('Start &Python Console'),
            0, 0,
            self, 'django_tools_pythonconsole')
        self.runPythonShellAct.setStatusTip(self.trUtf8(
            'Starts a Python interactive interpreter'))
        self.runPythonShellAct.setWhatsThis(self.trUtf8(
            """<b>Start Python Console</b>"""
            """<p>Starts a Python interactive interpreter.</p>"""
        ))
        self.runPythonShellAct.triggered[()].connect(self.__runPythonShell)
        self.actions.append(self.runPythonShellAct)
    
    def __initTestingActions(self):
        """
        Private method to define the testing actions.
        """
        self.dumpDataAct = E5Action(
            self.trUtf8('Dump Data'),
            self.trUtf8('&Dump Data'),
            0, 0,
            self, 'django_tools_dumpdata')
        self.dumpDataAct.setStatusTip(self.trUtf8(
            'Dump the database data to a fixture'))
        self.dumpDataAct.setWhatsThis(self.trUtf8(
            """<b>Dump Data</b>"""
            """<p>Dump the database data to a fixture.</p>"""
        ))
        self.dumpDataAct.triggered[()].connect(self.__dumpData)
        self.actions.append(self.dumpDataAct)
        
        self.loadDataAct = E5Action(
            self.trUtf8('Load Data'),
            self.trUtf8('&Load Data'),
            0, 0,
            self, 'django_tools_loaddata')
        self.loadDataAct.setStatusTip(self.trUtf8(
            'Load data from fixture files'))
        self.loadDataAct.setWhatsThis(self.trUtf8(
            """<b>Load Data</b>"""
            """<p>Load data from fixture files.</p>"""
        ))
        self.loadDataAct.triggered[()].connect(self.__loadData)
        self.actions.append(self.loadDataAct)
        
        self.runTestAct = E5Action(
            self.trUtf8('Run Testsuite'),
            self.trUtf8('Run &Testsuite'),
            0, 0,
            self, 'django_tools_run_test')
        self.runTestAct.setStatusTip(self.trUtf8(
            'Run the test suite for applications or the whole site'))
        self.runTestAct.setWhatsThis(self.trUtf8(
            """<b>Run Testsuite</b>"""
            """<p>Run the test suite for applications or the whole site.</p>"""
        ))
        self.runTestAct.triggered[()].connect(self.__runTestSuite)
        self.actions.append(self.runTestAct)
        
        self.runTestServerAct = E5Action(
            self.trUtf8('Run Testserver'),
            self.trUtf8('Run Test&server'),
            0, 0,
            self, 'django_tools_run_test_server')
        self.runTestServerAct.setStatusTip(self.trUtf8(
            'Run a development server with data from a set of fixtures'))
        self.runTestServerAct.setWhatsThis(self.trUtf8(
            """<b>Run Testserver</b>"""
            """<p>Run a development server with data from a set of"""
            """ fixtures.</p>"""
        ))
        self.runTestServerAct.triggered[()].connect(self.__runTestServer)
        self.actions.append(self.runTestServerAct)
    
    def __initAuthorizationActions(self):
        """
        Private method to define the authorization actions.
        """
        self.changePasswordAct = E5Action(
            self.trUtf8('Change Password'),
            self.trUtf8('Change &Password'),
            0, 0,
            self, 'django_auth_changepassword')
        self.changePasswordAct.setStatusTip(self.trUtf8(
            'Change the password of a user'))
        self.changePasswordAct.setWhatsThis(self.trUtf8(
            """<b>Change Password</b>"""
            """<p>Change the password of a user of the Django project.</p>"""
        ))
        self.changePasswordAct.triggered[()].connect(self.__changePassword)
        self.actions.append(self.changePasswordAct)
        
        self.createSuperUserAct = E5Action(
            self.trUtf8('Create Superuser'),
            self.trUtf8('Create &Superuser'),
            0, 0,
            self, 'django_auth_createsuperuser')
        self.createSuperUserAct.setStatusTip(self.trUtf8(
            'Create a superuser account'))
        self.createSuperUserAct.setWhatsThis(self.trUtf8(
            """<b>Create Superuser</b>"""
            """<p>Create a superuser account for the Django project.</p>"""
        ))
        self.createSuperUserAct.triggered[()].connect(self.__createSuperUser)
        self.actions.append(self.createSuperUserAct)
    
    def __initSessionActions(self):
        """
        Private method to define the session actions.
        """
        self.clearSessionsAct = E5Action(
            self.trUtf8('Clear Sessions'),
            self.trUtf8('Clear &Sessions'),
            0, 0,
            self, 'django_session_clearsessions')
        self.clearSessionsAct.setStatusTip(self.trUtf8(
            'Clear expired sessions'))
        self.clearSessionsAct.setWhatsThis(self.trUtf8(
            """<b>Clear Sessions</b>"""
            """<p>Clear expired sessions of the Django project.</p>"""
        ))
        self.clearSessionsAct.triggered[()].connect(self.__clearSessions)
        self.actions.append(self.clearSessionsAct)
    
    def initMenu(self):
        """
        Public slot to initialize the Django menu.
        
        @return the menu generated (QMenu)
        """
        self.__menus = {}   # clear menus references
        
        menu = QMenu(self.trUtf8('D&jango'), self.__ui)
        menu.setTearOffEnabled(True)
        
        menu.addAction(self.selectSiteAct)
        menu.addSeparator()
        menu.addAction(self.runServerAct)
        menu.addAction(self.runBrowserAct)
        menu.addSeparator()
        menu.addAction(self.startProjectAct)
        menu.addAction(self.startGlobalApplicationAct)
        menu.addAction(self.startLocalApplicationAct)
        menu.addSeparator()
        menu.addMenu(self.__initDatabaseMenu())
        menu.addSeparator()
        menu.addMenu(self.__initToolsMenu())
        menu.addSeparator()
        menu.addAction(self.createCacheTableAct)
        menu.addSeparator()
        menu.addMenu(self.__initTestingMenu())
        menu.addSeparator()
        menu.addMenu(self.__initAuthorizationMenu())
        menu.addSeparator()
        menu.addMenu(self.__initSessionMenu())
        menu.addSeparator()
        menu.addAction(self.aboutDjangoAct)
        menu.addSeparator()
        menu.addAction(self.helpAct)
        
        self.__menus["main"] = menu
        
        return menu

    def __initDatabaseMenu(self):
        """
        Private slot to initialize the database menu.
        
        @return the menu generated (QMenu)
        """
        menu = QMenu(self.trUtf8("&Database"), self.__ui)
        menu.setTearOffEnabled(True)
        
        menu.addAction(self.syncDatabaseAct)
        menu.addSeparator()
        menu.addAction(self.inspectDatabaseAct)
        menu.addSeparator()
        menu.addAction(self.flushDatabaseAct)
        menu.addSeparator()
        menu.addAction(self.databaseClientAct)
        menu.addSeparator()
        menu.addMenu(self.__initDatabaseSqlMenu())
        
        self.__menus["database"] = menu
        
        return menu
    
    def __initDatabaseSqlMenu(self):
        """
        Private slot to initialize the database SQL submenu.
        
        @return the menu generated (QMenu)
        """
        menu = QMenu(self.trUtf8("Show &SQL"), self.__ui)
        menu.setTearOffEnabled(True)
        
        menu.addAction(self.databaseSqlCreateTablesAct)
        menu.addAction(self.databaseSqlCreateIndexesAct)
        menu.addAction(self.databaseSqlCreateEverythingAct)
        menu.addSeparator()
        menu.addAction(self.databaseSqlCustomAct)
        menu.addSeparator()
        menu.addAction(self.databaseSqlDropTablesAct)
        menu.addSeparator()
        menu.addAction(self.databaseSqlFlushAct)
        menu.addAction(self.databaseSqlResetSeqAct)
        
        self.__menus["sql"] = menu
        
        return menu
    
    def __initToolsMenu(self):
        """
        Private slot to initialize the tools menu.
        
        @return the menu generated (QMenu)
        """
        menu = QMenu(self.trUtf8("&Tools"), self.__ui)
        menu.setTearOffEnabled(True)
        
        menu.addAction(self.diffSettingsAct)
        menu.addAction(self.cleanupAct)
        menu.addAction(self.validateAct)
        menu.addSeparator()
        menu.addAction(self.runPythonShellAct)
        
        self.__menus["tools"] = menu
        
        return menu
    
    def __initTestingMenu(self):
        """
        Private slot to initialize the testing menu.
        
        @return the menu generated (QMenu)
        """
        menu = QMenu(self.trUtf8("T&esting"), self.__ui)
        menu.setTearOffEnabled(True)
        
        menu.addAction(self.dumpDataAct)
        menu.addAction(self.loadDataAct)
        menu.addSeparator()
        menu.addAction(self.runTestAct)
        menu.addAction(self.runTestServerAct)
        
        self.__menus["testing"] = menu
        
        return menu
    
    def __initAuthorizationMenu(self):
        """
        Private slot to initialize the authorization menu.
        
        @return the menu generated (QMenu)
        """
        menu = QMenu(self.trUtf8("&Authorization"), self.__ui)
        menu.setTearOffEnabled(True)
        
        menu.addAction(self.changePasswordAct)
        menu.addAction(self.createSuperUserAct)
        
        self.__menus["authorization"] = menu
        
        return menu
    
    def __initSessionMenu(self):
        """
        Private slot to initialize the authorization menu.
        
        @return the menu generated (QMenu)
        """
        menu = QMenu(self.trUtf8("&Session"), self.__ui)
        menu.setTearOffEnabled(True)
        
        menu.addAction(self.clearSessionsAct)
        
        self.__menus["session"] = menu
        
        return menu
    
    def getMenu(self, name):
        """
        Public method to get a reference to the requested menu.
        
        @param name name of the menu (string)
        @return reference to the menu (QMenu) or None, if no
            menu with the given name exists
        """
        if name in self.__menus:
            return self.__menus[name]
        else:
            return None
    
    def getMenuNames(self):
        """
        Public method to get the names of all menus.
        
        @return menu names (list of string)
        """
        return list(self.__menus.keys())

    ##################################################################
    ## methods below implement the various hook related functions
    ##################################################################
    
    def registerOpenHook(self):
        """
        Public method to register the open hook to open a translations file
        in a translations editor.
        """
        if self.__hooksInstalled:
            editor = self.__plugin.getPreferences("TranslationsEditor")
            try:
                if editor:
                    self.__translationsBrowser.addHookMethodAndMenuEntry(
                        "open",
                        self.openPOEditor,
                        self.trUtf8("Open with {0}").format(
                            os.path.basename(editor)))
                else:
                    self.__translationsBrowser.removeHookMethod("open")
            except KeyError:
                # ignore for older eric5 versions
                pass
    
    def projectOpenedHooks(self):
        """
        Public method to add our hook methods.
        """
        if self.__e5project.getProjectType() == "Django":
            self.__formsBrowser = \
                e5App().getObject("ProjectBrowser").getProjectBrowser("forms")
            self.__formsBrowser.addHookMethodAndMenuEntry(
                "newForm",
                self.newForm, self.trUtf8("New template..."))
            
            self.__e5project.projectLanguageAddedByCode.connect(
                self.__projectLanguageAdded)
            self.__translationsBrowser = \
                e5App().getObject("ProjectBrowser")\
                .getProjectBrowser("translations")
            self.__translationsBrowser.addHookMethodAndMenuEntry(
                "generateAll",
                self.updateCatalogs, self.trUtf8("Update all catalogs"))
            self.__translationsBrowser.addHookMethodAndMenuEntry(
                "generateSelected",
                self.updateSelectedCatalogs,
                self.trUtf8("Update selected catalogs"))
            self.__translationsBrowser.addHookMethodAndMenuEntry(
                "generateAllWithObsolete", self.updateCatalogsWithObsolete,
                self.trUtf8("Update all catalogs (with obsolete)"))
            self.__translationsBrowser.addHookMethodAndMenuEntry(
                "generateSelectedWithObsolete",
                self.updateSelectedCatalogsWithObsolete,
                self.trUtf8("Update selected catalogs (with obsolete)"))
            self.__translationsBrowser.addHookMethodAndMenuEntry(
                "releaseAll",
                self.compileCatalogs, self.trUtf8("Compile all catalogs"))
            self.__translationsBrowser.addHookMethodAndMenuEntry(
                "releaseSelected",
                self.compileSelectedCatalogs,
                self.trUtf8("Compile selected catalogs"))
            
            self.__hooksInstalled = True
            
            self.registerOpenHook()
    
    def projectClosedHooks(self):
        """
        Public method to remove our hook methods.
        """
        if self.__hooksInstalled:
            self.__formsBrowser.removeHookMethod("newForm")
            self.__formsBrowser = None
            
            self.__e5project.projectLanguageAddedByCode.disconnect(
                self.__projectLanguageAdded)
            self.__translationsBrowser.removeHookMethod(
                "generateAll")
            self.__translationsBrowser.removeHookMethod(
                "generateSelected")
            self.__translationsBrowser.removeHookMethod(
                "generateAllWithObsolete")
            self.__translationsBrowser.removeHookMethod(
                "generateSelectedWithObsolete")
            self.__translationsBrowser.removeHookMethod(
                "releaseAll")
            self.__translationsBrowser.removeHookMethod(
                "releaseSelected")
            try:
                self.__translationsBrowser.removeHookMethod("open")
            except KeyError:
                # ignore for older eric5 versions
                pass
            self.__translationsBrowser = None
            
        self.__hooksInstalled = False
    
    def newForm(self, path):
        """
        Public method to create a new form.
        
        @param path full directory path for the new form file (string)
        """
        fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
            self.__ui,
            self.trUtf8("New Form"),
            path,
            filter,
            None,
            E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
        
        if not fname:
            # user aborted or didn't enter a filename
            return
        
        ext = QFileInfo(fname).suffix()
        if not ext:
            ex = selectedFilter.split("(*")[1].split(")")[0]
            if ex:
                fname += ex
        
        if os.path.exists(fname):
            res = E5MessageBox.yesNo(
                self.__ui,
                self.trUtf8("New Form"),
                self.trUtf8("The file already exists! Overwrite it?"),
                icon=E5MessageBox.Warning)
            
            if not res:
                # user selected to not overwrite
                return
        
        try:
            f = open(fname, "w")
            f.write('<!DOCTYPE html>')
            f.write('<html>\n')
            f.write('  <head>\n')
            f.write('    <meta content="" />\n')
            f.write('    <title></title>\n')
            f.write('    <link rel="stylesheet" type="text/css"'
                    ' href="style.css"/>')
            f.write('    <!--[if lte IE 7]>')
            f.write('      <link rel="stylesheet" type="text/css"'
                    ' href="ie.css"/>')
            f.write('    <![endif]-->')
            f.write('  </head>\n')
            f.write('\n')
            f.write('  <body  class="bodyclass">\n')
            f.write('    <div id="container">')
            f.write('    </div>')
            f.write('  </body>\n')
            f.close()
            f.write('</html>\n')
            f.close()
        except (IOError, OSError) as e:
            E5MessageBox.critical(
                self.__ui,
                self.trUtf8("New Form"),
                self.trUtf8("<p>The new form file <b>{0}</b> could not be"
                            " created.<br> Problem: {1}</p>")
                .format(fname, str(e)))
            return
        
        self.__e5project.appendFile(fname)
        self.__formsBrowser.sourceFile.emit(fname)

    ##################################################################
    ## slots below implement general functionality
    ##################################################################
    
    def projectClosed(self):
        """
        Public method to handle the closing of a project.
        """
        if self.__serverProc is not None:
            self.__serverProcFinished()
        self.__setCurrentSite(None)
    
    def __getExecutablePaths(self, file):
        """
        Private method to build all full path of an executable file from
        the environment.
        
        @param file filename of the executable (string)
        @return list of full executable names, if the executable file is
            accessible via the searchpath defined by the PATH environment
            variable, or an empty list otherwise.
        """
        paths = []
        
        if os.path.isabs(file):
            if os.access(file, os.X_OK):
                return [file]
            else:
                return []
            
        cur_path = os.path.join(os.curdir, file)
        if os.path.exists(cur_path):
            if os.access(cur_path, os.X_OK):
                paths.append(cur_path)

        path = os.getenv('PATH')
        
        # environment variable not defined
        if path is not None:
            dirs = path.split(os.pathsep)
            for dir in dirs:
                exe = os.path.join(dir, file)
                if os.access(exe, os.X_OK) and exe not in paths:
                    paths.append(exe)
        
        return paths
    
    def supportedPythonVariants(self):
        """
        Public method to get the supported Python variants.
        
        @return list of supported Python variants (list of strings)
        """
        variants = []
        for variant in 'Python2', 'Python3':
            virtEnv = self.__getVirtualEnvironment(variant)
            if virtEnv:
                if self.__getDjangoAdminCommand(variant):
                    variants.append(variant)
            else:
                cmd = self.__getDjangoAdminCommand(variant)
                if isWindowsPlatform():
                    if variant.lower() in cmd.lower():
                        variants.append(variant)
                else:
                    try:
                        fullCmds = Utilities.getExecutablePaths(cmd)
                    except AttributeError:
                        fullCmds = self.__getExecutablePaths(cmd)
                    for fullCmd in fullCmds:
                        try:
                            f = open(fullCmd, 'r', encoding='utf-8')
                            l0 = f.readline()
                            f.close()
                        except (IOError, OSError):
                            l0 = ""
                        if variant.lower() in l0.lower() or \
                           "{0}.".format(variant[-1]) in l0 or \
                           (variant == "Python2" and
                            "python3" not in l0.lower() and
                                "python" in l0.lower()):
                            variants.append(variant)
                            break
        
        return variants
    
    def __getVirtualEnvironment(self, language=""):
        """
        Private method to get the path of the virtual environment.
        
        @param language Python variant to get the virtual environment
            for (string, one of '', 'Python2' or 'Python3')
        @return path of the virtual environment (string)
        """
        if not language:
            language = self.__e5project.getProjectLanguage()
        if language == "Python3":
            virtEnv = self.__plugin.getPreferences("VirtualEnvironmentPy3")
        elif language == "Python2":
            virtEnv = self.__plugin.getPreferences("VirtualEnvironmentPy2")
        else:
            virtEnv = ""
        if virtEnv and not os.path.exists(virtEnv):
            virtEnv = ""
        return virtEnv

    def __getDebugEnvironment(self, language=""):
        """
        Private method to get the path of the debugger environment.
        
        @param language Python variant to get the debugger environment
            for (string, one of '', 'Python2' or 'Python3')
        @return path of the debugger environment (string)
        """
        if not language:
            language = self.__e5project.getProjectLanguage()
        if language == "Python3":
            debugEnv = Preferences.getDebugger("Python3Interpreter")
            if not debugEnv and sys.version_info[0] == 3:
                debugEnv = sys.executable
        elif language == "Python2":
            debugEnv = Preferences.getDebugger("PythonInterpreter")
            if not debugEnv and sys.version_info[0] == 2:
                debugEnv = sys.executable
        else:
            debugEnv = sys.executable
        debugEnv = os.path.dirname(debugEnv)
        if debugEnv and not os.path.exists(debugEnv):
            if (language == "Python3" and sys.version_info[0] == 3) or \
               (language == "Python2" and sys.version_info[0] == 2):
                debugEnv = sys.exec_prefix
            else:
                debugEnv = ""
        return debugEnv

    def __getDjangoAdminCommand(self, language=""):
        """
        Private method to build a django-admin.py command.
        
        @param language Python variant to get the django-admin.py
            command for (string, one of '', 'Python2' or 'Python3')
        @return full django-admin.py command (string)
        """
        if not language:
            language = self.__e5project.getProjectLanguage()
        
        virtualEnv = self.__getVirtualEnvironment(language)
        if virtualEnv:
            if isWindowsPlatform():
                cmd = os.path.join(virtualEnv, "Scripts", "django-admin.py")
            else:
                cmds = [
                    os.path.join(virtualEnv, "bin", "django-admin.py"),
                    os.path.join(virtualEnv, "bin", "django-admin"),
                    os.path.join(virtualEnv, "local", "bin",
                                 "django-admin.py"),
                    os.path.join(virtualEnv, "local", "bin", "django-admin"),
                ]
                for cmd in cmds:
                    if os.path.exists(cmd):
                        break
                else:
                    cmd = ""
        else:
            if isWindowsPlatform():
                debugEnv = self.__getDebugEnvironment(language)
                cmd = os.path.join(debugEnv, "Scripts", "django-admin.py")
            else:
                if language == "Python2":
                    cmds = ["django-admin2.py", "django-admin2",
                            "django-admin.py-2.7", "django-admin.py-2.6"]
                elif language == "Python3":
                    cmds = ["django-admin3.py", "django-admin3",
                            "django-admin.py-3.4", "django-admin.py-3.3",
                            "django-admin.py-3.2"]
                else:
                    cmds = []
                cmds.extend(["django-admin.py", "django-admin"])
                for cmd in cmds:
                    if Utilities.isinpath(cmd):
                        break
                else:
                    cmd = ""
        
        return cmd
    
    def __getPythonExecutable(self):
        """
        Public method to build the Python command.
        
        @return python command (string)
        """
        language = self.__e5project.getProjectLanguage()
        pythonExe = "python"
        if isWindowsPlatform():
            pythonExe += ".exe"
        else:
            if language == "Python3":
                pythonExe = "python3"
            elif language == "Python2":
                pythonExe = "python2"
        virtualEnv = self.__getVirtualEnvironment()
        if isWindowsPlatform() and not virtualEnv:
            virtualEnv = self.__getDebugEnvironment(language)
        if virtualEnv:
            if isWindowsPlatform():
                python = os.path.join(virtualEnv, "Scripts", pythonExe)
                if not os.path.exists(python):
                    python = os.path.join(virtualEnv, pythonExe)
            else:
                python = os.path.join(virtualEnv, "bin", pythonExe)
                if not os.path.exists(python):
                    python = python[:-1]    # omit the version character
        else:
            python = pythonExe
        
        return python
    
    def __djangoInfo(self):
        """
        Private slot to show some info about Django.
        """
        version = self.getDjangoVersion()
        url = "http://www.djangoproject.com"
        
        msgBox = E5MessageBox.E5MessageBox(
            E5MessageBox.Question,
            self.trUtf8("About Django"),
            self.trUtf8(
                "<p>Django is a high-level Python Web framework that"
                " encourages rapid "
                "development and clean, pragmatic design.</p>"
                "<p><table>"
                "<tr><td>Version:</td><td>{0}</td></tr>"
                "<tr><td>URL:</td><td><a href=\"{1}\">"
                "{1}</a></td></tr>"
                "</table></p>"
            ).format(version, url),
            modal=True,
            buttons=E5MessageBox.Ok)
        msgBox.setIconPixmap(UI.PixmapCache.getPixmap(
            os.path.join("ProjectDjango", "icons", "django64.png")))
        msgBox.exec_()
    
    def getDjangoVersion(self):
        """
        Public method to get the Django version.
        
        @return Django version (string)
        """
        djangoVersion = ""
        
        args = ['--version']
        ioEncoding = Preferences.getSystem("IOEncoding")
        cmd = self.__getDjangoAdminCommand()
        if isWindowsPlatform():
            args.insert(0, cmd)
            cmd = self.__getPythonExecutable()
        
        process = QProcess()
        process.start(cmd, args)
        procStarted = process.waitForStarted()
        if procStarted:
            finished = process.waitForFinished(30000)
            if finished and process.exitCode() == 0:
                output = str(process.readAllStandardOutput(), ioEncoding,
                             'replace')
                djangoVersion = output.splitlines()[0].strip()
        
        return djangoVersion
    
    def __getApplications(self):
        """
        Private method to ask the user for a list of application names.
        
        @return list of application names (list of strings)
        """
        applStr, ok = QInputDialog.getItem(
            self.__ui,
            self.trUtf8("Select Applications"),
            self.trUtf8("Enter the list of applications separated by spaces."),
            self.getRecentApplications(),
            0, True)
        if ok and applStr != "":
            self.setMostRecentApplication(applStr)
            return applStr.split()
        else:
            return []

    def __loadRecentApplications(self):
        """
        Private method to load the recently used applications list.
        """
        self.__recentApplications = []
        Preferences.Prefs.rsettings.sync()
        ra = Preferences.Prefs.rsettings.value(self.RecentApplicationsKey)
        if ra is not None:
            maxRecentApps = self.__plugin.getPreferences("RecentNumberApps")
            self.__recentApplications = ra[:maxRecentApps]
    
    def __saveRecentApplications(self):
        """
        Private method to save the list of recently used applications list.
        """
        Preferences.Prefs.rsettings.setValue(self.RecentApplicationsKey,
                                             self.__recentApplications)
        Preferences.Prefs.rsettings.sync()
    
    def getRecentApplications(self):
        """
        Public method to get the list of recent applications.
        
        @return list of recent applications entries (list of strings)
        """
        self.__loadRecentApplications()
        return self.__recentApplications
    
    def setMostRecentApplication(self, applStr):
        """
        Public method to set the most recently used applications entry.
        
        @param applStr applications entry (string)
        """
        if applStr in self.__recentApplications:
            self.__recentApplications.remove(applStr)
        self.__recentApplications.insert(0, applStr)
        
        maxRecentApps = self.__plugin.getPreferences("RecentNumberApps")
        if len(self.__recentApplications) > maxRecentApps:
            self.__recentApplications = self.recent[:maxRecentApps]
        self.__saveRecentApplications()
    
    def getProjectPath(self):
        """
        Public method to get the path of the eric5 project.
        
        @return path of the eric5 project (string)
        """
        return self.__e5project.getProjectPath()
    
    def __showHelpIndex(self):
        """
        Private slot to show the help index page.
        """
        page = os.path.join(os.path.dirname(__file__),
                            "Documentation", "help", "index.html")
        self.__ui.launchHelpViewer(page)
    
    def __isSpawningConsole(self, consoleCmd):
        """
        Private method to check, if the given console is a spawning console.
        
        @param consoleCmd console command (string)
        @return tuple of two entries giving an indication, if the console
            is spawning (boolean) and the (possibly) cleaned console command
            (string)
        """
        if consoleCmd and consoleCmd[0] == '@':
            return (True, consoleCmd[1:])
        else:
            return (False, consoleCmd)
    
    ##################################################################
    ## slots below implement creation functions
    ##################################################################
    
    def startProjectOrApplication(self):
        """
        Public slot to start a new Django project or application.
        """
        if self.__e5project.getProjectType() == "Django":
            projectStr = self.trUtf8("Project")
            applStr = self.trUtf8("Application")
            selections = ["", projectStr, applStr]
            selection,  ok = QInputDialog.getItem(
                self.__ui,
                self.trUtf8("Start Django"),
                self.trUtf8("Select if this project should be a "
                            "Django Project or Application.<br />"
                            "Select the empty entry for none."),
                selections,
                0, False)
            if ok and bool(selection):
                if selection == projectStr:
                    path, projectName = os.path.split(
                        self.__e5project.getProjectPath())
                    self.__createProject(projectName, path)
                elif selection == applStr:
                    path, applName = os.path.split(
                        self.__e5project.getProjectPath())
                    self.__createApplication(applName, path)
    
    def __createProject(self, projectName, path):
        """
        Private slot to create a new Django project.
        
        @param projectName name of the new project (string)
        @param path the directory where the project should be created
            (string)
        @return flag indicating a successful creation (boolean)
        """
        title = self.trUtf8("Start Django Project")
        
        args = []
        if Utilities.isWindowsPlatform():
            args.append(self.__getPythonExecutable())
            args.append(self.__getDjangoAdminCommand())
        else:
            cmd = self.__getDjangoAdminCommand()
            if cmd:
                args.append(cmd)
            else:
                E5MessageBox.critical(
                    self.__ui,
                    title,
                    self.trUtf8("""<p>The <b>django-admin.py</b> script is"""
                                """ not in the path. Aborting...</p>"""))
                return
        
        args.append("startproject")
        args.append(projectName)
        
        dia = DjangoDialog(
            title,
            msgSuccess=self.trUtf8("Django project created successfully."))
        res = dia.startProcess(args, path)
        if res:
            dia.exec_()
            
            # create the base directory for translations
            i18nPath = os.path.join(path, projectName, "locale")
            if not os.path.exists(i18nPath):
                os.makedirs(i18nPath)
            
            if os.path.join(path, projectName) == \
                    self.__e5project.getProjectPath():
                self.__setCurrentSite("")
            else:
                self.__setCurrentSite(projectName)
            
        return res
    
    def __startProject(self):
        """
        Private slot to start a new Django project.
        """
        projectName, ok = QInputDialog.getText(
            self.__ui,
            self.trUtf8("Start Django Project"),
            self.trUtf8("Enter the name of the new Django project."),
            QLineEdit.Normal)
        if ok and projectName != "":
            res = self.__createProject(projectName,
                                       self.__e5project.getProjectPath())
            if res:
                # search for new files and add them to the project
                sitePath = os.path.join(self.__e5project.getProjectPath(),
                                        projectName)
                for entry in os.walk(sitePath):
                    for fileName in entry[2]:
                        fullName = os.path.join(entry[0], fileName)
                        self.__e5project.appendFile(fullName)
    
    def __createApplication(self, applName, path, isGlobal=True):
        """
        Private slot to create a new Django application.
        
        @param applName name of the new application (string)
        @param path the directory where the application should be created
            (string)
        @param isGlobal flag indicating a standalone Django application
            (boolean)
        @return flag indicating a successful creation (boolean)
        """
        title = self.trUtf8("Start Django Application")
        
        args = []
        if isGlobal:
            if Utilities.isWindowsPlatform():
                args.append(self.__getPythonExecutable())
                args.append(self.__getDjangoAdminCommand())
            else:
                cmd = self.__getDjangoAdminCommand()
                if cmd:
                    args.append(cmd)
                else:
                    E5MessageBox.critical(
                        self.__ui,
                        title,
                        self.trUtf8("""<p>The <b>django-admin.py</b> script"""
                                    """ is not in the path."""
                                    """ Aborting...</p>"""))
                    return
        else:
            args.append(self.__getPythonExecutable())
            args.append("manage.py")
            try:
                path = self.__sitePath()
            except DjangoNoSiteSelectedException:
                return False
        args.append("startapp")
        args.append(applName)
        
        dia = DjangoDialog(
            title,
            msgSuccess=self.trUtf8("Django application created successfully."))
        res = dia.startProcess(args, path)
        if res:
            dia.exec_()
        return res
    
    def __startGlobalApplication(self):
        """
        Private slot to start a new global Django application.
        """
        applName, ok = QInputDialog.getText(
            self.__ui,
            self.trUtf8("Start Global Django Application"),
            self.trUtf8("Enter the name of the new global Django"
                        " application."),
            QLineEdit.Normal)
        if ok and applName != "":
            res = self.__createApplication(applName,
                                           self.__e5project.getProjectPath())
            if res:
                # search for new files and add them to the project
                appPath = os.path.join(self.__e5project.getProjectPath(),
                                       applName)
                for entry in os.walk(appPath):
                    for fileName in entry[2]:
                        fullName = os.path.join(entry[0], fileName)
                        self.__e5project.appendFile(fullName)
    
    def __startLocalApplication(self):
        """
        Private slot to start a new local Django application.
        """
        applName, ok = QInputDialog.getText(
            self.__ui,
            self.trUtf8("Start Local Django Application"),
            self.trUtf8("Enter the name of the new local Django application."),
            QLineEdit.Normal)
        if ok and applName != "":
            res = self.__createApplication(applName, "", False)
            if res:
                try:
                    # search for new files and add them to the project
                    appPath = os.path.join(self.__sitePath(), applName)
                    for entry in os.walk(appPath):
                        for fileName in entry[2]:
                            fullName = os.path.join(entry[0], fileName)
                            self.__e5project.appendFile(fullName)
                except DjangoNoSiteSelectedException:
                    return
    
    ##################################################################
    ## methods below implement site related functions
    ##################################################################
    
    def __findSites(self):
        """
        Private method to determine the relative path to all manage.py scripts.
        
        @return list of sites (list of strings)
        """
        sites = []
        for file in sorted(self.__e5project.getSources()):
            if os.path.basename(file) == "manage.py":
                sites.append(os.path.dirname(file))
        return sites
    
    def __selectSite(self):
        """
        Private method to select a site to work with.
        """
        sites = self.__findSites()
        if len(sites) == 1:
            site = sites[0]
        else:
            if self.__currentSite is not None:
                if self.__currentSite in sites:
                    cur = sites.index(self.__currentSite)
                else:
                    cur = 0
            else:
                cur = 0
            site, ok = QInputDialog.getItem(
                self.__ui,
                self.trUtf8("Select Project"),
                self.trUtf8("Select the Django project to work with."),
                sites,
                cur, False)
            if not ok:
                site = None
        self.__setCurrentSite(site)
    
    def __sitePath(self):
        """
        Private method to calculate the full path of the Django site.
        
        @exception DjangoNoSiteSelectedException raised, if no site is selected
        @return path of the site (string)
        """
        if self.__currentSite is None:
            self.__selectSite()
        
        if self.__currentSite is None:
            raise DjangoNoSiteSelectedException
        else:
            return os.path.join(self.__e5project.getProjectPath(),
                                self.__currentSite)
    
    def __setCurrentSite(self, site):
        """
        Private slot to set the current site.
        
        @param site name of the site (string)
        """
        self.__currentSite = site
        if self.__currentSite is None:
            curSite = self.trUtf8("None")
        elif self.__currentSite == "":
            curSite = self.trUtf8("Project")
        else:
            curSite = self.__currentSite
        self.selectSiteAct.setText(
            self.trUtf8('&Current Django project ({0})').format(curSite))
        
        if self.__currentSite is None:
            self.__e5project.pdata["TRANSLATIONPATTERN"] = []
        else:
            self.__e5project.pdata["TRANSLATIONPATTERN"] = [
                os.path.join(site, "locale", "%language%", "LC_MESSAGES",
                             "django.po")
            ]
    
    def __site(self):
        """
        Private method to get the name of the current site.
        
        @exception DjangoNoSiteSelectedException raised, if no site is selected
        @return name of the site (string)
        """
        if self.__currentSite is None:
            self.__selectSite()
        
        if self.__currentSite is None:
            raise DjangoNoSiteSelectedException
        else:
            return self.__currentSite

    ##################################################################
    ## slots below implement run functions
    ##################################################################
    
    def __runServer(self):
        """
        Private slot to start the Django Web server.
        """
        consoleCmd = self.__isSpawningConsole(
            self.__plugin.getPreferences("ConsoleCommand"))[1]
        if consoleCmd:
            args = Utilities.parseOptionString(consoleCmd)
            args[0] = Utilities.getExecutablePath(args[0])
            args.append(self.__getPythonExecutable())
            args.append("manage.py")
            args.append("runserver")
            if self.__plugin.getPreferences("UseIPv6"):
                args.append("--ipv6")
            addr = self.__plugin.getPreferences("ServerAddress")
            if addr:
                args.append(addr)
            
            try:
                if Utilities.isWindowsPlatform():
                    serverProcStarted, pid = QProcess.startDetached(
                        args[0], args[1:], self.__sitePath())
                else:
                    if self.__serverProc is not None:
                        self.__serverProcFinished()
                    
                    self.__serverProc = QProcess()
                    self.__serverProc.finished.connect(
                        self.__serverProcFinished)
                    self.__serverProc.setWorkingDirectory(self.__sitePath())
                    self.__serverProc.start(args[0], args[1:])
                    serverProcStarted = self.__serverProc.waitForStarted()
                if not serverProcStarted:
                    E5MessageBox.critical(
                        None,
                        self.trUtf8('Process Generation Error'),
                        self.trUtf8('The Django server could not be started.'))
            except DjangoNoSiteSelectedException:
                pass
    
    def __serverProcFinished(self):
        """
        Private slot connected to the finished signal.
        """
        if self.__serverProc is not None and \
           self.__serverProc.state() != QProcess.NotRunning:
            self.__serverProc.terminate()
            QTimer.singleShot(2000, self.__serverProc.kill)
            self.__serverProc.waitForFinished(3000)
        self.__serverProc = None
    
    def __runBrowser(self):
        """
        Private slot to start the default web browser with the server URL.
        """
        addr = self.__plugin.getPreferences("ServerAddress")
        ipv6 = self.__plugin.getPreferences("UseIPv6")
        if addr:
            # test for an IPv6 and port address
            if ']:' in addr:
                addr, port = addr.rsplit(':', 1)
            elif ':' in addr:
                addr, port = addr.split(':', 1)
            else:
                port = addr
                if ipv6:
                    addr = "[::1]"
                else:
                    addr = "127.0.0.1"
        else:
            port = "8000"
            if ipv6:
                addr = "[::1]"
            else:
                addr = "127.0.0.1"
        url = QUrl("http://{0}:{1}".format(addr, port))
        res = QDesktopServices.openUrl(url)
        if not res:
            E5MessageBox.critical(
                None,
                self.trUtf8('Run Web-Browser'),
                self.trUtf8('Could not start the web-browser for the'
                            ' url "{0}".').format(url.toString()))

    ##################################################################
    ## slots below implement database related functions
    ##################################################################
    
    def __databaseSynchronize(self):
        """
        Private slot to synchronize the database.
        """
        consoleCmd = self.__isSpawningConsole(
            self.__plugin.getPreferences("ConsoleCommandNoClose"))[1]
        if consoleCmd:
            args = Utilities.parseOptionString(consoleCmd)
            args[0] = Utilities.getExecutablePath(args[0])
            args.append(self.__getPythonExecutable())
            args.append("manage.py")
            args.append("syncdb")
            try:
                wd = self.__sitePath()
                started, pid = QProcess.startDetached(args[0], args[1:], wd)
                if not started:
                    E5MessageBox.critical(
                        None,
                        self.trUtf8('Process Generation Error'),
                        self.trUtf8('The Django process could not be'
                                    ' started.'))
            except DjangoNoSiteSelectedException:
                pass
    
    def __databaseInspect(self):
        """
        Private slot to introspect the database and output a Django model
        module.
        """
        title = self.trUtf8("Introspect Database")
        
        args = []
        args.append(self.__getPythonExecutable())
        args.append("manage.py")
        args.append("inspectdb")
        
        try:
            path = self.__sitePath()
        except DjangoNoSiteSelectedException:
            return
        
        dia = DjangoDialog(title, fixed=True, linewrap=False)
        res = dia.startProcess(args, path, False)
        if res:
            dia.exec_()
    
    def __databaseFlush(self):
        """
        Private slot to return all database tables to the state just after
        their installation.
        """
        try:
            path = self.__sitePath()
        except DjangoNoSiteSelectedException:
            return
        
        title = self.trUtf8("Flush Database")
        
        res = E5MessageBox.yesNo(
            self.__ui,
            title,
            self.trUtf8("""Flushing the database will destroy all data."""
                        """ Are you sure?"""))
        if res:
            args = []
            args.append(self.__getPythonExecutable())
            args.append("manage.py")
            args.append("flush")
            args.append("--noinput")
            
            dia = DjangoDialog(
                title,
                msgSuccess=self.trUtf8("Database tables flushed"
                                       " successfully."))
            res = dia.startProcess(args, path)
            if res:
                dia.exec_()
    
    def __runDatabaseClient(self):
        """
        Private slot to start a database client for a Django project.
        """
        consoleCmd = self.__isSpawningConsole(
            self.__plugin.getPreferences("ConsoleCommand"))[1]
        if consoleCmd:
            args = Utilities.parseOptionString(consoleCmd)
            args[0] = Utilities.getExecutablePath(args[0])
            args.append(self.__getPythonExecutable())
            args.append("manage.py")
            args.append("dbshell")
            try:
                wd = self.__sitePath()
                started, pid = QProcess.startDetached(args[0], args[1:], wd)
                if not started:
                    E5MessageBox.critical(
                        None,
                        self.trUtf8('Process Generation Error'),
                        self.trUtf8('The Django process could not be'
                                    ' started.'))
            except DjangoNoSiteSelectedException:
                pass
    
    #######################################################################
    ## slots below implement database functions outputting SQL statements
    #######################################################################
    
    def __sqlCommand(self, title, command, requestApps=True):
        """
        Private method to perform an SQL creation function.
        
        @param title dialog title (string)
        @param command Django sql... command (string)
        @param requestApps flag indicating to request a list of applications
            to work on (boolean)
        """
        try:
            path = self.__sitePath()
        except DjangoNoSiteSelectedException:
            return
        
        if requestApps:
            apps = self.__getApplications()
            if not apps:
                return
        else:
            apps = []
        
        args = []
        args.append(self.__getPythonExecutable())
        args.append("manage.py")
        args.append(command)
        args += apps
        
        fileFilter = self.trUtf8("SQL Files (*.sql)")
        
        dia = DjangoDialog(title, fixed=True, linewrap=False,
                           saveFilters=fileFilter)
        res = dia.startProcess(args, path, False)
        if res:
            dia.exec_()
    
    def __databaseSqlCreateTables(self):
        """
        Private slot to print the CREATE TABLE SQL statements for one
        or more applications.
        """
        self.__sqlCommand(self.trUtf8("Create Tables"), "sql")
    
    def __databaseSqlCreateIndexes(self):
        """
        Private slot to print the CREATE INDEX SQL statements for one
        or more applications.
        """
        self.__sqlCommand(self.trUtf8("Create Indexes"), "sqlindexes")
    
    def __databaseSqlCreateEverything(self):
        """
        Private slot to print the CREATE TABLE, custom SQL and
        CREATE INDEX SQL statements for one or more applications.
        """
        self.__sqlCommand(self.trUtf8("Create Everything"), "sqlall")
    
    def __databaseSqlCustom(self):
        """
        Private slot to print the custom table modifying SQL statements
        for one or more applications.
        """
        self.__sqlCommand(self.trUtf8("Custom Statements"), "sqlcustom")
    
    def __databaseSqlDropTables(self):
        """
        Private slot to print the DROP TABLE SQL statements for one or
        more applications.
        """
        self.__sqlCommand(self.trUtf8("Drop Tables"), "sqlclear")
    
    def __databaseSqlFlushDatabase(self):
        """
        Private slot to print a list of statements to return all database
        tables to their initial state.
        """
        self.__sqlCommand(self.trUtf8("Flush Database"), "sqlflush", False)
    
    def __databaseSqlResetSequences(self):
        """
        Private slot to print the SQL statements for resetting sequences for
        one or more applications.
        """
        self.__sqlCommand(self.trUtf8("Reset Sequences"), "sqlsequencereset")
    
    ##################################################################
    ## slots below implement some tool functions
    ##################################################################
    
    def __diffSettings(self):
        """
        Private slot to show the changes made to the settings.py file.
        """
        title = self.trUtf8("Diff Settings")
        
        args = []
        args.append(self.__getPythonExecutable())
        args.append("manage.py")
        args.append("diffsettings")
        
        try:
            path = self.__sitePath()
        except DjangoNoSiteSelectedException:
            return
        
        dia = DjangoDialog(title, fixed=True, linewrap=False)
        res = dia.startProcess(args, path, False)
        if res:
            dia.exec_()
    
    def __cleanup(self):
        """
        Private slot to clean out old data from the database.
        """
        title = self.trUtf8("Cleanup")
        
        args = []
        args.append(self.__getPythonExecutable())
        args.append("manage.py")
        args.append("cleanup")
        
        try:
            path = self.__sitePath()
        except DjangoNoSiteSelectedException:
            return
        
        dia = DjangoDialog(
            title,
            msgSuccess=self.trUtf8("Database cleaned up successfully."))
        res = dia.startProcess(args, path)
        if res:
            dia.exec_()
    
    def __validate(self):
        """
        Private slot to validate all installed models.
        """
        title = self.trUtf8("Validate")
        
        args = []
        args.append(self.__getPythonExecutable())
        args.append("manage.py")
        args.append("validate")
        
        try:
            path = self.__sitePath()
        except DjangoNoSiteSelectedException:
            return
        
        dia = DjangoDialog(title)
        res = dia.startProcess(args, path)
        if res:
            dia.exec_()
    
    def __runPythonShell(self):
        """
        Private slot to start a Python console for a Django project.
        """
        consoleCmd = self.__isSpawningConsole(
            self.__plugin.getPreferences("ConsoleCommand"))[1]
        if consoleCmd:
            args = Utilities.parseOptionString(consoleCmd)
            args[0] = Utilities.getExecutablePath(args[0])
            args.append(self.__getPythonExecutable())
            args.append("manage.py")
            args.append("shell")
            language = self.__e5project.getProjectLanguage()
            if language == "Python2":
                if self.__plugin.getPreferences("UsePlainPythonPy2"):
                    args.append("--plain")
            else:
                if self.__plugin.getPreferences("UsePlainPythonPy3"):
                    args.append("--plain")
            try:
                wd = self.__sitePath()
                started, pid = QProcess.startDetached(args[0], args[1:], wd)
                if not started:
                    E5MessageBox.critical(
                        None,
                        self.trUtf8('Process Generation Error'),
                        self.trUtf8('The Django process could not be'
                                    ' started.'))
            except DjangoNoSiteSelectedException:
                pass
    
    ##################################################################
    ## slots below implement caching functions
    ##################################################################
    
    def __createCacheTables(self):
        """
        Private slot to create the tables for the SQL caching backend.
        """
        title = self.trUtf8("Create Cache Tables")
        
        try:
            wd = self.__sitePath()
        except DjangoNoSiteSelectedException:
            return
        
        tblStr, ok = QInputDialog.getText(
            self.__ui,
            title,
            self.trUtf8("Enter the names of the cache tables separated by"
                        " spaces."),
            QLineEdit.Normal)
        if ok and tblStr != "":
            tableNames = tblStr.split()
            
            args = []
            args.append(self.__getPythonExecutable())
            args.append("manage.py")
            args.append("createcachetable")
            args += tableNames
            
            dia = DjangoDialog(
                title,
                msgSuccess=self.trUtf8("Cache tables created successfully."))
            res = dia.startProcess(args, wd)
            if res:
                dia.exec_()
    
    ##################################################################
    ## slots below implement testing functions
    ##################################################################
    
    def __dumpData(self):
        """
        Private slot to dump the database data to a fixture.
        """
        title = self.trUtf8("Dump Data")
        
        try:
            wd = self.__sitePath()
        except DjangoNoSiteSelectedException:
            return
        
        from .DjangoDumpdataDataDialog import DjangoDumpdataDataDialog
        dlg = DjangoDumpdataDataDialog(self, self.__ui)
        if dlg.exec_() == QDialog.Accepted:
            appls, excls, format, indent = dlg.getData()
            
            args = []
            args.append(self.__getPythonExecutable())
            args.append("manage.py")
            args.append("dumpdata")
            args.append("--format={0}".format(format))
            args.append("--indent={0}".format(indent))
            for excl in excls:
                args.append("--exclude={0}".format(excl))
            args += appls
            
            if format == "json":
                fileFilters = self.trUtf8("JSON Files (*.json)")
            elif format == "xml":
                fileFilters = self.trUtf8("XML Files (*.xml)")
            elif format == "yaml":
                fileFilters = self.trUtf8("YAML Files (*.yaml)")
            
            dia = DjangoDialog(
                title, fixed=True, linewrap=False, saveFilters=fileFilters)
            res = dia.startProcess(args, wd, showCommand=False)
            if res:
                dia.exec_()
    
    def __loadData(self):
        """
        Private slot to load data from fixture files.
        """
        title = self.trUtf8("Load Data")
        
        try:
            wd = self.__sitePath()
        except DjangoNoSiteSelectedException:
            return
        
        from .DjangoLoaddataDataDialog import DjangoLoaddataDataDialog
        dlg = DjangoLoaddataDataDialog(self, self.__ui)
        if dlg.exec_() == QDialog.Accepted:
            fixtures = dlg.getData()
            
            args = []
            args.append(self.__getPythonExecutable())
            args.append("manage.py")
            args.append("loaddata")
            args += fixtures
            
            dia = DjangoDialog(title)
            res = dia.startProcess(args, wd)
            if res:
                dia.exec_()
    
    def __runTestSuite(self):
        """
        Private slot to run the test suite for applications or the whole site.
        """
        consoleCmd = self.__isSpawningConsole(
            self.__plugin.getPreferences("ConsoleCommandNoClose"))[1]
        if consoleCmd:
            try:
                wd = self.__sitePath()
            except DjangoNoSiteSelectedException:
                return
            
            args = Utilities.parseOptionString(consoleCmd)
            args[0] = Utilities.getExecutablePath(args[0])
            args.append(self.__getPythonExecutable())
            args.append("manage.py")
            args.append("test")
            args += self.__getApplications()
            
            started, pid = QProcess.startDetached(args[0], args[1:], wd)
            if not started:
                E5MessageBox.critical(
                    None,
                    self.trUtf8('Process Generation Error'),
                    self.trUtf8('The Django process could not be started.'))
    
    def __runTestServer(self):
        """
        Private slot to run a development server with data from a set of
        fixtures.
        """
        consoleCmd = self.__isSpawningConsole(
            self.__plugin.getPreferences("ConsoleCommand"))[1]
        if consoleCmd:
            from .DjangoLoaddataDataDialog import DjangoLoaddataDataDialog
            dlg = DjangoLoaddataDataDialog(self, self.__ui)
            if dlg.exec_() == QDialog.Accepted:
                fixtures = dlg.getData()
                
                args = Utilities.parseOptionString(consoleCmd)
                args[0] = Utilities.getExecutablePath(args[0])
                args.append(self.__getPythonExecutable())
                args.append("manage.py")
                args.append("testserver")
                if self.__plugin.getPreferences("UseIPv6"):
                    args.append("--ipv6")
                addr = self.__plugin.getPreferences("ServerAddress")
                if addr:
                    args.append("--addrport=%s" % addr)
                args += fixtures
                
                try:
                    if Utilities.isWindowsPlatform():
                        serverProcStarted, pid = QProcess.startDetached(
                            args[0], args[1:], self.__sitePath())
                    else:
                        if self.__testServerProc is not None:
                            self.__testServerProcFinished()
                        
                        self.__testServerProc = QProcess()
                        self.__testServerProc.finished.connect(
                            self.__serverProcFinished)
                        self.__testServerProc.setWorkingDirectory(
                            self.__sitePath())
                        self.__testServerProc.start(args[0], args[1:])
                        serverProcStarted = \
                            self.__testServerProc.waitForStarted()
                    if not serverProcStarted:
                        E5MessageBox.critical(
                            None,
                            self.trUtf8('Process Generation Error'),
                            self.trUtf8('The Django test server could not be'
                                        ' started.'))
                except DjangoNoSiteSelectedException:
                    pass
    
    def __testServerProcFinished(self):
        """
        Private slot connected to the finished signal of the test server.
        """
        if self.__testServerProc is not None and \
           self.__testServerProc.state() != QProcess.NotRunning:
            self.__testServerProc.terminate()
            QTimer.singleShot(2000, self.__testServerProc.kill)
            self.__testServerProc.waitForFinished(3000)
        self.__testServerProc = None
    
    ##################################################################
    ## slots below implement authorization functions
    ##################################################################
    
    def __changePassword(self):
        """
        Private slot to change the password of a user.
        """
        consoleCmd = self.__isSpawningConsole(
            self.__plugin.getPreferences("ConsoleCommandNoClose"))[1]
        if consoleCmd:
            userName, ok = QInputDialog.getText(
                self.__ui,
                self.trUtf8("Change Password"),
                self.trUtf8("Enter the name of the user:"),
                QLineEdit.Normal)
            if ok and userName != "":
                args = Utilities.parseOptionString(consoleCmd)
                args[0] = Utilities.getExecutablePath(args[0])
                args.append(self.__getPythonExecutable())
                args.append("manage.py")
                args.append("changepassword")
                args.append(userName)
                try:
                    wd = self.__sitePath()
                    started, pid = QProcess.startDetached(
                        args[0], args[1:], wd)
                    if not started:
                        E5MessageBox.critical(
                            None,
                            self.trUtf8('Process Generation Error'),
                            self.trUtf8('The Django process could not be'
                                        ' started.'))
                except DjangoNoSiteSelectedException:
                    pass
    
    def __createSuperUser(self):
        """
        Private slot to create a super user account.
        """
        consoleCmd = self.__isSpawningConsole(
            self.__plugin.getPreferences("ConsoleCommandNoClose"))[1]
        if consoleCmd:
            args = Utilities.parseOptionString(consoleCmd)
            args[0] = Utilities.getExecutablePath(args[0])
            args.append(self.__getPythonExecutable())
            args.append("manage.py")
            args.append("createsuperuser")
            try:
                wd = self.__sitePath()
                started, pid = QProcess.startDetached(args[0], args[1:], wd)
                if not started:
                    E5MessageBox.critical(
                        None,
                        self.trUtf8('Process Generation Error'),
                        self.trUtf8('The Django process could not be'
                                    ' started.'))
            except DjangoNoSiteSelectedException:
                pass
    
    ##################################################################
    ## slots below implement session functions
    ##################################################################
    
    def __clearSessions(self):
        """
        Private slot to clear expired sessions.
        """
        title = self.trUtf8("Clear Sessions")
        
        try:
            wd = self.__sitePath()
        except DjangoNoSiteSelectedException:
            return
        
        args = []
        args.append(self.__getPythonExecutable())
        args.append("manage.py")
        args.append("clearsessions")
        
        dia = DjangoDialog(
            title,
            msgSuccess=self.trUtf8("Expired sessions cleared successfully."))
        res = dia.startProcess(args, wd)
        if res:
            dia.exec_()
    
    ##################################################################
    ## slots below implement translation functions
    ##################################################################
    
    def __getLocale(self, filename):
        """
        Private method to extract the locale out of a file name.
        
        @param filename name of the file used for extraction (string)
        @return extracted locale (string) or None
        """
        if self.__e5project.pdata["TRANSLATIONPATTERN"]:
            pattern = self.__e5project.pdata["TRANSLATIONPATTERN"][0]\
                .replace("%language%", "(.*?)")
            match = re.search(pattern, filename)
            if match is not None:
                loc = match.group(1)
                return loc
            else:
                loc = None
        else:
            loc = None
        
        return loc
    
    def __normalizeList(self, filenames):
        """
        Private method to normalize a list of file names.
        
        @param filenames list of file names to normalize (list of strings)
        @return normalized file names (list of strings)
        """
        nfilenames = []
        for filename in filenames:
            if filename.endswith(".mo"):
                filename = filename.replace(".mo", ".po")
            if filename not in nfilenames:
                nfilenames.append(filename)
        
        return nfilenames
    
    def __siteFilteredList(self, filenames):
        """
        Private method to filter a list of file names by site.
        
        @param filenames list of file names to be filtered (list of strings)
        @return file names belonging to the current site (list of strings)
        """
        site = self.__site()
        nfilenames = []
        for filename in filenames:
            if site == "" or filename.startswith(site + os.sep):
                nfilenames.append(filename)
        
        return nfilenames
    
    def __projectLanguageAdded(self, code):
        """
        Private slot handling the addition of a new language.
        
        @param code language code of the new language (string)
        """
        title = self.trUtf8("Initializing message catalog for '{0}'")\
            .format(code)
        
        args = []
        args.append(self.__getPythonExecutable())
        args.append("manage.py")
        args.append("makemessages")
        args.append("-l")
        args.append(code)
        
        try:
            wd = self.__sitePath()
        except DjangoNoSiteSelectedException:
            E5MessageBox.warning(
                None,
                title,
                self.trUtf8('No current site selected or no site created yet.'
                            ' Aborting...'))
            return
        
        dia = DjangoDialog(
            title,
            msgSuccess=self.trUtf8(
                "\nMessage catalog initialized successfully."))
        res = dia.startProcess(args, wd)
        if res:
            dia.exec_()
            
            langFile = self.__e5project.pdata["TRANSLATIONPATTERN"][0]\
                .replace("%language%", code)
            self.__e5project.appendFile(langFile)
    
    def updateSelectedCatalogs(self, filenames):
        """
        Public method to update the message catalogs.
        
        @param filenames list of file names (list of strings)
        """
        title = self.trUtf8("Updating message catalogs")
        
        try:
            wd = self.__sitePath()
        except DjangoNoSiteSelectedException:
            E5MessageBox.warning(
                None,
                title,
                self.trUtf8('No current site selected or no site created yet.'
                            ' Aborting...'))
            return
        
        argsLists = []
        
        for filename in self.__normalizeList(
                self.__siteFilteredList(filenames)):
            locale = self.__getLocale(filename)
            if locale:
                args = []
                args.append(self.__getPythonExecutable())
                args.append("manage.py")
                args.append("makemessages")
                args.append("--no-obsolete")
                args.append("-l")
                args.append(locale)
                argsLists.append(args)
        
        if len(argsLists) == 0:
            E5MessageBox.warning(
                None,
                title,
                self.trUtf8('No locales detected. Aborting...'))
            return
        
        dia = DjangoDialog(
            title,
            msgSuccess=self.trUtf8("\nMessage catalogs updated successfully."))
        res = dia.startBatchProcesses(argsLists, wd)
        if res:
            dia.exec_()
    
    def updateSelectedCatalogsWithObsolete(self, filenames):
        """
        Public method to update the message catalogs keeping obsolete messages.
        
        @param filenames list of filenames
        """
        title = self.trUtf8("Updating message catalogs (keeping obsolete"
                            " messages)")
        
        try:
            wd = self.__sitePath()
        except DjangoNoSiteSelectedException:
            E5MessageBox.warning(
                None,
                title,
                self.trUtf8('No current site selected or no site created yet.'
                            ' Aborting...'))
            return
        
        argsLists = []
        
        for filename in self.__normalizeList(
                self.__siteFilteredList(filenames)):
            locale = self.__getLocale(filename)
            if locale:
                args = []
                args.append(self.__getPythonExecutable())
                args.append("manage.py")
                args.append("makemessages")
                args.append("-l")
                args.append(locale)
                argsLists.append(args)
        
        if len(argsLists) == 0:
            E5MessageBox.warning(
                None,
                title,
                self.trUtf8('No locales detected. Aborting...'))
            return
        
        dia = DjangoDialog(
            title,
            msgSuccess=self.trUtf8("\nMessage catalogs updated successfully."))
        res = dia.startBatchProcesses(argsLists, wd)
        if res:
            dia.exec_()
    
    def updateCatalogs(self, filenames):
        """
        Public method to update the message catalogs.
        
        @param filenames list of filenames (not used)
        """
        title = self.trUtf8("Updating message catalogs")
        
        args = []
        args.append(self.__getPythonExecutable())
        args.append("manage.py")
        args.append("makemessages")
        args.append("-a")
        args.append("--no-obsolete")
        
        try:
            wd = self.__sitePath()
        except DjangoNoSiteSelectedException:
            E5MessageBox.warning(
                None,
                title,
                self.trUtf8('No current site selected or no site created yet.'
                            ' Aborting...'))
            return
        
        dia = DjangoDialog(
            title,
            msgSuccess=self.trUtf8("\nMessage catalogs updated successfully."))
        res = dia.startProcess(args, wd)
        if res:
            dia.exec_()
    
    def updateCatalogsWithObsolete(self, filenames):
        """
        Public method to update the message catalogs keeping obsolete messages.
        
        @param filenames list of filenames (not used)
        """
        title = self.trUtf8("Updating message catalogs (keeping obsolete"
                            " messages)")
        
        args = []
        args.append(self.__getPythonExecutable())
        args.append("manage.py")
        args.append("makemessages")
        args.append("-a")
        
        try:
            wd = self.__sitePath()
        except DjangoNoSiteSelectedException:
            E5MessageBox.warning(
                None,
                title,
                self.trUtf8('No current site selected or no site created yet.'
                            ' Aborting...'))
            return
        
        dia = DjangoDialog(
            title,
            msgSuccess=self.trUtf8("\nMessage catalogs updated successfully."))
        res = dia.startProcess(args, wd)
        if res:
            dia.exec_()
    
    def compileSelectedCatalogs(self, filenames):
        """
        Public method to update the message catalogs.
        
        @param filenames list of filenames
        """
        title = self.trUtf8("Compiling message catalogs")
        
        try:
            wd = self.__sitePath()
        except DjangoNoSiteSelectedException:
            E5MessageBox.warning(
                None,
                title,
                self.trUtf8('No current site selected or no site created yet.'
                            ' Aborting...'))
            return
        
        argsLists = []
        
        for filename in self.__normalizeList(
                self.__siteFilteredList(filenames)):
            locale = self.__getLocale(filename)
            if locale:
                args = []
                args.append(self.__getPythonExecutable())
                args.append("manage.py")
                args.append("compilemessages")
                args.append("-l")
                args.append(locale)
                argsLists.append(args)
        
        if len(argsLists) == 0:
            E5MessageBox.warning(
                None,
                title,
                self.trUtf8('No locales detected. Aborting...'))
            return
        
        dia = DjangoDialog(
            title,
            msgSuccess=self.trUtf8(
                "\nMessage catalogs compiled successfully."))
        res = dia.startBatchProcesses(argsLists, wd, mergedOutput=True)
        if res:
            dia.exec_()
            
            for entry in os.walk(self.__sitePath()):
                for fileName in entry[2]:
                    fullName = os.path.join(entry[0], fileName)
                    if fullName.endswith('.mo'):
                        self.__e5project.appendFile(fullName)
    
    def compileCatalogs(self, filenames):
        """
        Public method to compile the message catalogs.
        
        @param filenames list of filenames (not used)
        """
        title = self.trUtf8("Compiling message catalogs")
        
        args = []
        args.append(self.__getPythonExecutable())
        args.append("manage.py")
        args.append("compilemessages")
        
        try:
            wd = self.__sitePath()
        except DjangoNoSiteSelectedException:
            E5MessageBox.warning(
                None,
                title,
                self.trUtf8('No current site selected or no site created yet.'
                            ' Aborting...'))
            return
        
        dia = DjangoDialog(
            title,
            msgSuccess=self.trUtf8(
                "\nMessage catalogs compiled successfully."))
        res = dia.startProcess(args, wd, mergedOutput=True)
        if res:
            dia.exec_()
            
            for entry in os.walk(self.__sitePath()):
                for fileName in entry[2]:
                    fullName = os.path.join(entry[0], fileName)
                    if fullName.endswith('.mo'):
                        self.__e5project.appendFile(fullName)
    
    def openPOEditor(self, poFile):
        """
        Public method to edit the given file in an external .po editor.
        
        @param poFile name of the .po file (string)
        """
        editor = self.__plugin.getPreferences("TranslationsEditor")
        if poFile.endswith(".po") and editor:
            try:
                wd = self.__sitePath()
            except DjangoNoSiteSelectedException:
                wd = ""
            started, pid = QProcess.startDetached(editor, [poFile], wd)
            if not started:
                E5MessageBox.critical(
                    None,
                    self.trUtf8('Process Generation Error'),
                    self.trUtf8('The translations editor process ({0}) could'
                                ' not be started.')
                    .format(os.path.basename(editor)))

eric ide

mercurial