Preferences dialogue. Patch by Ashesh and Khushboo Vashi.
							parent
							
								
									43116750b4
								
							
						
					
					
						commit
						5ea822f33e
					
				| 
						 | 
				
			
			@ -138,7 +138,7 @@ MAX_SESSION_IDLE_TIME = 60
 | 
			
		|||
 | 
			
		||||
# The schema version number for the configuration database
 | 
			
		||||
# DO NOT CHANGE UNLESS YOU ARE A PGADMIN DEVELOPER!!
 | 
			
		||||
SETTINGS_SCHEMA_VERSION = 7
 | 
			
		||||
SETTINGS_SCHEMA_VERSION = 8
 | 
			
		||||
 | 
			
		||||
# The default path to the SQLite database used to store user accounts and
 | 
			
		||||
# settings. This default places the file in the same directory as this
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ from flask.ext.security import Security, SQLAlchemyUserDatastore
 | 
			
		|||
from flask_security.utils import login_user
 | 
			
		||||
from flask_mail import Mail
 | 
			
		||||
from htmlmin.minify import html_minify
 | 
			
		||||
from pgadmin.settings.settings_model import db, Role, User, Version
 | 
			
		||||
from pgadmin.model import db, Role, User, Version
 | 
			
		||||
from importlib import import_module
 | 
			
		||||
from werkzeug.local import LocalProxy
 | 
			
		||||
from pgadmin.utils import PgAdminModule, driver
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,8 +29,11 @@ except:
 | 
			
		|||
 | 
			
		||||
MODULE_NAME = 'browser'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BrowserModule(PgAdminModule):
 | 
			
		||||
 | 
			
		||||
    LABEL = gettext('Browser')
 | 
			
		||||
 | 
			
		||||
    def get_own_stylesheets(self):
 | 
			
		||||
        stylesheets = []
 | 
			
		||||
        # Add browser stylesheets
 | 
			
		||||
| 
						 | 
				
			
			@ -168,20 +171,56 @@ class BrowserModule(PgAdminModule):
 | 
			
		|||
            scripts.extend(module.get_own_javascripts())
 | 
			
		||||
        return scripts
 | 
			
		||||
 | 
			
		||||
    def register_preferences(self):
 | 
			
		||||
        self.show_system_objects = self.preference.register(
 | 
			
		||||
            'display', 'show_system_objects',
 | 
			
		||||
            gettext("Show system objects"), 'boolean', False,
 | 
			
		||||
            category_label=gettext('Display')
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
blueprint = BrowserModule(MODULE_NAME, __name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@six.add_metaclass(ABCMeta)
 | 
			
		||||
class BrowserPluginModule(PgAdminModule):
 | 
			
		||||
    """
 | 
			
		||||
    Base class for browser submodules.
 | 
			
		||||
    Abstract base class for browser submodules.
 | 
			
		||||
 | 
			
		||||
    It helps to define the node for each and every node comes under the browser
 | 
			
		||||
    tree. It makes sure every module comes under browser will have prefix
 | 
			
		||||
    '/browser', and sets the 'url_prefix', 'static_url_path', etc.
 | 
			
		||||
 | 
			
		||||
    Also, creates some of the preferences to be used by the node.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    browser_url_prefix = blueprint.url_prefix + '/'
 | 
			
		||||
    SHOW_ON_BROWSER = True
 | 
			
		||||
 | 
			
		||||
    def __init__(self, import_name, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Construct a new 'BrowserPluginModule' object.
 | 
			
		||||
 | 
			
		||||
        :param import_name: Name of the module
 | 
			
		||||
        :param **kwargs:    Extra parameters passed to the base class
 | 
			
		||||
                            pgAdminModule.
 | 
			
		||||
 | 
			
		||||
        :return: returns nothing
 | 
			
		||||
 | 
			
		||||
        It sets the url_prefix to based on the 'node_path'. And,
 | 
			
		||||
        static_url_path to relative path to '/static'.
 | 
			
		||||
 | 
			
		||||
        Every module extended from this will be identified as 'NODE-<type>'.
 | 
			
		||||
 | 
			
		||||
        Also, create a preference 'show_node_<type>' to fetch whether it
 | 
			
		||||
        can be shown in the browser or not. Also,  refer to the browser-preference.
 | 
			
		||||
        """
 | 
			
		||||
        kwargs.setdefault("url_prefix", self.node_path)
 | 
			
		||||
        kwargs.setdefault("static_url_path", '/static')
 | 
			
		||||
 | 
			
		||||
        self.browser_preference = None
 | 
			
		||||
        self.pref_show_system_objects = None
 | 
			
		||||
        self.pref_show_node = None
 | 
			
		||||
 | 
			
		||||
        super(BrowserPluginModule, self).__init__(
 | 
			
		||||
                                            "NODE-%s" % self.node_type,
 | 
			
		||||
                                            import_name,
 | 
			
		||||
| 
						 | 
				
			
			@ -196,6 +235,24 @@ class BrowserPluginModule(PgAdminModule):
 | 
			
		|||
        return []
 | 
			
		||||
 | 
			
		||||
    def get_own_javascripts(self):
 | 
			
		||||
        """
 | 
			
		||||
        Returns the list of javascripts information used by the module.
 | 
			
		||||
 | 
			
		||||
        Each javascripts information must contain name, path of the script.
 | 
			
		||||
 | 
			
		||||
        The name must be unique for each module, hence - in order to refer them
 | 
			
		||||
        properly, we do use 'pgadmin.node.<type>' as norm.
 | 
			
		||||
 | 
			
		||||
        That can also refer to when to load the script.
 | 
			
		||||
 | 
			
		||||
        i.e.
 | 
			
		||||
        We may not need to load the javascript of table node, when we're
 | 
			
		||||
        not yet connected to a server, and no database is loaded. Hence - it
 | 
			
		||||
        make sense to load them when a database is loaded.
 | 
			
		||||
 | 
			
		||||
        We may also add 'deps', which also refers to the list of javascripts,
 | 
			
		||||
        it may depends on.
 | 
			
		||||
        """
 | 
			
		||||
        scripts = []
 | 
			
		||||
 | 
			
		||||
        scripts.extend([{
 | 
			
		||||
| 
						 | 
				
			
			@ -211,6 +268,27 @@ class BrowserPluginModule(PgAdminModule):
 | 
			
		|||
    def generate_browser_node(
 | 
			
		||||
            self, node_id, parent_id, label, icon, inode, node_type, **kwargs
 | 
			
		||||
            ):
 | 
			
		||||
        """
 | 
			
		||||
        Helper function to create a browser node for this particular subnode.
 | 
			
		||||
 | 
			
		||||
        :param node_id:   Unique Id for each node
 | 
			
		||||
        :param parent_id: Id of the parent.
 | 
			
		||||
        :param label:     Label for the node
 | 
			
		||||
        :param icon:      Icon for displaying along with this node on browser
 | 
			
		||||
                          tree. Icon refers to a class name, it refers to.
 | 
			
		||||
        :param inode:     True/False.
 | 
			
		||||
                          Used by the browser tree node to check, if the
 | 
			
		||||
                          current node will have children or not.
 | 
			
		||||
        :param node_type: String to refer to the node type.
 | 
			
		||||
        :param **kwargs:  A node can have extra information other than this
 | 
			
		||||
                          data, which can be passed as key-value pair as
 | 
			
		||||
                          argument here.
 | 
			
		||||
                          i.e. A database, server node can have extra
 | 
			
		||||
                          information like connected, or not.
 | 
			
		||||
 | 
			
		||||
        Returns a dictionary object representing this node object for the
 | 
			
		||||
        browser tree.
 | 
			
		||||
        """
 | 
			
		||||
        obj = {
 | 
			
		||||
                "id": "%s/%s" % (node_type, node_id),
 | 
			
		||||
                "label": label,
 | 
			
		||||
| 
						 | 
				
			
			@ -269,12 +347,66 @@ class BrowserPluginModule(PgAdminModule):
 | 
			
		|||
 | 
			
		||||
    @property
 | 
			
		||||
    def node_path(self):
 | 
			
		||||
        """
 | 
			
		||||
        Defines the url path prefix for this submodule.
 | 
			
		||||
        """
 | 
			
		||||
        return self.browser_url_prefix + self.node_type
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def javascripts(self):
 | 
			
		||||
        """
 | 
			
		||||
        Override the javascript of PgAdminModule, so that - we don't return
 | 
			
		||||
        javascripts from the get_own_javascripts itself.
 | 
			
		||||
        """
 | 
			
		||||
        return []
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def label(self):
 | 
			
		||||
        """
 | 
			
		||||
        Module label.
 | 
			
		||||
        """
 | 
			
		||||
        return self.LABEL
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def show_node(self):
 | 
			
		||||
        """
 | 
			
		||||
        A proper to check to show node for this module on the browser tree or not.
 | 
			
		||||
 | 
			
		||||
        Relies on show_node preference object, otherwise on the SHOW_ON_BROWSER
 | 
			
		||||
        default value.
 | 
			
		||||
        """
 | 
			
		||||
        if self.pref_show_node:
 | 
			
		||||
            return self.pref_show_node.get()
 | 
			
		||||
        else:
 | 
			
		||||
            return self.SHOW_ON_BROWSER
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def show_system_objects(self):
 | 
			
		||||
        """
 | 
			
		||||
        Show/Hide the system objects in the database server.
 | 
			
		||||
        """
 | 
			
		||||
        if self.pref_show_system_objects:
 | 
			
		||||
            return self.pref_show_system_objects.get()
 | 
			
		||||
        else:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def register_preferences(self):
 | 
			
		||||
        """
 | 
			
		||||
        Registers the preferences object for this module.
 | 
			
		||||
 | 
			
		||||
        Sets the browser_preference, show_system_objects, show_node preference
 | 
			
		||||
        objects for this submodule.
 | 
			
		||||
        """
 | 
			
		||||
        # Add the node informaton for browser, not in respective node preferences
 | 
			
		||||
        self.browser_preference = blueprint.preference
 | 
			
		||||
        self.pref_show_system_objects = blueprint.preference.preference(
 | 
			
		||||
            'display', 'show_system_objects'
 | 
			
		||||
            )
 | 
			
		||||
        self.pref_show_node = self.browser_preference.preference(
 | 
			
		||||
            'node', 'show_node_' + self.node_type,
 | 
			
		||||
            self.label, 'boolean', self.SHOW_ON_BROWSER, category_label=gettext('Nodes')
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@blueprint.route("/")
 | 
			
		||||
@login_required
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,8 @@ from flask.ext.babel import gettext
 | 
			
		|||
from pgadmin.utils import PgAdminModule
 | 
			
		||||
from pgadmin.browser.utils import PGChildModule
 | 
			
		||||
from pgadmin.browser import BrowserPluginModule
 | 
			
		||||
from pgadmin.utils.preferences import Preferences
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@six.add_metaclass(ABCMeta)
 | 
			
		||||
class CollectionNodeModule(PgAdminModule, PGChildModule):
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +23,7 @@ class CollectionNodeModule(PgAdminModule, PGChildModule):
 | 
			
		|||
    Base class for collection node submodules.
 | 
			
		||||
    """
 | 
			
		||||
    browser_url_prefix = BrowserPluginModule.browser_url_prefix
 | 
			
		||||
    SHOW_ON_BROWSER = True
 | 
			
		||||
 | 
			
		||||
    def __init__(self, import_name, **kwargs):
 | 
			
		||||
        kwargs.setdefault("url_prefix", self.node_path)
 | 
			
		||||
| 
						 | 
				
			
			@ -123,6 +126,10 @@ class CollectionNodeModule(PgAdminModule, PGChildModule):
 | 
			
		|||
        """
 | 
			
		||||
        return self.COLLECTION_LABEL
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def label(self):
 | 
			
		||||
        return self.COLLECTION_LABEL
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def collection_icon(self):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -173,3 +180,47 @@ class CollectionNodeModule(PgAdminModule, PGChildModule):
 | 
			
		|||
    @property
 | 
			
		||||
    def javascripts(self):
 | 
			
		||||
        return []
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def show_node(self):
 | 
			
		||||
        """
 | 
			
		||||
        Property to check whether to show the node for this module on the
 | 
			
		||||
        browser tree or not.
 | 
			
		||||
 | 
			
		||||
        Relies on show_node preference object, otherwise on the SHOW_ON_BROWSER
 | 
			
		||||
        default value.
 | 
			
		||||
        """
 | 
			
		||||
        if self.pref_show_node:
 | 
			
		||||
            return self.pref_show_node.get()
 | 
			
		||||
        else:
 | 
			
		||||
            return self.SHOW_ON_BROWSER
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def show_system_objects(self):
 | 
			
		||||
        """
 | 
			
		||||
        Show/Hide the system objects in the database server.
 | 
			
		||||
        """
 | 
			
		||||
        if self.pref_show_system_objects:
 | 
			
		||||
            return self.pref_show_system_objects.get()
 | 
			
		||||
        else:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def register_preferences(self):
 | 
			
		||||
        """
 | 
			
		||||
        register_preferences
 | 
			
		||||
        Register preferences for this module.
 | 
			
		||||
 | 
			
		||||
        Keep the browser preference object to be used by overriden submodule,
 | 
			
		||||
        along with that get two browser level preferences show_system_objects,
 | 
			
		||||
        and show_node will be registered to used by the submodules.
 | 
			
		||||
        """
 | 
			
		||||
        # Add the node informaton for browser, not in respective node preferences
 | 
			
		||||
        self.browser_preference = Preferences.module('browser')
 | 
			
		||||
        self.pref_show_system_objects = self.browser_preference.preference(
 | 
			
		||||
                'show_system_objects'
 | 
			
		||||
                )
 | 
			
		||||
        self.pref_show_node = self.browser_preference.register(
 | 
			
		||||
                'node', 'show_node_' + self.node_type,
 | 
			
		||||
                self.collection_label, 'node', self.SHOW_ON_BROWSER,
 | 
			
		||||
                category_label=gettext('Nodes')
 | 
			
		||||
                )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,17 +9,15 @@
 | 
			
		|||
"""Defines views for management of server groups"""
 | 
			
		||||
 | 
			
		||||
from abc import ABCMeta, abstractmethod
 | 
			
		||||
import traceback
 | 
			
		||||
import json
 | 
			
		||||
from flask import request, render_template, make_response, jsonify
 | 
			
		||||
from flask.ext.babel import gettext
 | 
			
		||||
from flask.ext.security import current_user, login_required
 | 
			
		||||
from pgadmin import current_blueprint
 | 
			
		||||
from flask.ext.security import current_user
 | 
			
		||||
from pgadmin.utils.ajax import make_json_response, \
 | 
			
		||||
    make_response as ajax_response
 | 
			
		||||
from pgadmin.browser import BrowserPluginModule
 | 
			
		||||
from pgadmin.utils.menu import MenuItem
 | 
			
		||||
from pgadmin.settings.settings_model import db, ServerGroup
 | 
			
		||||
from pgadmin.model import db, ServerGroup
 | 
			
		||||
from pgadmin.browser.utils import NodeView
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -41,12 +39,30 @@ class ServerGroupModule(BrowserPluginModule):
 | 
			
		|||
 | 
			
		||||
    @property
 | 
			
		||||
    def node_type(self):
 | 
			
		||||
        """
 | 
			
		||||
        node_type
 | 
			
		||||
        Node type for Server Group is server-group. It is defined by NODE_TYPE
 | 
			
		||||
        static attribute of the class.
 | 
			
		||||
        """
 | 
			
		||||
        return self.NODE_TYPE
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def script_load(self):
 | 
			
		||||
        """
 | 
			
		||||
        script_load
 | 
			
		||||
        Load the server-group javascript module on loading of browser module.
 | 
			
		||||
        """
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def register_preferences(self):
 | 
			
		||||
        """
 | 
			
		||||
        register_preferences
 | 
			
		||||
        Overrides the register_preferences PgAdminModule, because - we will not
 | 
			
		||||
        register any preference for server-group (specially the show_node
 | 
			
		||||
        preference.)
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ServerGroupMenuItem(MenuItem):
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -153,7 +169,6 @@ class ServerGroupView(NodeView):
 | 
			
		|||
        sg = ServerGroup.query.filter_by(
 | 
			
		||||
                user_id=current_user.id,
 | 
			
		||||
                id=gid).first()
 | 
			
		||||
        data = {}
 | 
			
		||||
 | 
			
		||||
        if sg is None:
 | 
			
		||||
            return make_json_response(
 | 
			
		||||
| 
						 | 
				
			
			@ -181,7 +196,7 @@ class ServerGroupView(NodeView):
 | 
			
		|||
 | 
			
		||||
                data[u'id'] = sg.id
 | 
			
		||||
                data[u'name'] = sg.name
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
                return jsonify(
 | 
			
		||||
                        node=self.blueprint.generate_browser_node(
 | 
			
		||||
                            "%d" % (sg.id), None,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,25 +8,22 @@
 | 
			
		|||
##########################################################################
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
from abc import ABCMeta, abstractproperty
 | 
			
		||||
from flask import render_template, request, make_response, jsonify, \
 | 
			
		||||
        current_app, url_for
 | 
			
		||||
from flask.ext.security import login_required, current_user
 | 
			
		||||
from pgadmin.settings.settings_model import db, Server, ServerGroup, User
 | 
			
		||||
from flask.ext.security import current_user
 | 
			
		||||
from pgadmin.model import db, Server, ServerGroup, User
 | 
			
		||||
from pgadmin.utils.menu import MenuItem
 | 
			
		||||
from pgadmin.utils.ajax import make_json_response, \
 | 
			
		||||
    make_response as ajax_response, internal_server_error, success_return, \
 | 
			
		||||
    unauthorized, bad_request, precondition_required, forbidden
 | 
			
		||||
from pgadmin.utils.ajax import make_json_response, bad_request, forbidden, \
 | 
			
		||||
    make_response as ajax_response, internal_server_error, unauthorized
 | 
			
		||||
from pgadmin.browser.utils import PGChildNodeView
 | 
			
		||||
import traceback
 | 
			
		||||
from flask.ext.babel import gettext
 | 
			
		||||
import pgadmin.browser.server_groups as sg
 | 
			
		||||
from pgadmin.utils.crypto import encrypt
 | 
			
		||||
from pgadmin.browser import BrowserPluginModule
 | 
			
		||||
from config import PG_DEFAULT_DRIVER
 | 
			
		||||
import six
 | 
			
		||||
from pgadmin.browser.server_groups.servers.types import ServerType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def has_any(data, keys):
 | 
			
		||||
    """
 | 
			
		||||
    Checks any one of the keys present in the data given
 | 
			
		||||
| 
						 | 
				
			
			@ -46,6 +43,7 @@ def has_any(data, keys):
 | 
			
		|||
 | 
			
		||||
class ServerModule(sg.ServerGroupPluginModule):
 | 
			
		||||
    NODE_TYPE = "server"
 | 
			
		||||
    LABEL = gettext("Servers")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def node_type(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -146,6 +144,15 @@ class ServerModule(sg.ServerGroupPluginModule):
 | 
			
		|||
 | 
			
		||||
        super(ServerModule, self).register(app, options, first_registration)
 | 
			
		||||
 | 
			
		||||
    # We do not have any preferences for server node.
 | 
			
		||||
    def register_preferences(self):
 | 
			
		||||
        """
 | 
			
		||||
        register_preferences
 | 
			
		||||
        Override it so that - it does not register the show_node preference for
 | 
			
		||||
        server type.
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
class ServerMenuItem(MenuItem):
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        kwargs.setdefault("type", ServerModule.NODE_TYPE)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,7 +41,8 @@ class DatabaseModule(CollectionNodeModule):
 | 
			
		|||
        """
 | 
			
		||||
        Generate the collection node
 | 
			
		||||
        """
 | 
			
		||||
        yield self.generate_browser_collection_node(sid)
 | 
			
		||||
        if self.show_node:
 | 
			
		||||
            yield self.generate_browser_collection_node(sid)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def script_load(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -634,7 +635,7 @@ class DatabaseView(PGChildNodeView):
 | 
			
		|||
        except Exception as e:
 | 
			
		||||
            current_app.logger.exception(e)
 | 
			
		||||
            return make_json_response(
 | 
			
		||||
                    data="-- modified SQL",
 | 
			
		||||
                    data=_("-- modified SQL"),
 | 
			
		||||
                    status=200
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,8 +10,7 @@ from flask import render_template, request, jsonify, current_app
 | 
			
		|||
from flask.ext.babel import gettext as _
 | 
			
		||||
from pgadmin.utils.ajax import make_json_response, \
 | 
			
		||||
    make_response as ajax_response, precondition_required, \
 | 
			
		||||
    internal_server_error, forbidden, \
 | 
			
		||||
    not_implemented, success_return, gone
 | 
			
		||||
    internal_server_error, forbidden, success_return, gone
 | 
			
		||||
from pgadmin.browser.utils import PGChildNodeView
 | 
			
		||||
from pgadmin.browser.collection import CollectionNodeModule
 | 
			
		||||
import pgadmin.browser.server_groups as sg
 | 
			
		||||
| 
						 | 
				
			
			@ -37,8 +36,9 @@ class RoleModule(CollectionNodeModule):
 | 
			
		|||
        """
 | 
			
		||||
        Generate the collection node
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        yield self.generate_browser_collection_node(sid)
 | 
			
		||||
        if self.show_node:
 | 
			
		||||
            yield self.generate_browser_collection_node(sid)
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def node_inode(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -480,7 +480,8 @@ rolmembership:{
 | 
			
		|||
                if check_permission:
 | 
			
		||||
                    user = self.manager.user_info
 | 
			
		||||
 | 
			
		||||
                    if not user['is_superuser'] and not user['can_create_role']:
 | 
			
		||||
                    if not user['is_superuser'] and \
 | 
			
		||||
                            not user['can_create_role']:
 | 
			
		||||
                        if (action != 'update' or
 | 
			
		||||
                                'rid' in kwargs and kwargs['rid'] != -1 and
 | 
			
		||||
                                user['id'] != kwargs['rid']):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,12 +26,6 @@ class TablespaceModule(CollectionNodeModule):
 | 
			
		|||
    NODE_TYPE = 'tablespace'
 | 
			
		||||
    COLLECTION_LABEL = gettext("Tablespaces")
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        self.min_ver = None
 | 
			
		||||
        self.max_ver = None
 | 
			
		||||
 | 
			
		||||
        super(TablespaceModule, self).__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_nodes(self, gid, sid):
 | 
			
		||||
        """
 | 
			
		||||
        Generate the collection node
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,29 +9,14 @@
 | 
			
		|||
 | 
			
		||||
"""Browser helper utilities"""
 | 
			
		||||
 | 
			
		||||
from abc import ABCMeta, abstractmethod
 | 
			
		||||
from abc import abstractmethod
 | 
			
		||||
import flask
 | 
			
		||||
from flask.views import View, MethodViewType, with_metaclass
 | 
			
		||||
from flask.ext.babel import gettext
 | 
			
		||||
from flask import render_template, current_app
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from config import PG_DEFAULT_DRIVER
 | 
			
		||||
from pgadmin.utils.ajax import make_json_response, precondition_required
 | 
			
		||||
 | 
			
		||||
@six.add_metaclass(ABCMeta)
 | 
			
		||||
class NodeAttr(object):
 | 
			
		||||
    """
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def validate(self, mode, value):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def schema(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PGChildModule(object):
 | 
			
		||||
    """
 | 
			
		||||
| 
						 | 
				
			
			@ -47,10 +32,6 @@ class PGChildModule(object):
 | 
			
		|||
    - Return True when it supports certain version.
 | 
			
		||||
      Uses the psycopg2 server connection manager as input for checking the
 | 
			
		||||
      compatibility of the current module.
 | 
			
		||||
 | 
			
		||||
    * AddAttr(attr)
 | 
			
		||||
    - This adds the attribute supported for specific version only. It takes
 | 
			
		||||
      NodeAttr as input, and update max_ver, min_ver variables for this module.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
| 
						 | 
				
			
			@ -58,9 +39,12 @@ class PGChildModule(object):
 | 
			
		|||
        self.max_ver = 1000000000
 | 
			
		||||
        self.server_type = None
 | 
			
		||||
 | 
			
		||||
        super(PGChildModule, self).__init__(*args, **kwargs)
 | 
			
		||||
        super(PGChildModule, self).__init__()
 | 
			
		||||
 | 
			
		||||
    def BackendSupported(self, manager, **kwargs):
 | 
			
		||||
        if hasattr(self, 'show_node'):
 | 
			
		||||
            if not self.show_node:
 | 
			
		||||
                return False
 | 
			
		||||
        sversion = getattr(manager, 'sversion', None)
 | 
			
		||||
        if (sversion is None or not isinstance(sversion, int)):
 | 
			
		||||
            return False
 | 
			
		||||
| 
						 | 
				
			
			@ -83,6 +67,7 @@ class PGChildModule(object):
 | 
			
		|||
    def get_nodes(self, sid=None, **kwargs):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NodeView(with_metaclass(MethodViewType, View)):
 | 
			
		||||
    """
 | 
			
		||||
    A PostgreSQL Object has so many operaions/functions apart from CRUD
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -109,3 +109,42 @@ class Server(db.Model):
 | 
			
		|||
    comment = db.Column(
 | 
			
		||||
            db.String(1024),
 | 
			
		||||
            nullable=True)
 | 
			
		||||
 | 
			
		||||
class ModulePreference(db.Model):
 | 
			
		||||
    """Define a preferences table for any modules."""
 | 
			
		||||
    __tablename__ = 'module_preference'
 | 
			
		||||
    id = db.Column(db.Integer, primary_key=True)
 | 
			
		||||
    name = db.Column(db.String(256), nullable=False)
 | 
			
		||||
 | 
			
		||||
class PreferenceCategory(db.Model):
 | 
			
		||||
    """Define a preferences category for each modules."""
 | 
			
		||||
    __tablename__ = 'preference_category'
 | 
			
		||||
    id = db.Column(db.Integer, primary_key=True)
 | 
			
		||||
    mid = db.Column(
 | 
			
		||||
        db.Integer,
 | 
			
		||||
        db.ForeignKey('module_preference.id'),
 | 
			
		||||
        nullable=False
 | 
			
		||||
        )
 | 
			
		||||
    name = db.Column(db.String(256), nullable=False)
 | 
			
		||||
 | 
			
		||||
class Preferences(db.Model):
 | 
			
		||||
    """Define a particular preference."""
 | 
			
		||||
    __tablename__ = 'preferences'
 | 
			
		||||
    id = db.Column(db.Integer, primary_key=True)
 | 
			
		||||
    cid = db.Column(
 | 
			
		||||
        db.Integer,
 | 
			
		||||
        db.ForeignKey('preference_category.id'),
 | 
			
		||||
        nullable=False
 | 
			
		||||
        )
 | 
			
		||||
    name = db.Column(db.String(1024), nullable=False)
 | 
			
		||||
 | 
			
		||||
class UserPreference(db.Model):
 | 
			
		||||
    """Define the preference for a particular user."""
 | 
			
		||||
    __tablename__ = 'user_preferences'
 | 
			
		||||
    pid = db.Column(
 | 
			
		||||
        db.Integer, db.ForeignKey('preferences.id'), primary_key=True
 | 
			
		||||
        )
 | 
			
		||||
    uid = db.Column(
 | 
			
		||||
        db.Integer, db.ForeignKey('user.id'), primary_key=True
 | 
			
		||||
        )
 | 
			
		||||
    value = db.Column(db.String(1024), nullable=False)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,137 @@
 | 
			
		|||
##########################################################################
 | 
			
		||||
#
 | 
			
		||||
# pgAdmin 4 - PostgreSQL Tools
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
 | 
			
		||||
#
 | 
			
		||||
# This software is released under the PostgreSQL Licence
 | 
			
		||||
#
 | 
			
		||||
##########################################################################
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Implements the routes for creating Preferences/Options Dialog on the client
 | 
			
		||||
side and for getting/setting preferences.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from pgadmin.utils import PgAdminModule
 | 
			
		||||
from pgadmin.utils.ajax import success_return, \
 | 
			
		||||
    make_response as ajax_response, internal_server_error
 | 
			
		||||
 | 
			
		||||
from flask import render_template, url_for, Response, request
 | 
			
		||||
from flask.ext.security import login_required
 | 
			
		||||
from flask.ext.login import current_user
 | 
			
		||||
from flask.ext.babel import gettext
 | 
			
		||||
 | 
			
		||||
from pgadmin.utils.menu import MenuItem
 | 
			
		||||
from pgadmin.utils.preferences import Preferences
 | 
			
		||||
import simplejson as json
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MODULE_NAME = 'preferences'
 | 
			
		||||
 | 
			
		||||
class PreferencesModule(PgAdminModule):
 | 
			
		||||
    """
 | 
			
		||||
    PreferenceModule represets the preferences of different modules to the
 | 
			
		||||
    user in UI.
 | 
			
		||||
 | 
			
		||||
    And, allows the user to modify (not add/remove) as per their requirement.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def get_own_javascripts(self):
 | 
			
		||||
        return [{
 | 
			
		||||
            'name': 'pgadmin.preferences',
 | 
			
		||||
            'path': url_for('preferences.index') + 'preferences',
 | 
			
		||||
            'when': None
 | 
			
		||||
        }]
 | 
			
		||||
 | 
			
		||||
    def get_own_stylesheets(self):
 | 
			
		||||
        return [url_for('preferences.static', filename='css/preferences.css')]
 | 
			
		||||
 | 
			
		||||
    def get_own_menuitems(self):
 | 
			
		||||
        return {
 | 
			
		||||
            'file_items': [
 | 
			
		||||
                MenuItem(name='mnu_preferences',
 | 
			
		||||
                         priority=999,
 | 
			
		||||
                         module="pgAdmin.Preferences",
 | 
			
		||||
                         callback='show',
 | 
			
		||||
                         icon='fa fa-cog',
 | 
			
		||||
                         label=gettext('Preferences'))
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
blueprint = PreferencesModule(MODULE_NAME, __name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@blueprint.route("/")
 | 
			
		||||
@login_required
 | 
			
		||||
def index():
 | 
			
		||||
    """Render the preferences dialog."""
 | 
			
		||||
    return render_template(
 | 
			
		||||
            MODULE_NAME + "/index.html",
 | 
			
		||||
            username=current_user.email,
 | 
			
		||||
            _=gettext
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@blueprint.route("/preferences.js")
 | 
			
		||||
@login_required
 | 
			
		||||
def script():
 | 
			
		||||
    """render the required javascript"""
 | 
			
		||||
    return Response(response=render_template("preferences/preferences.js", _=gettext),
 | 
			
		||||
                    status=200,
 | 
			
		||||
                    mimetype="application/javascript")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@blueprint.route("/preferences", methods=["GET"])
 | 
			
		||||
@login_required
 | 
			
		||||
def preferences():
 | 
			
		||||
    """Fetch all the preferences of pgAdmin IV."""
 | 
			
		||||
 | 
			
		||||
    # Load Preferences
 | 
			
		||||
    preferences = Preferences.preferences()
 | 
			
		||||
    res = []
 | 
			
		||||
 | 
			
		||||
    for m in preferences:
 | 
			
		||||
        if len(m['categories']):
 | 
			
		||||
            om = {
 | 
			
		||||
                "id": m['id'],
 | 
			
		||||
                "label": m['label'],
 | 
			
		||||
                "inode": True,
 | 
			
		||||
                "open": True,
 | 
			
		||||
                "branch": []
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            for c in m['categories']:
 | 
			
		||||
                oc = {
 | 
			
		||||
                    "id": c['id'],
 | 
			
		||||
                    "mid": m['id'],
 | 
			
		||||
                    "label": c['label'],
 | 
			
		||||
                    "inode": False,
 | 
			
		||||
                    "open": False,
 | 
			
		||||
                    "preferences": c['preferences']
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                (om['branch']).append(oc)
 | 
			
		||||
 | 
			
		||||
            res.append(om)
 | 
			
		||||
 | 
			
		||||
    return ajax_response(
 | 
			
		||||
            response=res,
 | 
			
		||||
            status=200
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@blueprint.route("/preferences/<int:pid>", methods=["PUT"])
 | 
			
		||||
@login_required
 | 
			
		||||
def save(pid):
 | 
			
		||||
    """
 | 
			
		||||
    Save a specific preference.
 | 
			
		||||
    """
 | 
			
		||||
    data = request.form if request.form else json.loads(request.data.decode())
 | 
			
		||||
 | 
			
		||||
    res, msg = Preferences.save(data['mid'], data['cid'], data['id'], data['value'])
 | 
			
		||||
 | 
			
		||||
    if not res:
 | 
			
		||||
        return internal_server_error(errormsg=msg)
 | 
			
		||||
 | 
			
		||||
    return success_return()
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
.preferences_dialog {
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 5px;
 | 
			
		||||
  left: 0px;
 | 
			
		||||
  bottom: 0px;
 | 
			
		||||
  right: 0px;
 | 
			
		||||
  padding-bottom: 30px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.preferences_tree{
 | 
			
		||||
  padding: 0px;
 | 
			
		||||
  padding-top: 2px;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
  border-right: 2px solid #999999;
 | 
			
		||||
  background-image: #FAFAFA;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.preferences_content {
 | 
			
		||||
  padding-top: 10px;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.preferences_content .control-label, .preferences_content .pgadmin-controls {
 | 
			
		||||
  min-width: 100px !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.pgadmin-preference-body {
 | 
			
		||||
    min-width: 300px !important;
 | 
			
		||||
    min-height: 400px !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (min-width: 768px) {
 | 
			
		||||
  .pgadmin-preference-body {
 | 
			
		||||
    min-width: 600px !important;
 | 
			
		||||
    min-height: 480px !important;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
<div id="options_dialog">
 | 
			
		||||
  <div id="options_tree" class="">
 | 
			
		||||
          ACI TREE
 | 
			
		||||
  </div>
 | 
			
		||||
  <div id='options_content'>
 | 
			
		||||
      Right hand side content
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,374 @@
 | 
			
		|||
define(
 | 
			
		||||
  ['jquery', 'alertify', 'pgadmin', 'underscore', 'backform', 'pgadmin.backform'],
 | 
			
		||||
 | 
			
		||||
  // This defines the Preference/Options Dialog for pgAdmin IV.
 | 
			
		||||
  function($, alertify, pgAdmin, _, Backform) {
 | 
			
		||||
    pgAdmin = pgAdmin || window.pgAdmin || {};
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Hmm... this module is already been initialized, we can refer to the old
 | 
			
		||||
     * object from here.
 | 
			
		||||
     */
 | 
			
		||||
    if (pgAdmin.Preferences)
 | 
			
		||||
        return pgAdmin.Preferences;
 | 
			
		||||
 | 
			
		||||
    pgAdmin.Preferences = {
 | 
			
		||||
      init: function() {
 | 
			
		||||
        if (this.initialized)
 | 
			
		||||
          return;
 | 
			
		||||
 | 
			
		||||
        this.initialized = true;
 | 
			
		||||
 | 
			
		||||
        // Declare the Preferences dialog
 | 
			
		||||
        alertify.dialog('preferencesDlg', function() {
 | 
			
		||||
 | 
			
		||||
          var jTree,         // Variable to create the aci-tree
 | 
			
		||||
              controls = [], // Keep tracking of all the backform controls
 | 
			
		||||
                             // created by the dialog.
 | 
			
		||||
              // Dialog containter
 | 
			
		||||
              $container = $("<div class='preferences_dialog'></div>");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
          /*
 | 
			
		||||
           * Preference Model
 | 
			
		||||
           *
 | 
			
		||||
           * This model will be used to keep tracking of the changes done for
 | 
			
		||||
           * an individual option.
 | 
			
		||||
           */
 | 
			
		||||
          var PreferenceModel = Backbone.Model.extend({
 | 
			
		||||
            idAttribute: 'id',
 | 
			
		||||
            defaults: {
 | 
			
		||||
              id: undefined,
 | 
			
		||||
              value: undefined
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          /*
 | 
			
		||||
           * Preferences Collection object.
 | 
			
		||||
           *
 | 
			
		||||
           * We will use only one collection object to keep track of all the
 | 
			
		||||
           * preferences.
 | 
			
		||||
           */
 | 
			
		||||
          var preferences = this.preferences = new (Backbone.Collection.extend({
 | 
			
		||||
            model: PreferenceModel,
 | 
			
		||||
            url: "{{ url_for('preferences.preferences') }}",
 | 
			
		||||
            updateAll: function() {
 | 
			
		||||
              // We will send only the modified data to the server.
 | 
			
		||||
              this.each(function(m) {
 | 
			
		||||
                if (m.hasChanged()) {
 | 
			
		||||
                  m.save({
 | 
			
		||||
                    fail: function() {
 | 
			
		||||
                    }
 | 
			
		||||
                  });
 | 
			
		||||
                }
 | 
			
		||||
              });
 | 
			
		||||
              return true;
 | 
			
		||||
            }
 | 
			
		||||
          }))(null);
 | 
			
		||||
 | 
			
		||||
          /*
 | 
			
		||||
           * Function: renderPreferencePanel
 | 
			
		||||
           *
 | 
			
		||||
           * Renders the preference panel in the content div based on the given
 | 
			
		||||
           * preferences.
 | 
			
		||||
           */
 | 
			
		||||
          var renderPreferencePanel = function(prefs) {
 | 
			
		||||
            /*
 | 
			
		||||
             * Clear the existing html in the preferences content
 | 
			
		||||
             */
 | 
			
		||||
            var content = $container.find('.preferences_content');
 | 
			
		||||
            content.empty();
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * We should clean up the existing controls.
 | 
			
		||||
             */
 | 
			
		||||
            if (controls) {
 | 
			
		||||
              _.each(controls, function(c) {
 | 
			
		||||
                c.remove();
 | 
			
		||||
              });
 | 
			
		||||
            }
 | 
			
		||||
            controls = [];
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * We will create new set of controls and render it based on the
 | 
			
		||||
             * list of preferences using the Backform Field, Control.
 | 
			
		||||
             */
 | 
			
		||||
            _.each(prefs, function(p) {
 | 
			
		||||
 | 
			
		||||
              var m = preferences.get(p.id),
 | 
			
		||||
                  f = new Backform.Field(_.extend({}, p, {id: 'value', name: 'value'})),
 | 
			
		||||
                  cntr = new (f.get("control")) ({
 | 
			
		||||
                    field: f,
 | 
			
		||||
                    model: m
 | 
			
		||||
                  });
 | 
			
		||||
                  content.append(cntr.render().$el);
 | 
			
		||||
 | 
			
		||||
                  // We will keep track of all the controls rendered at the
 | 
			
		||||
                  // moment.
 | 
			
		||||
                  controls.push(cntr);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          /*
 | 
			
		||||
           * Function: dialogContentCleanup
 | 
			
		||||
           *
 | 
			
		||||
           * Do the dialog container cleanup on openning.
 | 
			
		||||
           */
 | 
			
		||||
 | 
			
		||||
          var dialogContentCleanup = function() {
 | 
			
		||||
              // Remove the existing preferences
 | 
			
		||||
              if (!jTree)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
              /*
 | 
			
		||||
               * Remove the aci-tree (mainly to remove the jquery object of
 | 
			
		||||
               * aciTree from the system for this container).
 | 
			
		||||
               */
 | 
			
		||||
              try {
 | 
			
		||||
                jTreeApi = jTree.aciTree('destroy');
 | 
			
		||||
              } catch(ex) {
 | 
			
		||||
                // Sometimes - it fails to destroy the tree properly and throws
 | 
			
		||||
                // exception.
 | 
			
		||||
              }
 | 
			
		||||
              jTree.off('acitree', treeEventHandler);
 | 
			
		||||
 | 
			
		||||
              // We need to reset the data from the preferences too
 | 
			
		||||
              preferences.reset();
 | 
			
		||||
 | 
			
		||||
              /*
 | 
			
		||||
               * Clean up the existing controls.
 | 
			
		||||
               */
 | 
			
		||||
              if (controls) {
 | 
			
		||||
                _.each(controls, function(c) {
 | 
			
		||||
                  c.remove();
 | 
			
		||||
                });
 | 
			
		||||
              }
 | 
			
		||||
              controls = [];
 | 
			
		||||
 | 
			
		||||
              // Remove all the objects now.
 | 
			
		||||
              $container.empty();
 | 
			
		||||
            },
 | 
			
		||||
            /*
 | 
			
		||||
             * Function: selectFirstCategory
 | 
			
		||||
             *
 | 
			
		||||
             * Whenever a user select a module instead of a category, we should
 | 
			
		||||
             * select the first categroy of it.
 | 
			
		||||
             */
 | 
			
		||||
            selectFirstCategory = function(api, item) {
 | 
			
		||||
              var  data = item ? api.itemData(item) : null;
 | 
			
		||||
 | 
			
		||||
              if (data && data.preferences) {
 | 
			
		||||
                  api.select(item);
 | 
			
		||||
                  return;
 | 
			
		||||
              }
 | 
			
		||||
              item = api.first(item);
 | 
			
		||||
              selectFirstCategory(api, item);
 | 
			
		||||
            },
 | 
			
		||||
            /*
 | 
			
		||||
             * A map on how to create controls for each datatype in preferences
 | 
			
		||||
             * dialog.
 | 
			
		||||
             */
 | 
			
		||||
            getControlMappedForType = function(p) {
 | 
			
		||||
              switch(p.type) {
 | 
			
		||||
                case 'boolean':
 | 
			
		||||
                  p.options = {
 | 
			
		||||
                      onText: '{{ _('True') }}',
 | 
			
		||||
                      offText: '{{ _('False') }}',
 | 
			
		||||
                      onColor: 'success',
 | 
			
		||||
                      offColor: 'default',
 | 
			
		||||
                      size: 'mini'
 | 
			
		||||
                    };
 | 
			
		||||
                  return 'switch';
 | 
			
		||||
                case 'node':
 | 
			
		||||
                    p.options = {
 | 
			
		||||
                      onText: '{{ _('Show') }}',
 | 
			
		||||
                      offText: '{{ _('Hide') }}',
 | 
			
		||||
                      onColor: 'success',
 | 
			
		||||
                      offColor: 'default',
 | 
			
		||||
                      size: 'mini'
 | 
			
		||||
                    };
 | 
			
		||||
                    return 'switch';
 | 
			
		||||
                case 'integer':
 | 
			
		||||
                  return 'integer';
 | 
			
		||||
                case 'numeric':
 | 
			
		||||
                  return 'numeric';
 | 
			
		||||
                case 'date':
 | 
			
		||||
                  // TODO::
 | 
			
		||||
                  // Datetime picker Control is missing at the moment, replace
 | 
			
		||||
                  // once it has been implemented.
 | 
			
		||||
                  return 'datepicker';
 | 
			
		||||
                case 'datetime':
 | 
			
		||||
                  return 'datepicker';
 | 
			
		||||
                case 'options':
 | 
			
		||||
                  var opts = [];
 | 
			
		||||
                  // Convert the array to SelectControl understandable options.
 | 
			
		||||
                  _.each(p.options, function(o) {
 | 
			
		||||
                    opts.push({'label': o, 'value': o});
 | 
			
		||||
                  });
 | 
			
		||||
                  p.options = opts;
 | 
			
		||||
                  return 'select2';
 | 
			
		||||
                case 'multiline':
 | 
			
		||||
                  return 'textarea';
 | 
			
		||||
                case 'switch':
 | 
			
		||||
                  return 'switch';
 | 
			
		||||
                default:
 | 
			
		||||
                    if (console && console.log) {
 | 
			
		||||
                      // Warning for developer only.
 | 
			
		||||
                       console.log(
 | 
			
		||||
                         "Hmm.. We don't know how to render this type - ''" + type + "' of control."
 | 
			
		||||
                       );
 | 
			
		||||
                    }
 | 
			
		||||
                  return 'input';
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            /*
 | 
			
		||||
             * function: treeEventHandler
 | 
			
		||||
             *
 | 
			
		||||
             * It is basically a callback, which listens to aci-tree events,
 | 
			
		||||
             * and act accordingly.
 | 
			
		||||
             *
 | 
			
		||||
             * + Selection of the node will existance of the preferences for
 | 
			
		||||
             *   the selected tree-node, if not pass on to select the first
 | 
			
		||||
             *   category under a module, else pass on to the render function.
 | 
			
		||||
             *
 | 
			
		||||
             * + When a new node is added in the tree, it will add the relavent
 | 
			
		||||
             *   preferences in the preferences model collection, which will be
 | 
			
		||||
             *   called during initialization itself.
 | 
			
		||||
             *
 | 
			
		||||
             *
 | 
			
		||||
             */
 | 
			
		||||
            treeEventHandler = function(event, api, item, eventName) {
 | 
			
		||||
              // Look for selected item (if none supplied)!
 | 
			
		||||
              item = item || api.selected();
 | 
			
		||||
 | 
			
		||||
              // Event tree item has itemData
 | 
			
		||||
              var d = item ? api.itemData(item) : null;
 | 
			
		||||
 | 
			
		||||
              /*
 | 
			
		||||
               * boolean (switch/checkbox), string, enum (combobox - enumvals),
 | 
			
		||||
               * integer (min-max), font, color
 | 
			
		||||
               */
 | 
			
		||||
              switch (eventName) {
 | 
			
		||||
                case "selected":
 | 
			
		||||
                  if (!d)
 | 
			
		||||
                    return true;
 | 
			
		||||
 | 
			
		||||
                  if (d.preferences) {
 | 
			
		||||
                    /*
 | 
			
		||||
                     * Clear the existing html in the preferences content
 | 
			
		||||
                     */
 | 
			
		||||
                    renderPreferencePanel(d.preferences);
 | 
			
		||||
 | 
			
		||||
                    return true;
 | 
			
		||||
                  } else{
 | 
			
		||||
                    selectFirstCategory(api, item);
 | 
			
		||||
                  }
 | 
			
		||||
                  break;
 | 
			
		||||
                case 'added':
 | 
			
		||||
                  if (!d)
 | 
			
		||||
                    return true;
 | 
			
		||||
 | 
			
		||||
                  // We will add the preferences in to the preferences data
 | 
			
		||||
                  // collection.
 | 
			
		||||
                  if (d.preferences && _.isArray(d.preferences)) {
 | 
			
		||||
                    _.each(d.preferences, function(p) {
 | 
			
		||||
                      preferences.add({
 | 
			
		||||
                        'id': p.id, 'value': p.value, 'cid': d.id, 'mid': d.mid
 | 
			
		||||
                      });
 | 
			
		||||
                      /*
 | 
			
		||||
                       * We don't know until now, how to render the control for
 | 
			
		||||
                       * this preference.
 | 
			
		||||
                       */
 | 
			
		||||
                      if (!p.control) {
 | 
			
		||||
                        p.control = getControlMappedForType(p);
 | 
			
		||||
                      }
 | 
			
		||||
                    });
 | 
			
		||||
                  }
 | 
			
		||||
                  d.sortable = false;
 | 
			
		||||
                  break;
 | 
			
		||||
                case 'loaded':
 | 
			
		||||
                  // Let's select the first category from the prefrences.
 | 
			
		||||
                  // We need to wait for sometime before all item gets loaded
 | 
			
		||||
                  // properly.
 | 
			
		||||
                  setTimeout(
 | 
			
		||||
                    function() {
 | 
			
		||||
                      selectFirstCategory(api, null);
 | 
			
		||||
                    }, 300);
 | 
			
		||||
                  break;
 | 
			
		||||
              }
 | 
			
		||||
              return true;
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          // Dialog property
 | 
			
		||||
          return {
 | 
			
		||||
            main: function() {
 | 
			
		||||
 | 
			
		||||
              // Remove the existing content first.
 | 
			
		||||
              dialogContentCleanup();
 | 
			
		||||
 | 
			
		||||
              $container.append(
 | 
			
		||||
                "<div class='col-xs-3 preferences_tree aciTree'></div>"
 | 
			
		||||
              ).append(
 | 
			
		||||
                "<div class='col-xs-9 preferences_content'>" +
 | 
			
		||||
                " {{ _('Category is not selected.')|safe }}" +
 | 
			
		||||
                "</div>"
 | 
			
		||||
              );
 | 
			
		||||
 | 
			
		||||
              // Create the aci-tree for listing the modules and categories of
 | 
			
		||||
              // it.
 | 
			
		||||
              jTree = $container.find('.preferences_tree');
 | 
			
		||||
              jTree.on('acitree', treeEventHandler);
 | 
			
		||||
 | 
			
		||||
              jTree.aciTree({
 | 
			
		||||
                selectable: true,
 | 
			
		||||
                expand: true,
 | 
			
		||||
                ajax: {
 | 
			
		||||
                  url: "{{ url_for('preferences.preferences') }}"
 | 
			
		||||
                }
 | 
			
		||||
              });
 | 
			
		||||
 | 
			
		||||
              this.show();
 | 
			
		||||
            },
 | 
			
		||||
            setup:function(){
 | 
			
		||||
              return {
 | 
			
		||||
                buttons:[
 | 
			
		||||
                  {
 | 
			
		||||
                    text: "{{ _('OK') }}", key: 13, className: "btn btn-primary"
 | 
			
		||||
                  },
 | 
			
		||||
                  {
 | 
			
		||||
                    text: "{{ _('Cancel') }}", className: "btn btn-danger"
 | 
			
		||||
                  }
 | 
			
		||||
                ],
 | 
			
		||||
                focus: { element: 0 },
 | 
			
		||||
                options: {
 | 
			
		||||
                  padding: !1,
 | 
			
		||||
                  overflow: !1,
 | 
			
		||||
                  title: '{{ _('Preferences')|safe }}'
 | 
			
		||||
                }
 | 
			
		||||
              };
 | 
			
		||||
            },
 | 
			
		||||
            callback: function(closeEvent){
 | 
			
		||||
              if (closeEvent.button.text == "{{ _('OK') }}"){
 | 
			
		||||
                preferences.updateAll();
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            build: function() {
 | 
			
		||||
              this.elements.content.appendChild($container.get(0));
 | 
			
		||||
            },
 | 
			
		||||
            hooks: {
 | 
			
		||||
              onshow: function() {
 | 
			
		||||
                $(this.elements.body).addClass('pgadmin-preference-body');
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
      },
 | 
			
		||||
      show: function() {
 | 
			
		||||
        alertify.preferencesDlg(true).resizeTo('60%', '60%');
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return pgAdmin.Preferences;
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			@ -9,16 +9,13 @@
 | 
			
		|||
 | 
			
		||||
"""Utility functions for storing and retrieving user configuration settings."""
 | 
			
		||||
 | 
			
		||||
from flask import current_app
 | 
			
		||||
from flask.ext.login import current_user
 | 
			
		||||
from flask.ext.sqlalchemy import SQLAlchemy
 | 
			
		||||
 | 
			
		||||
from .settings_model import db, Setting
 | 
			
		||||
from pgadmin.model import db, Setting
 | 
			
		||||
import traceback
 | 
			
		||||
from flask import Blueprint, Response, abort, request, render_template
 | 
			
		||||
from flask import Response, request, render_template
 | 
			
		||||
from flask.ext.security import login_required
 | 
			
		||||
 | 
			
		||||
import config
 | 
			
		||||
from pgadmin.utils.ajax import make_json_response
 | 
			
		||||
from pgadmin.utils import PgAdminModule
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +56,6 @@ def store(setting=None, value=None):
 | 
			
		|||
    """Store a configuration setting, or if this is a POST request and a
 | 
			
		||||
    count value is present, store multiple settings at once."""
 | 
			
		||||
    success = 1
 | 
			
		||||
    errorcode = 0
 | 
			
		||||
    errormsg = ''
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +93,6 @@ def get(setting=None, default=None):
 | 
			
		|||
        default = request.form['default']
 | 
			
		||||
 | 
			
		||||
    success = 1
 | 
			
		||||
    errorcode = 0
 | 
			
		||||
    errormsg = ''
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -425,6 +425,7 @@
 | 
			
		|||
    /* Array of objects having attributes [label, fields] */
 | 
			
		||||
    schema: undefined,
 | 
			
		||||
    tagName: "form",
 | 
			
		||||
    legend: true,
 | 
			
		||||
    className: function() {
 | 
			
		||||
      return 'col-sm-12 col-md-12 col-lg-12 col-xs-12';
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			@ -441,6 +442,7 @@
 | 
			
		|||
          o.cId = o.cId || _.uniqueId('pgC_');
 | 
			
		||||
          o.hId = o.hId || _.uniqueId('pgH_');
 | 
			
		||||
          o.disabled = o.disabled || false;
 | 
			
		||||
          o.legend = opts.legend;
 | 
			
		||||
        });
 | 
			
		||||
        if (opts.tabPanelClassName && _.isFunction(opts.tabPanelClassName)) {
 | 
			
		||||
          this.tabPanelClassName = opts.tabPanelClassName;
 | 
			
		||||
| 
						 | 
				
			
			@ -547,8 +549,9 @@
 | 
			
		|||
    template: {
 | 
			
		||||
      'header': _.template([
 | 
			
		||||
        '<fieldset class="<%=fieldsetClass%>" <%=disabled ? "disabled" : ""%>>',
 | 
			
		||||
        ' <% if (legend != false) { %>',
 | 
			
		||||
        '  <legend class="<%=legendClass%>" <%=collapse ? "data-toggle=\'collapse\'" : ""%> data-target="#<%=cId%>"><%=collapse ? "<span class=\'caret\'></span>" : "" %><%=label%></legend>',
 | 
			
		||||
        '  ',
 | 
			
		||||
        ' <% } %>',
 | 
			
		||||
        '</fieldset>'
 | 
			
		||||
      ].join("\n")),
 | 
			
		||||
      'content': _.template(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@
 | 
			
		|||
from flask import Blueprint
 | 
			
		||||
from collections import defaultdict
 | 
			
		||||
from operator import attrgetter
 | 
			
		||||
import sys
 | 
			
		||||
from .preferences import Preferences
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PgAdminModule(Blueprint):
 | 
			
		||||
| 
						 | 
				
			
			@ -26,8 +26,25 @@ class PgAdminModule(Blueprint):
 | 
			
		|||
        kwargs.setdefault('template_folder', 'templates')
 | 
			
		||||
        kwargs.setdefault('static_folder', 'static')
 | 
			
		||||
        self.submodules = []
 | 
			
		||||
 | 
			
		||||
        super(PgAdminModule, self).__init__(name, import_name, **kwargs)
 | 
			
		||||
 | 
			
		||||
        def create_module_preference():
 | 
			
		||||
            # Create preference for each module by default
 | 
			
		||||
            if hasattr(self, 'LABEL'):
 | 
			
		||||
                self.preference = Preferences(self.name, self.LABEL)
 | 
			
		||||
            else:
 | 
			
		||||
                self.preference = Preferences(self.name, None)
 | 
			
		||||
 | 
			
		||||
            self.register_preferences()
 | 
			
		||||
 | 
			
		||||
        # Create and register the module preference object and preferences for
 | 
			
		||||
        # it just before the first request
 | 
			
		||||
        self.before_app_first_request(create_module_preference)
 | 
			
		||||
 | 
			
		||||
    def register_preferences(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def register(self, app, options, first_registration=False):
 | 
			
		||||
        """
 | 
			
		||||
        Override the default register function to automagically register
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +52,9 @@ class PgAdminModule(Blueprint):
 | 
			
		|||
        """
 | 
			
		||||
        if first_registration:
 | 
			
		||||
            self.submodules = list(app.find_submodules(self.import_name))
 | 
			
		||||
 | 
			
		||||
        super(PgAdminModule, self).register(app, options, first_registration)
 | 
			
		||||
 | 
			
		||||
        for module in self.submodules:
 | 
			
		||||
            app.register_blueprint(module)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ from flask.ext.babel import gettext
 | 
			
		|||
from flask.ext.security import current_user
 | 
			
		||||
 | 
			
		||||
from ..abstract import BaseDriver, BaseConnection
 | 
			
		||||
from pgadmin.settings.settings_model import Server, User
 | 
			
		||||
from pgadmin.model import Server, User
 | 
			
		||||
from pgadmin.utils.crypto import decrypt
 | 
			
		||||
import random
 | 
			
		||||
import select
 | 
			
		||||
| 
						 | 
				
			
			@ -1003,7 +1003,7 @@ class Driver(BaseDriver):
 | 
			
		|||
 | 
			
		||||
        managers['pinged'] = datetime.datetime.now()
 | 
			
		||||
        if str(sid) not in managers:
 | 
			
		||||
            from pgadmin.settings.settings_model import Server
 | 
			
		||||
            from pgadmin.model import Server
 | 
			
		||||
            s = Server.query.filter_by(id=sid).first()
 | 
			
		||||
 | 
			
		||||
            managers[str(sid)] = ServerManager(s)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,528 @@
 | 
			
		|||
##########################################################################
 | 
			
		||||
#
 | 
			
		||||
# pgAdmin 4 - PostgreSQL Tools
 | 
			
		||||
#
 | 
			
		||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
 | 
			
		||||
# This software is released under the PostgreSQL Licence
 | 
			
		||||
#
 | 
			
		||||
##########################################################################
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Utility classes to register, getter, setter functions for the preferences of a
 | 
			
		||||
module within the system.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from flask import current_app
 | 
			
		||||
from flask.ext.security import current_user
 | 
			
		||||
from pgadmin.model import db, Preferences as PrefTable, \
 | 
			
		||||
    ModulePreference as ModulePrefTable, UserPreference as UserPrefTable, \
 | 
			
		||||
    PreferenceCategory as PrefCategoryTbl
 | 
			
		||||
from flask.ext.babel import gettext
 | 
			
		||||
import dateutil.parser as dateutil_parser
 | 
			
		||||
import decimal
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class _Preference(object):
 | 
			
		||||
    """
 | 
			
		||||
    Internal class representing module, and categoy bound preference.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
            self, cid, name, label, _type, default, help_str=None, min_val=None,
 | 
			
		||||
            max_val=None, options=None
 | 
			
		||||
            ):
 | 
			
		||||
        """
 | 
			
		||||
        __init__
 | 
			
		||||
        Constructor/Initializer for the internal _Preference object.
 | 
			
		||||
 | 
			
		||||
        It creates a new entry for this preference in configuration table based
 | 
			
		||||
        on the name (if not exists), and keep the id of it for on demand value
 | 
			
		||||
        fetching from the configuration table in later stage. Also, keeps track
 | 
			
		||||
        of type of the preference/option, and other supporting parameters like
 | 
			
		||||
        min, max, options, etc.
 | 
			
		||||
 | 
			
		||||
        :param cid: configuration id
 | 
			
		||||
        :param name: Name of the preference (must be unique for each
 | 
			
		||||
                     configuration)
 | 
			
		||||
        :param label: Display name of the options/preference
 | 
			
		||||
        :param _type: Type for proper validation on value
 | 
			
		||||
        :param default: Default value
 | 
			
		||||
        :param help_str: Help string to be shown in preferences dialog.
 | 
			
		||||
        :param min_val: minimum value
 | 
			
		||||
        :param max_val: maximum value
 | 
			
		||||
        :param options: options (Array of list objects)
 | 
			
		||||
 | 
			
		||||
        :returns: nothing
 | 
			
		||||
        """
 | 
			
		||||
        self.cid = cid
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.default = default
 | 
			
		||||
        self.label = label
 | 
			
		||||
        self._type = _type
 | 
			
		||||
        self.help_str = help_str
 | 
			
		||||
        self.min_val = min_val
 | 
			
		||||
        self.max_val = max_val
 | 
			
		||||
        self.options = options
 | 
			
		||||
 | 
			
		||||
        # Look into the configuration table to find out the id of the specific
 | 
			
		||||
        # preference.
 | 
			
		||||
        res = PrefTable.query.filter_by(
 | 
			
		||||
            name=name
 | 
			
		||||
            ).first()
 | 
			
		||||
 | 
			
		||||
        if res is None:
 | 
			
		||||
            # Couldn't find in the configuration table, we will create new
 | 
			
		||||
            # entry for it.
 | 
			
		||||
            res = PrefTable(name=self.name, cid=cid)
 | 
			
		||||
            db.session.add(res)
 | 
			
		||||
            db.session.commit()
 | 
			
		||||
            res = PrefTable.query.filter_by(
 | 
			
		||||
                name=name
 | 
			
		||||
                ).first()
 | 
			
		||||
 | 
			
		||||
        # Save this id for letter use.
 | 
			
		||||
        self.pid = res.id
 | 
			
		||||
 | 
			
		||||
    def get(self):
 | 
			
		||||
        """
 | 
			
		||||
        get
 | 
			
		||||
        Fetch the value from the server for the current user from the
 | 
			
		||||
        configuration table (if available), otherwise returns the default value
 | 
			
		||||
        for it.
 | 
			
		||||
 | 
			
		||||
        :returns: value for this preference.
 | 
			
		||||
        """
 | 
			
		||||
        res = UserPrefTable.query.filter_by(
 | 
			
		||||
            pid=self.pid
 | 
			
		||||
            ).filter_by(uid=current_user.id).first()
 | 
			
		||||
 | 
			
		||||
        # Couldn't find any preference for this user, return default value.
 | 
			
		||||
        if res is None:
 | 
			
		||||
            return self.default
 | 
			
		||||
 | 
			
		||||
        # The data stored in the configuration will be in string format, we
 | 
			
		||||
        # need to convert them in proper format.
 | 
			
		||||
        if self._type == 'boolean' or self._type == 'switch' or \
 | 
			
		||||
                self._type == 'node':
 | 
			
		||||
            return res.value == 'True'
 | 
			
		||||
        if self._type == 'integer':
 | 
			
		||||
            try:
 | 
			
		||||
                return int(res.value)
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                current_app.logger.exeception(e)
 | 
			
		||||
                return self.default
 | 
			
		||||
        if self._type == 'numeric':
 | 
			
		||||
            try:
 | 
			
		||||
                return decimal.Decimal(res.value)
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                current_app.logger.exeception(e)
 | 
			
		||||
                return self.default
 | 
			
		||||
        if self._type == 'date' or self._type == 'datetime':
 | 
			
		||||
            try:
 | 
			
		||||
                return dateutil_parser.parse(res.value)
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                current_app.logger.exeception(e)
 | 
			
		||||
                return self.default
 | 
			
		||||
        if self._type == 'options':
 | 
			
		||||
            if res.value in self.options:
 | 
			
		||||
                return res.value
 | 
			
		||||
            return self.default
 | 
			
		||||
 | 
			
		||||
        return res.value
 | 
			
		||||
 | 
			
		||||
    def set(self, value):
 | 
			
		||||
        """
 | 
			
		||||
        set
 | 
			
		||||
        Set the value into the configuration table for this current user.
 | 
			
		||||
 | 
			
		||||
        :param value: Value to be set
 | 
			
		||||
 | 
			
		||||
        :returns: nothing.
 | 
			
		||||
        """
 | 
			
		||||
        # We can't store the values in the given format, we need to convert
 | 
			
		||||
        # them in string first. We also need to validate the value type.
 | 
			
		||||
        if self._type == 'boolean' or self._type == 'switch' or \
 | 
			
		||||
                self._type == 'node':
 | 
			
		||||
            if type(value) != bool:
 | 
			
		||||
                return False, gettext("Invalid value for boolean type!")
 | 
			
		||||
        elif self._type == 'integer':
 | 
			
		||||
            if type(value) != int:
 | 
			
		||||
                return False, gettext("Invalid value for integer type!")
 | 
			
		||||
        elif self._type == 'numeric':
 | 
			
		||||
            t = type(value)
 | 
			
		||||
            if t != float and t != int and t != decimal.Decimal:
 | 
			
		||||
                return False, gettext("Invalid value for numeric type!")
 | 
			
		||||
        elif self._type == 'date':
 | 
			
		||||
            try:
 | 
			
		||||
                value = dateutil_parser.parse(value).date()
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                current_app.logger.exeception(e)
 | 
			
		||||
                return False, gettext("Invalid value for date type!")
 | 
			
		||||
        elif self._type == 'datetime':
 | 
			
		||||
            try:
 | 
			
		||||
                value = dateutil_parser.parse(value)
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                current_app.logger.exeception(e)
 | 
			
		||||
                return False, gettext("Invalid value for datetime type!")
 | 
			
		||||
        elif self._type == 'options':
 | 
			
		||||
            if value not in self.options:
 | 
			
		||||
                return False, gettext("Invalid value for options type!")
 | 
			
		||||
 | 
			
		||||
        pref = UserPrefTable.query.filter_by(
 | 
			
		||||
            pid=self.pid
 | 
			
		||||
            ).filter_by(uid=current_user.id).first()
 | 
			
		||||
 | 
			
		||||
        if pref is None:
 | 
			
		||||
            pref = UserPrefTable(
 | 
			
		||||
                uid=current_user.id, pid=self.pid, value=str(value)
 | 
			
		||||
                )
 | 
			
		||||
            db.session.add(pref)
 | 
			
		||||
        else:
 | 
			
		||||
            pref.value = str(value)
 | 
			
		||||
        db.session.commit()
 | 
			
		||||
 | 
			
		||||
        return True, None
 | 
			
		||||
 | 
			
		||||
    def to_json(self):
 | 
			
		||||
        """
 | 
			
		||||
        to_json
 | 
			
		||||
        Returns the JSON object representing this preferences.
 | 
			
		||||
 | 
			
		||||
        :returns: the JSON representation for this preferences
 | 
			
		||||
        """
 | 
			
		||||
        res = {
 | 
			
		||||
            'id': self.pid,
 | 
			
		||||
            'cid': self.cid,
 | 
			
		||||
            'name': self.name,
 | 
			
		||||
            'label': self.label or self.name,
 | 
			
		||||
            'type': self._type,
 | 
			
		||||
            'help_str': self.help_str,
 | 
			
		||||
            'min_val': self.min_val,
 | 
			
		||||
            'max_val': self.max_val,
 | 
			
		||||
            'options': self.options,
 | 
			
		||||
            'value': self.get()
 | 
			
		||||
            }
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Preferences(object):
 | 
			
		||||
    """
 | 
			
		||||
    class Preferences
 | 
			
		||||
 | 
			
		||||
    It helps to manage all the preferences/options related to a specific
 | 
			
		||||
    module.
 | 
			
		||||
 | 
			
		||||
    It keeps track of all the preferences registered with it using this class
 | 
			
		||||
    in the group of categories.
 | 
			
		||||
 | 
			
		||||
    Also, create the required entries for each module, and categories in the
 | 
			
		||||
    preferences tables (if required). If it is already present, it will refer
 | 
			
		||||
    to the existing data from those tables.
 | 
			
		||||
 | 
			
		||||
    class variables:
 | 
			
		||||
    ---------------
 | 
			
		||||
    modules:
 | 
			
		||||
    Dictionary of all the modules, can be refered by its name.
 | 
			
		||||
    Keeps track of all the modules in it, so that - only one object per module
 | 
			
		||||
    gets created. If the same module refered by different object, the
 | 
			
		||||
    categories dictionary within it will be shared between them to keep the
 | 
			
		||||
    consistent data among all the object.
 | 
			
		||||
 | 
			
		||||
    Instance Definitions:
 | 
			
		||||
    -------- -----------
 | 
			
		||||
    """
 | 
			
		||||
    modules = dict()
 | 
			
		||||
 | 
			
		||||
    def __init__(self, name, label=None):
 | 
			
		||||
        """
 | 
			
		||||
        __init__
 | 
			
		||||
        Constructor/Initializer for the Preferences class.
 | 
			
		||||
 | 
			
		||||
        :param name: Name of the module
 | 
			
		||||
        :param label: Display name of the module, it will be displayed in the
 | 
			
		||||
                      preferences dialog.
 | 
			
		||||
 | 
			
		||||
        :returns nothing
 | 
			
		||||
        """
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.label = label
 | 
			
		||||
        self.categories = dict()
 | 
			
		||||
 | 
			
		||||
        # Find the entry for this module in the configuration database.
 | 
			
		||||
        module = ModulePrefTable.query.filter_by(name=name).first()
 | 
			
		||||
 | 
			
		||||
        # Can't find the reference for it in the configuration database,
 | 
			
		||||
        # create on for it.
 | 
			
		||||
        if module is None:
 | 
			
		||||
            module = ModulePrefTable(name=name)
 | 
			
		||||
            db.session.add(module)
 | 
			
		||||
            db.session.commit()
 | 
			
		||||
            module = ModulePrefTable.query.filter_by(name=name).first()
 | 
			
		||||
 | 
			
		||||
        self.mid = module.id
 | 
			
		||||
 | 
			
		||||
        if name in Preferences.modules:
 | 
			
		||||
            m = Preferences.modules
 | 
			
		||||
            self.categories = m.categories
 | 
			
		||||
        else:
 | 
			
		||||
            Preferences.modules[name] = self
 | 
			
		||||
 | 
			
		||||
    def to_json(self):
 | 
			
		||||
        """
 | 
			
		||||
        to_json
 | 
			
		||||
        Converts the preference object to the JSON Format.
 | 
			
		||||
 | 
			
		||||
        :returns: a JSON object contains information.
 | 
			
		||||
        """
 | 
			
		||||
        res = {
 | 
			
		||||
            'id': self.mid,
 | 
			
		||||
            'label': self.label or self.name,
 | 
			
		||||
            'categories': []
 | 
			
		||||
        }
 | 
			
		||||
        for c in self.categories:
 | 
			
		||||
            cat = self.categories[c]
 | 
			
		||||
            interm = {
 | 
			
		||||
                'id': cat['id'],
 | 
			
		||||
                'label': cat['label'] or cat['name'],
 | 
			
		||||
                'preferences': []
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            res['categories'].append(interm)
 | 
			
		||||
 | 
			
		||||
            for p in cat['preferences']:
 | 
			
		||||
                pref = (cat['preferences'][p]).to_json().copy()
 | 
			
		||||
                pref.update({'mid': self.mid, 'cid': cat['id']})
 | 
			
		||||
                interm['preferences'].append(pref)
 | 
			
		||||
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
    def __category(self, name, label):
 | 
			
		||||
        """
 | 
			
		||||
        __category
 | 
			
		||||
 | 
			
		||||
        A private method to create/refer category for/of this module.
 | 
			
		||||
 | 
			
		||||
        :param name: Name of the category
 | 
			
		||||
        :param label: Display name of the category, it will be send to
 | 
			
		||||
                      client/front end to list down in the preferences/options
 | 
			
		||||
                      dialog.
 | 
			
		||||
        :returns: A dictionary object reprenting this category.
 | 
			
		||||
        """
 | 
			
		||||
        if name in self.categories:
 | 
			
		||||
            res = self.categories[name]
 | 
			
		||||
            # Update the category label (if not yet defined)
 | 
			
		||||
            res['label'] = res['label'] or label
 | 
			
		||||
 | 
			
		||||
            return res
 | 
			
		||||
 | 
			
		||||
        cat = PrefCategoryTbl.query.filter_by(
 | 
			
		||||
            mid=self.mid
 | 
			
		||||
            ).filter_by(name=name).first()
 | 
			
		||||
 | 
			
		||||
        if cat is None:
 | 
			
		||||
            cat = PrefCategoryTbl(name=name, mid=self.mid)
 | 
			
		||||
            db.session.add(cat)
 | 
			
		||||
            db.session.commit()
 | 
			
		||||
            cat = PrefCategoryTbl.query.filter_by(
 | 
			
		||||
                mid=self.mid
 | 
			
		||||
                ).filter_by(name=name).first()
 | 
			
		||||
 | 
			
		||||
        self.categories[name] = res = {
 | 
			
		||||
            'id': cat.id,
 | 
			
		||||
            'name': name,
 | 
			
		||||
            'label': label,
 | 
			
		||||
            'preferences': dict()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
    def register(
 | 
			
		||||
        self, category, name, label, _type, default, min_val=None,
 | 
			
		||||
        max_val=None, options=None, help_str=None, category_label=None
 | 
			
		||||
    ):
 | 
			
		||||
        """
 | 
			
		||||
        register
 | 
			
		||||
        Register/Refer the particular preference in this module.
 | 
			
		||||
 | 
			
		||||
        :param category: name of the category, in which this preference/option
 | 
			
		||||
                         will be displayed.
 | 
			
		||||
        :param name:     name of the preference/option
 | 
			
		||||
        :param label:    Display name of the preference
 | 
			
		||||
        :param _type:    [optional] Type of the options.
 | 
			
		||||
                         It is an optional argument, only if this
 | 
			
		||||
                         option/preference is registered earlier.
 | 
			
		||||
        :param default:  [optional] Default value of the options
 | 
			
		||||
                         It is an optional argument, only if this
 | 
			
		||||
                         option/preference is registered earlier.
 | 
			
		||||
        :param min_val:
 | 
			
		||||
        :param max_val:
 | 
			
		||||
        :param options:
 | 
			
		||||
        :param help_str:
 | 
			
		||||
        :param category_label:
 | 
			
		||||
        """
 | 
			
		||||
        cat = self.__category(category, category_label)
 | 
			
		||||
        if name in cat['preferences']:
 | 
			
		||||
            return (cat['preferences'])[name]
 | 
			
		||||
 | 
			
		||||
        assert label is not None, "Label for a preference can not be none!"
 | 
			
		||||
        assert _type is not None, "Type for a preference can not be none!"
 | 
			
		||||
        assert _type in (
 | 
			
		||||
            'boolean', 'integer', 'numeric', 'date', 'datetime',
 | 
			
		||||
            'options', 'multiline', 'switch', 'node'
 | 
			
		||||
            ), "Type can not be found in the defined list!"
 | 
			
		||||
 | 
			
		||||
        (cat['preferences'])[name] = res = _Preference(
 | 
			
		||||
            cat['id'], name, label, _type, default, help_str, min_val,
 | 
			
		||||
            max_val, options
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
    def preference(self, name):
 | 
			
		||||
        """
 | 
			
		||||
        preference
 | 
			
		||||
        Refer the particular preference in this module.
 | 
			
		||||
 | 
			
		||||
        :param name:     name of the preference/option
 | 
			
		||||
        """
 | 
			
		||||
        for key in self.categories:
 | 
			
		||||
            cat = self.categories[key]
 | 
			
		||||
            if name in cat['preferences']:
 | 
			
		||||
                return (cat['preferences'])[name]
 | 
			
		||||
 | 
			
		||||
        assert False, """Couldn't find the preference in this preference!
 | 
			
		||||
Did you forget to register it?"""
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def preferences(cls):
 | 
			
		||||
        """
 | 
			
		||||
        preferences
 | 
			
		||||
        Convert all the module preferences in the JSON format.
 | 
			
		||||
 | 
			
		||||
        :returns: a list of the preferences for each of the modules.
 | 
			
		||||
        """
 | 
			
		||||
        res = []
 | 
			
		||||
 | 
			
		||||
        for m in Preferences.modules:
 | 
			
		||||
            res.append(Preferences.modules[m].to_json())
 | 
			
		||||
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def register_preference(
 | 
			
		||||
            cls, module, category, name, label, _type, default, min_val=None,
 | 
			
		||||
            max_val=None, options=None, help_str=None, module_label=None,
 | 
			
		||||
            category_label=None
 | 
			
		||||
            ):
 | 
			
		||||
        """
 | 
			
		||||
        register
 | 
			
		||||
        Register/Refer a preference in the system for any module.
 | 
			
		||||
 | 
			
		||||
        :param module:   Name of the module
 | 
			
		||||
        :param category: Name of category
 | 
			
		||||
        :param name:     Name of the option
 | 
			
		||||
        :param label:    Label of the option, shown in the preferences dialog.
 | 
			
		||||
        :param _type:    Type of the option.
 | 
			
		||||
                         Allowed type of options are as below:
 | 
			
		||||
                         boolean, integer, numeric, date, datetime,
 | 
			
		||||
                         options, multiline, switch, node
 | 
			
		||||
        :param default:  Default value for the preference/option
 | 
			
		||||
        :param min_val:  Minimum value for integer, and numeric type
 | 
			
		||||
        :param max_val:  Maximum value for integer, and numeric type
 | 
			
		||||
        :param options:  Allowed list of options for 'option' type
 | 
			
		||||
        :param help_str: Help string show for that preference/option.
 | 
			
		||||
        :param module_label: Label for the module
 | 
			
		||||
        :param category_label: Label for the category
 | 
			
		||||
        """
 | 
			
		||||
        m = None
 | 
			
		||||
        if module in Preferences.modules:
 | 
			
		||||
            m = Preferences.modules[module]
 | 
			
		||||
            # Update the label (if not defined yet)
 | 
			
		||||
            m.label = m.label or module_label
 | 
			
		||||
        else:
 | 
			
		||||
            m = Preferences(module, module_label)
 | 
			
		||||
 | 
			
		||||
        return m.register(
 | 
			
		||||
            category, name, label, _type, default, min_val, max_val,
 | 
			
		||||
            options, help_str, category_label
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def module(cls, name):
 | 
			
		||||
        """
 | 
			
		||||
        module (classmethod)
 | 
			
		||||
        Get the module preferences object
 | 
			
		||||
 | 
			
		||||
        :param name: Name of the module
 | 
			
		||||
        :returns: a Preferences object representing for the module.
 | 
			
		||||
        """
 | 
			
		||||
        if name in Preferences.modules:
 | 
			
		||||
            m = Preferences.modules[name]
 | 
			
		||||
            # Update the label (if not defined yet)
 | 
			
		||||
            if m.label is None:
 | 
			
		||||
                m.label = name
 | 
			
		||||
            return m
 | 
			
		||||
        else:
 | 
			
		||||
            m = Preferences(name, None)
 | 
			
		||||
 | 
			
		||||
        return m
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def save(cls, mid, cid, pid, value):
 | 
			
		||||
        """
 | 
			
		||||
        save
 | 
			
		||||
        Update the value for the preference in the configuration database.
 | 
			
		||||
 | 
			
		||||
        :param mid: Module ID
 | 
			
		||||
        :param cid: Category ID
 | 
			
		||||
        :param pid: Preference ID
 | 
			
		||||
        :param value: Value for the options
 | 
			
		||||
        """
 | 
			
		||||
        # Find the entry for this module in the configuration database.
 | 
			
		||||
        module = ModulePrefTable.query.filter_by(id=mid).first()
 | 
			
		||||
 | 
			
		||||
        # Can't find the reference for it in the configuration database,
 | 
			
		||||
        # create on for it.
 | 
			
		||||
        if module is None:
 | 
			
		||||
            return False, gettext("Couldn't find the specified module.")
 | 
			
		||||
 | 
			
		||||
        m = cls.modules[module.name]
 | 
			
		||||
 | 
			
		||||
        if m is None:
 | 
			
		||||
            return False, gettext(
 | 
			
		||||
                "Module '{0}' is no longer in use!"
 | 
			
		||||
            ).format(module.name)
 | 
			
		||||
 | 
			
		||||
        category = None
 | 
			
		||||
 | 
			
		||||
        for c in m.categories:
 | 
			
		||||
            cat = m.categories[c]
 | 
			
		||||
            if cid == cat['id']:
 | 
			
		||||
                category = cat
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
        if category is None:
 | 
			
		||||
            return False, gettext(
 | 
			
		||||
                 "Module '{0}' does not have category with id '{1}'"
 | 
			
		||||
            ).format(module.name, cid)
 | 
			
		||||
 | 
			
		||||
        preference = None
 | 
			
		||||
 | 
			
		||||
        for p in category['preferences']:
 | 
			
		||||
            pref = (category['preferences'])[p]
 | 
			
		||||
 | 
			
		||||
            if pref.pid == pid:
 | 
			
		||||
                preference = pref
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
        if preference is None:
 | 
			
		||||
            return False, gettext(
 | 
			
		||||
                 "Couldn't find the given preference!"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            pref.set(value)
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            return False, str(e)
 | 
			
		||||
 | 
			
		||||
        return True, None
 | 
			
		||||
							
								
								
									
										60
									
								
								web/setup.py
								
								
								
								
							
							
						
						
									
										60
									
								
								web/setup.py
								
								
								
								
							| 
						 | 
				
			
			@ -17,10 +17,9 @@ import random
 | 
			
		|||
import string
 | 
			
		||||
 | 
			
		||||
from flask import Flask
 | 
			
		||||
from flask.ext.sqlalchemy import SQLAlchemy
 | 
			
		||||
from flask.ext.security import Security, SQLAlchemyUserDatastore
 | 
			
		||||
from flask.ext.security.utils import encrypt_password
 | 
			
		||||
from pgadmin.settings.settings_model import db, Role, User, Server, \
 | 
			
		||||
from pgadmin.model import db, Role, User, Server, \
 | 
			
		||||
    ServerGroup, Version
 | 
			
		||||
 | 
			
		||||
# Configuration settings
 | 
			
		||||
| 
						 | 
				
			
			@ -146,7 +145,7 @@ Exiting...""".format(version.value))
 | 
			
		|||
                )
 | 
			
		||||
        if int(version.value) < 5:
 | 
			
		||||
            db.engine.execute('ALTER TABLE server ADD COLUMN role text(64)')
 | 
			
		||||
        if int(version.value) == 6:
 | 
			
		||||
        if int(version.value) < 6:
 | 
			
		||||
            db.engine.execute("ALTER TABLE server RENAME TO server_old")
 | 
			
		||||
            db.engine.execute("""
 | 
			
		||||
CREATE TABLE server (
 | 
			
		||||
| 
						 | 
				
			
			@ -177,9 +176,46 @@ INSERT INTO server (
 | 
			
		|||
FROM server_old""")
 | 
			
		||||
            db.engine.execute("DROP TABLE server_old")
 | 
			
		||||
 | 
			
		||||
        # Finally, update the schema version
 | 
			
		||||
        version.value = config.SETTINGS_SCHEMA_VERSION
 | 
			
		||||
        db.session.merge(version)
 | 
			
		||||
        if int(version.value) < 8:
 | 
			
		||||
            app.logger.info(
 | 
			
		||||
                "Creating the preferences tables..."
 | 
			
		||||
                )
 | 
			
		||||
            db.engine.execute("""
 | 
			
		||||
CREATE TABLE module_preference(
 | 
			
		||||
    id INTEGER PRIMARY KEY,
 | 
			
		||||
    name VARCHAR(256) NOT NULL
 | 
			
		||||
    )""")
 | 
			
		||||
 | 
			
		||||
            db.engine.execute("""
 | 
			
		||||
CREATE TABLE preference_category(
 | 
			
		||||
    id INTEGER PRIMARY KEY,
 | 
			
		||||
    mid INTEGER,
 | 
			
		||||
    name VARCHAR(256) NOT NULL,
 | 
			
		||||
 | 
			
		||||
    FOREIGN KEY(mid) REFERENCES module_preference(id)
 | 
			
		||||
    )""")
 | 
			
		||||
 | 
			
		||||
            db.engine.execute("""
 | 
			
		||||
CREATE TABLE preferences (
 | 
			
		||||
 | 
			
		||||
    id INTEGER PRIMARY KEY,
 | 
			
		||||
    cid INTEGER NOT NULL,
 | 
			
		||||
    name VARCHAR(256) NOT NULL,
 | 
			
		||||
 | 
			
		||||
    FOREIGN KEY(cid) REFERENCES preference_category (id)
 | 
			
		||||
    )""")
 | 
			
		||||
 | 
			
		||||
            db.engine.execute("""
 | 
			
		||||
CREATE TABLE user_preferences (
 | 
			
		||||
 | 
			
		||||
    pid INTEGER,
 | 
			
		||||
    uid INTEGER,
 | 
			
		||||
    value VARCHAR(1024) NOT NULL,
 | 
			
		||||
 | 
			
		||||
    PRIMARY KEY (pid, uid),
 | 
			
		||||
    FOREIGN KEY(pid) REFERENCES preferences (pid),
 | 
			
		||||
    FOREIGN KEY(uid) REFERENCES user (id)
 | 
			
		||||
    )""")
 | 
			
		||||
 | 
			
		||||
    # Finally, update the schema version
 | 
			
		||||
    version.value = config.SETTINGS_SCHEMA_VERSION
 | 
			
		||||
| 
						 | 
				
			
			@ -190,7 +226,7 @@ FROM server_old""")
 | 
			
		|||
    # Done!
 | 
			
		||||
    app.logger.info(
 | 
			
		||||
        "The configuration database %s has been upgraded to version %d" %
 | 
			
		||||
        (config.SQLITE_PATH, config.SETTINGS_SCHEMA_VERSION)
 | 
			
		||||
            (config.SQLITE_PATH, config.SETTINGS_SCHEMA_VERSION)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
| 
						 | 
				
			
			@ -222,8 +258,8 @@ if __name__ == '__main__':
 | 
			
		|||
    # Check if the database exists. If it does, tell the user and exit.
 | 
			
		||||
    if os.path.isfile(config.SQLITE_PATH):
 | 
			
		||||
        print("""
 | 
			
		||||
The configuration database %s already exists.
 | 
			
		||||
Entering upgrade mode...""".format(config.SQLITE_PATH))
 | 
			
		||||
The configuration database '%s' already exists.
 | 
			
		||||
Entering upgrade mode...""" % config.SQLITE_PATH)
 | 
			
		||||
 | 
			
		||||
        # Setup Flask-Security
 | 
			
		||||
        user_datastore = SQLAlchemyUserDatastore(db, User, Role)
 | 
			
		||||
| 
						 | 
				
			
			@ -238,12 +274,12 @@ Entering upgrade mode...""".format(config.SQLITE_PATH))
 | 
			
		|||
                print("""
 | 
			
		||||
The database schema version is %d, whilst the version required by the \
 | 
			
		||||
software is %d.
 | 
			
		||||
Exiting...""".format(version.value, config.SETTINGS_SCHEMA_VERSION))
 | 
			
		||||
Exiting...""" % (version.value, config.SETTINGS_SCHEMA_VERSION))
 | 
			
		||||
                sys.exit(1)
 | 
			
		||||
            elif int(version.value) == int(config.SETTINGS_SCHEMA_VERSION):
 | 
			
		||||
                print("""
 | 
			
		||||
The database schema version is %d as required.
 | 
			
		||||
Exiting...""".format(version.value))
 | 
			
		||||
Exiting...""" % (version.value))
 | 
			
		||||
                sys.exit(1)
 | 
			
		||||
 | 
			
		||||
            print("NOTE: Upgrading database schema from version %d to %d." % (
 | 
			
		||||
| 
						 | 
				
			
			@ -252,6 +288,6 @@ Exiting...""".format(version.value))
 | 
			
		|||
            do_upgrade(app, user_datastore, security, version)
 | 
			
		||||
    else:
 | 
			
		||||
        print("""
 | 
			
		||||
The configuration database - {0} does not exist.
 | 
			
		||||
The configuration database - '{0}' does not exist.
 | 
			
		||||
Entering initial setup mode...""".format(config.SQLITE_PATH))
 | 
			
		||||
        do_setup(app)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue