Hi Dave/Ronan,

On Mon, Apr 20, 2015 at 4:10 PM, Dave Page <dp...@pgadmin.org> wrote:

> On Mon, Apr 20, 2015 at 10:52 AM, Ronan Dunklau
> <ronan.dunk...@dalibo.com> wrote:
> >> Ronan; can you update the test, help and about modules as well please?
> >
> > Done, please find attached a new patch for that. Ashesh, once you're
> done with
> > what you are doing now, feel free to ask me for any help needed to
> integrate
> > this after the fact.
>
> Thanks.
>
Thanks - It looks good to me.
(And, attached patch is based on that.)

>
> >> > snippets of javsacript generated by the server. I feel like this
> apporach
> >> > is extremely fragile. Even before the patch, most of the tree actions
> >> > were not working properly.
> >>
> >> That's odd - they all worked for me
> >
> > Adding a server is not functional, as well as adding a server group.
>
> Server groups should be fully functional. Adding a server isn't -
> that's what Ashesh is investigating backbone.js for.
>
It is now fully functional as it was earlier with the attached patch.
I am still working on the backbone.js.

I am trying to make it general, so that - we can fit it in both properties
pane, and dialog both together.

In order to achieve, I have made a class NodeView (inherited from flask's
view), just like the MethodView.

Flask's MethodView is good to achieve CRUD functionality.
Browser Tree Node (PostgreSQL object) requires more than just CRUD.
i.e.
   - CRUD (Create, Read, Update & Delete)
    - Reversed Engineered SQL for the object
    - Modified Query in edit mode
      i.e. ALTER TABLE ...
    - Statistics
    - List of dependents
    - List of dependencies
    - Children node list

This class can be inherited to achieve the different routes for each of the
object types/collections.

    OPERATION      |              URL       | Method
    ---------------+------------------------+--------
    List           | /obj/[Parent URL]/     | GET
    Properties     | /obj/[Parent URL]/id   | GET
    Create         | /obj/[Parent URL]/     | POST
    Delete         | /obj/[Parent URL]/id   | DELETE
    Update         | /obj/[Parent URL]/id   | PUT

    SQL (Reversed  | /sql/[Parent URL]/id   | GET
    Engineering)   |
    SQL (Modified  | /sql/[Parent URL]/id   | POST
    Properties)    |

    Statistics     | /stats/[Parent URL]/id | GET
    Dependencies   | /deps/[Parent URL]/id  | GET
    Dependents     | /deps/[Parent URL]/id  | POST

    Children Nodes | /nodes/[Parent URL]/id | GET

    NOTE:
    Parent URL can be seen as the path to identify the particular node.

    i.e.
     In order to identify the TABLE object, we requires information about
the server -> database -> schema objects.
     Hence, the Parent URL for the TABLE object will be something like this
as below:
      <int:sid>/<str:database>/<str:schema>


I already did that for ServerGroup, and Server nodes.

Attached patch is still in work in progress, and based on Ranon's latest
patch.
(So - in order to apply my patch, you should apply Ronan's latest patch
first, and then mine.)


--

Thanks & Regards,

Ashesh Vashi
EnterpriseDB INDIA: Enterprise PostgreSQL Company
<http://www.enterprisedb.com>


*http://www.linkedin.com/in/asheshvashi*
<http://www.linkedin.com/in/asheshvashi>


> >> > noticed) feel once again not really robust. All this widget assembly
> is
> >> > done "ad-hoc".
> >>
> >> How would you expect it to be done?
> >
> > In my opinion, it should either be done by using a set of widgets already
> > designed to work together (think of wxwidgets, but for the web), using
> one of
> > the frameworks I mentioned before. Or alternatively, by developing the
> glue
> > between those widgets ourselves, using something like Backbone to wrap
> those
> > libraries into nice views able to play together.
>
> The problem is that I haven't found any OSS frameworks that provide
> anything like the capabilities we have by using ad-hoc components.
> There is nothing at all that comes close to the functionality (and
> look/feel) of wcDocker, CodeMirror, AlertifyJS and aciTree that I've
> found, let alone in a single framework. I've spent a *lot* of time
> researching that, and trying to find components that give us what
> we're going to need.
>
> > +1 for that. As for the extensibility, how is it expected for someone to
> > provide a plugin ? Should he write a module that is supposed to be
> installed
> > in the pgadmin directory directly ? Using the modular approach proposed
> by
> > distutils/setuptools ? Plugins seems to be installed into the pgadmin
> package
> > directly, which is maybe not the most convenient way to manage
> third-party
> > modules.
>
> The current plugin mechanism is documented, but the basic premise is
> that you can add a new module or node by dropping a Python package in
> the right directory, without having to edit any configuration or code.
> That is particularly important for tree nodes where the directory
> structure is used to define the tree structure, to avoid having parent
> nodes needing to have any pre-knowledge of what their children will
> be.
>
> If you have better ideas of how to do that, I'm happy to hear them. I
> do need to avoid long, drawn out discussions and rewrites though; I
> can only justify putting significant EDB resources into the project if
> we continue to move forwards at a reasonable speed.
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
diff --git a/web/pgAdmin4.py b/web/pgAdmin4.py
index 9d2cff2..237cb32 100644
--- a/web/pgAdmin4.py
+++ b/web/pgAdmin4.py
@@ -50,8 +50,8 @@ if not os.path.isfile(config.SQLITE_PATH):
 # Create the app!
 app = create_app()
 
-#if config.DEBUG:
-#    app.debug = True
+if config.DEBUG:
+    app.debug = True
 
 # Start the web server. The port number should have already been set by the
 # runtime if we're running in desktop mode, otherwise we'll just use the
diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py
index e340c83..ae42104 100644
--- a/web/pgadmin/browser/__init__.py
+++ b/web/pgadmin/browser/__init__.py
@@ -21,25 +21,23 @@ MODULE_NAME = 'browser'
 class BrowserModule(PgAdminModule):
 
 
-
     def get_own_stylesheets(self):
         stylesheets = []
         # Add browser stylesheets
         for (endpoint, filename) in [
             ('static', 'css/codemirror/codemirror.css'),
-            ('static', 'css/wcDocker/theme.css'),
             ('static', 'css/jQuery-contextMenu/jquery.contextMenu.css'),
-            ('browser.static', 'css/browser.css'),
-            ('browser.static', 'css/aciTree/css/aciTree.css')
+            ('static', 'css/wcDocker/wcDockerSkeleton.css' if \
+                    current_app.debug else \
+                       'css/wcDocker/wcDockerSkeleton.min.css'),
+            ('static', 'css/wcDocker/theme.css'),
+            ('browser.static', 'css/aciTree/css/aciTree.css'),
             ]:
             stylesheets.append(url_for(endpoint, filename=filename))
         stylesheets.append(url_for('browser.browser_css'))
-        if current_app.debug:
-            stylesheets.append(url_for('static', filename='css/wcDocker/wcDockerSkeleton.css'))
-        else:
-            stylesheets.append(url_for('static', filename='css/wcDocker/wcDockerSkeleton.min.css'))
         return stylesheets
 
+
     def get_own_javascripts(self):
         scripts = []
         for (endpoint, filename) in [
@@ -74,7 +72,7 @@ class BrowserPluginModule(PgAdminModule):
 
     def __init__(self, import_name, **kwargs):
         kwargs.setdefault("url_prefix", self.node_path)
-        kwargs.setdefault("static_url_path", '')
+        kwargs.setdefault("static_url_path", 'static')
         super(BrowserPluginModule, self).__init__("NODE-%s" % self.node_type,
                                             import_name,
                                             **kwargs)
@@ -95,8 +93,8 @@ class BrowserPluginModule(PgAdminModule):
         Returns a snippet of css to include in the page
         """
         # TODO: move those methods to BrowserModule subclass ?
-        return render_template("browser/css/node.css",
-                               node_type=self.node_type)
+        return [render_template("browser/css/node.css",
+                               node_type=self.node_type)]
 
     @abstractmethod
     def get_nodes(self):
@@ -137,11 +135,12 @@ def browser_js():
     snippets = []
     for submodule in current_blueprint.submodules:
         snippets.extend(submodule.jssnippets)
-    return make_response(render_template(
-        'browser/js/browser.js',
-        layout=layout,
-        jssnippets=snippets),
-        200, {'Content-Type': 'application/x-javascript'})
+    return make_response(
+            render_template(
+                'browser/js/browser.js',
+                layout=layout,
+                jssnippets=snippets),
+            200, {'Content-Type': 'application/x-javascript'})
 
 @blueprint.route("/browser.css")
 @login_required
@@ -150,9 +149,9 @@ def browser_css():
     snippets = []
     for submodule in current_blueprint.submodules:
         snippets.extend(submodule.csssnippets)
-    return make_response(render_template('browser/css/browser.css',
-                           snippets=snippets),
-                         200, {'Content-Type': 'text/css'})
+    return make_response(
+            render_template('browser/css/browser.css', snippets=snippets),
+            200, {'Content-Type': 'text/css'})
 
 
 @blueprint.route("/nodes/")
diff --git a/web/pgadmin/browser/server_groups/__init__.py b/web/pgadmin/browser/server_groups/__init__.py
index bfcc7f3..b5a4167 100644
--- a/web/pgadmin/browser/server_groups/__init__.py
+++ b/web/pgadmin/browser/server_groups/__init__.py
@@ -18,10 +18,10 @@ from pgadmin.utils.ajax import make_json_response
 from pgadmin.browser import BrowserPluginModule
 from pgadmin.utils.menu import MenuItem
 from pgadmin.settings.settings_model import db, ServerGroup
+from pgadmin.browser.utils import generate_browser_node
 import config
 
 
-
 class ServerGroupModule(BrowserPluginModule):
 
     NODE_TYPE = "server-group"
@@ -65,18 +65,20 @@ class ServerGroupModule(BrowserPluginModule):
         # TODO: Move this JSON generation to a Server method
         # this code is duplicated somewhere else
         for group in groups:
-            yield {
-                "id": "%s/%d" % (self.node_type, group.id),
-                "label": group.name,
-                "icon": "icon-%s" % self.node_type,
-                "inode": True,
-                "_type": self.node_type
-            }
+            yield generate_browser_node(
+                    "%d" % (group.id),
+                    group.name,
+                    "icon-%s" % self.node_type,
+                    True,
+                    self.node_type)
 
     @property
     def node_type(self):
         return self.NODE_TYPE
 
+    @property
+    def node_path(self):
+        return '/browser/' + self.node_type
 
 
 class ServerGroupMenuItem(MenuItem):
@@ -99,109 +101,142 @@ class ServerGroupPluginModule(BrowserPluginModule):
         pass
 
 
-# Initialise the module
+    @property
+    def node_path(self):
+        return '/browser/' + self.node_type
+
+
 blueprint = ServerGroupModule( __name__, static_url_path='')
 
-@blueprint.route("/<server_group>")
-@login_required
-def get_nodes(server_group):
-    """Build a list of treeview nodes from the child nodes."""
-    nodes = []
-    for module in current_blueprint.submodules:
-        nodes.extend(module.get_nodes(server_group=server_group))
-    return make_json_response(data=nodes)
-
-
-@blueprint.route('/add/', methods=['POST'])
-@login_required
-def add():
-    """Add a server group node to the settings database"""
-    success = 1
-    errormsg = ''
-    data = { }
-
-    if request.form['name'] != '':
-        servergroup = ServerGroup(user_id=current_user.id, name=request.form['name'])
-
-        try:
-            db.session.add(servergroup)
-            db.session.commit()
-        except Exception as e:
-            success = 0
-            errormsg = e.message
-
-    else:
-        success = 0
-        errormsg = gettext('No server group name was specified')
-
-    if success == 1:
-        data['id'] = servergroup.id
-        data['name'] = servergroup.name
-
-    return make_json_response(success=success,
-                              errormsg=errormsg,
-                              info=traceback.format_exc(),
-                              result=request.form,
-                              data=data)
-
-@blueprint.route('/delete/', methods=['POST'])
-@login_required
-def delete():
-    """Delete a server group node in the settings database"""
-    success = 1
-    errormsg = ''
-
-    if request.form['id'] != '':
+# Initialise the module
+from pgadmin.browser.utils import NodeView
+
+
+class ServerGroupView(NodeView):
+
+    node_type = ServerGroupModule.NODE_TYPE
+    parent_ids = []
+    ids = [{'type':'int', 'id':'gid'}]
+
+
+    def list(self):
+        res = []
+        for g in blueprint.get_nodes():
+            res.append(g)
+        return make_json_response(result=res)
+
+
+    def delete(self, gid):
+        """Delete a server group node in the settings database"""
+
         # There can be only one record at most
-        servergroup = ServerGroup.query.filter_by(user_id=current_user.id, id=int(request.form['id'])).first()
+        servergroup = ServerGroup.query.filter_by(
+                user_id=current_user.id,
+                id=gid)
 
         if servergroup is None:
-            success = 0
-            errormsg = gettext('The specified server group could not be found.')
+            return make_json_response(
+                    success=0,
+                    errormsg=gettext('The specified server group could not be found.'))
         else:
             try:
                 db.session.delete(servergroup)
                 db.session.commit()
             except Exception as e:
-                success = 0
-                errormsg = e.message
+                return make_json_response(success=0, errormsg=e.message)
 
-    else:
-        success = 0
-        errormsg = gettext('No server group  was specified.')
+        return make_json_response(result=request.form)
 
-    return make_json_response(success=success,
-                              errormsg=errormsg,
-                              info=traceback.format_exc(),
-                              result=request.form)
 
-@blueprint.route('/rename/', methods=['POST'])
-@login_required
-def rename():
-    """Rename a server group node in the settings database"""
-    success = 1
-    errormsg = ''
+    def update(self, gid):
+        """Update the server-group properties"""
 
-    if request.form['id'] != '':
         # There can be only one record at most
-        servergroup = ServerGroup.query.filter_by(user_id=current_user.id, id=int(request.form['id'])).first()
+        servergroup = ServerGroup.query.filter_by(
+                user_id=current_user.id,
+                id=gid).first()
 
         if servergroup is None:
-            success = 0
-            errormsg = gettext('The specified server group could not be found.')
+            return make_json_response(
+                    success=0,
+                    errormsg=gettext('The specified server group could not be found.'))
         else:
             try:
-                servergroup.name = request.form['name']
+                if 'name' in request.form:
+                    servergroup.name = request.form['name']
                 db.session.commit()
             except Exception as e:
-                success = 0
-                errormsg = e.message
+                return make_json_response(success=0, errormsg=e.message)
 
-    else:
-        success = 0
-        errormsg = gettext('No server group was specified.')
+        return make_json_response(result=request.form)
 
-    return make_json_response(success=success,
-                              errormsg=errormsg,
-                              info=traceback.format_exc(),
-                              result=request.form)
+
+    def properties(self, gid):
+        """Update the server-group properties"""
+
+        # There can be only one record at most
+        sg = ServerGroup.query.filter_by(
+                user_id=current_user.id,
+                id=gid).first()
+        data = {}
+
+        if sg is None:
+            return make_json_response(
+                    success=0,
+                    errormsg=gettext('The specified server group could not be found.'))
+        else:
+            return make_json_response(data={'id': sg.id, 'name': sg.name})
+
+
+    def create(self):
+        data = []
+        if request.form['name'] != '':
+            servergroup = ServerGroup(
+                    user_id=current_user.id,
+                    name=request.form['name'])
+            try:
+                db.session.add(servergroup)
+                db.session.commit()
+
+                data['id'] = servergroup.id
+                data['name'] = servergroup.name
+            except Exception as e:
+                return make_json_response(success=0, errormsg=e.message)
+
+        else:
+            return make_json_response(
+                    success=0,
+                    errormsg=gettext('No server group name was specified'))
+
+        return make_json_response(data=data)
+
+
+    def nodes(self, gid):
+        """Build a list of treeview nodes from the child nodes."""
+        nodes = []
+        for module in blueprint.submodules:
+            nodes.extend(module.get_nodes(server_group=gid))
+        return make_json_response(data=nodes)
+
+
+    def sql(self, gid):
+        return make_json_response(data='')
+
+
+    def modified_sql(self, gid):
+        return make_json_response(data='')
+
+
+    def statistics(self, gid):
+        return make_json_response(data='')
+
+
+    def dependencies(self, gid):
+        return make_json_response(data='')
+
+
+    def dependents(self, gid):
+        return make_json_response(data='')
+
+
+ServerGroupView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index 855bb19..68e501c 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -9,9 +9,10 @@
 from flask import render_template, request
 from pgadmin.browser.server_groups import ServerGroupPluginModule
 from flask.ext.security import login_required, current_user
-from pgadmin.settings.settings_model import db, Server
+from pgadmin.settings.settings_model import db, Server, ServerGroup
 from pgadmin.utils.menu import MenuItem
 from pgadmin.utils.ajax import make_json_response
+from pgadmin.browser.utils import generate_browser_node, NodeView
 import traceback
 from flask.ext.babel import gettext
 
@@ -30,13 +31,13 @@ class ServerModule(ServerGroupPluginModule):
 
         # TODO: Move this JSON generation to a Server method
         for server in servers:
-            yield {
-                "id": "%s/%d" % (NODE_TYPE, server.id),
-                "label": server.name,
-                "icon": "icon-%s" % NODE_TYPE,
-                "inode": True,
-                "_type": NODE_TYPE
-            }
+            yield generate_browser_node(
+                    "%d" % server.id,
+                    server.name,
+                    "icon-%s" % self.NODE_TYPE,
+                    True,
+                    self.NODE_TYPE
+                    )
 
     def get_own_menuitems(self):
         return {
@@ -49,13 +50,13 @@ class ServerModule(ServerGroupPluginModule):
                                name="create_server",
                                label=gettext('Server...'),
                                priority=50,
-                               function='create_server')
+                               function='create_server(item)')
             ],
             'context_items': [
                 ServerMenuItem(name='delete_server',
                                label=gettext('Delete server'),
                                priority=50,
-                               onclick='drop_server'),
+                               onclick='drop_server(item)'),
                 ServerMenuItem(name='rename_server',
                                label=gettext('Rename server...'),
                                priority=60,
@@ -75,98 +76,224 @@ class ServerMenuItem(MenuItem):
         kwargs.setdefault("type", ServerModule.NODE_TYPE)
         super(ServerMenuItem, self).__init__(**kwargs)
 
+
 blueprint = ServerModule(__name__)
 
-@blueprint.route('/add/', methods=['POST'])
-@login_required
-def add():
-    """Add a server node to the settings database"""
-    success = 1
-    errormsg = ''
-    data = {}
-
-    success = False
-    errormsg = ''
-    if request.form['name'] != '':
-        server = Server(user_id=current_user.id, name=request.form['name'])
-        try:
-            db.session.add(server)
-            db.session.commit()
-            success = True
-        except Exception as e:
-            errormsg = e.message
-    else:
-        errormsg = gettext('No server name was specified')
-
-    if success:
-        data['id'] = server.id
-        data['name'] = server.name
-
-    return make_json_response(success=success,
-                              errormsg=errormsg,
-                              info=traceback.format_exc(),
-                              result=request.form,
-                              data=data)
-
-@blueprint.route('/delete/', methods=['POST'])
-@login_required
-def delete():
-    """Delete a server node in the settings database"""
-    success = 1
-    errormsg = ''
-
-    if request.form['id'] != '':
-        # There can be only one record at most
-        servergroup = Server.query.filter_by(user_id=current_user.id, id=int(request.form['id'])).first()
 
+class ServerNode(NodeView):
+
+    node_type = ServerModule.NODE_TYPE
+    parent_ids = [{'type':'int', 'id':'gid'}]
+    ids = [{'type':'int', 'id':'sid'}]
+
+
+    def list(self, gid):
+        res = []
+        """Return a JSON document listing the server groups for the user"""
+        servers = Server.query.filter_by(user_id=current_user.id,
+                servergroup_id=gid)
+
+        for server in servers:
+            res.append(
+                    generate_browser_node(
+                        "%d/%d" % (gid, server.id),
+                        server.name,
+                        "icon-%s" % NODE_TYPE,
+                        True,
+                        NODE_TYPE
+                        )
+                    )
+        return make_json_response(result=res)
+
+
+    def delete(self, gid, sid):
+        """Delete a server node in the settings database"""
+        server = Server.query.filter_by(user_id=current_user.id, id=sid)
+
+        # TODO:: A server, which is connected, can not be deleted
         if server is None:
-            success = 0
-            errormsg = gettext('The specified server could not be found.')
+            return make_json_response(
+                    success=0,
+                    errormsg=gettext(
+                        'The specified server could not be found.\n'
+                        'Does the user have permission to access the '
+                        'server?'
+                        )
+                    )
         else:
             try:
                 db.session.delete(server)
                 db.session.commit()
             except Exception as e:
-                success = 0
-                errormsg = e.message
+                return make_json_response(
+                        success=0,
+                        errormsg=e.message)
 
-    else:
-        success = 0
-        errormsg = gettext('No server was specified.')
+        return make_json_response(success=success,
+                errormsg=errormsg,
+                info=traceback.format_exc())
 
-    return make_json_response(success=success,
-                              errormsg=errormsg,
-                              info=traceback.format_exc(),
-                              result=request.form)
 
-@blueprint.route('/rename/', methods=['POST'])
-@login_required
-def rename():
-    """Rename a server node in the settings database"""
-    success = 1
-    errormsg = ''
+    def update(self, gid, sid):
+        """Update the server settings"""
+        server = Server.query.filter_by(user_id=current_user.id, id=sid).first()
 
-    if request.form['id'] != '':
-        # There can be only one record at most
-        servergroup = Server.query.filter_by(user_id=current_user.id, id=int(request.form['id'])).first()
+        if server is None:
+            return make_json_response(
+                    success=0,
+                    errormsg=gettext("Couldn't find the given server.")
+                    )
+
+        # TODO::
+        #   Not all parameters can be modified, while the server is connected
+        possible_args = {
+                'name': 'name',
+                'host': 'host',
+                'port': 'port',
+                'db': 'maintenance_db',
+                'username': 'username',
+                'sslmode': 'sslmode',
+                'gid': 'servergroup_id'
+                }
+
+        idx = 0
+        for arg in possible_args:
+            if arg in request.form:
+                server[possible_args[arg]] = request.form[arg]
+                idx += 1
+
+        if idx == 0:
+            return make_json_response(
+                    success=0,
+                    errormsg=gettext('No parameters were chagned!')
+                    )
+
+        try:
+            db.session.commit()
+        except Exception as e:
+            return make_json_response(
+                    success=0,
+                    errormsg=e.message
+                    )
+
+        return make_json_response(
+                success=1,
+                data={
+                    'id': server.id,
+                    'gid': server.servergroup_id
+                    }
+                )
+
+
+    def properties(self, gid, sid):
+        """Return list of attributes of a server"""
+        server = Server.query.filter_by(
+                user_id=current_user.id,
+                id=sid).first()
 
         if server is None:
-            success = 0
-            errormsg = gettext('The specified server could not be found.')
-        else:
-            try:
-                server.name = request.form['name']
-                db.session.commit()
-            except Exception as e:
-                success = 0
-                errormsg = e.message
-
-    else:
-        success = 0
-        errormsg = gettext('No server was specified.')
-
-    return make_json_response(success=success,
-                              errormsg=errormsg,
-                              info=traceback.format_exc(),
-                              result=request.form)
+            return make_json_response(
+                    success=0,
+                    errormsg=gettext("Couldn't find the given server")
+                    )
+
+        sg = ServerGroup.query.filter_by(
+                user_id=current_user.id,
+                id=server.servergroup_id
+                ).first()
+
+        return make_json_response(
+                success=1,
+                data={
+                    'id':server.id,
+                    'name':server.name,
+                    'host':server.host,
+                    'port':server.port,
+                    'db':server.maintenance_db,
+                    'username':server.username,
+                    'gid':server.servergroup_id,
+                    'group-name':sg.name
+                    }
+                )
+
+
+    def create(self, gid):
+        """Add a server node to the settings database"""
+        required_args = [
+                'name',
+                'host',
+                'port',
+                'db',
+                'username',
+                'sslmode'
+                ]
+
+        for arg in required_args:
+            if arg not in request.form:
+                return make_json_response(
+                        success=0,
+                        errormsg=gettext(
+                            "Couldn't find the required parameter (%s)." % arg
+                            )
+                        )
+
+        server = Server(
+                user_id=current_user.id,
+                servergroup_id=gid,
+                name=request.form['name'],
+                host=request.form['host'],
+                port=request.form['port'],
+                maintenance_db=request.form['db'],
+                username=request.form['username'],
+                sslmode=request.form['username']
+                )
+
+        try:
+            db.session.add(server)
+            db.session.commit()
+        except Exception as e:
+            return make_json_response(
+                    success=0,
+                    errormsg=e.message
+                    )
+
+        return make_json_response(success=1,
+                data={
+                    'id': server.id,
+                    'name': server.name,
+                    'gid': gid
+                    })
+
+
+    def nodes(self, gid, sid):
+        """Build a list of treeview nodes from the child nodes."""
+        nodes = []
+        # TODO::
+        # We can have nodes for the server object, only when
+        # the server is connected at the moment.
+        for module in blueprint.submodules:
+            nodes.extend(module.get_nodes(server=sid))
+        return make_json_response(data=nodes)
+
+
+    def sql(self, gid, sid):
+        return make_json_response(data='')
+
+
+    def modified_sql(self, gid, sid):
+        return make_json_response(data='')
+
+
+    def statistics(self, gid, sid):
+        return make_json_response(data='')
+
+
+    def dependencies(self, gid, sid):
+        return make_json_response(data='')
+
+
+    def dependents(self, gid, sid):
+        return make_json_response(data='')
+
 
+ServerNode.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js b/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js
index 822441d..5361229 100644
--- a/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js
+++ b/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js
@@ -1,11 +1,18 @@
 // Add a server
-function create_server() {
+function create_server(item) {
     var alert = alertify.prompt(
         '{{ _('Create a server') }}',
         '{{ _('Enter a name for the new server') }}', 
         '', 
         function(evt, value) { 
-            $.post("{{ url_for('NODE-server.add') }}", { name: value })
+            var d = tree.itemData(item);
+            if (d._type != 'server-group') {
+                d = tree.itemData(tree.parent(item));
+            }
+            $.post(
+                "{{ url_for('browser.index') }}server/obj/" + d.refid + '/',
+                { name: value }
+                )
                 .done(function(data) {
                     if (data.success == 0) {
                         report_error(data.errormsg, data.info);
@@ -38,8 +45,10 @@ function drop_server(item) {
         '{{ _('Are you sure you wish to drop the server "{0}"?') }}'.replace('{0}', tree.getLabel(item)),
         function() {
             var id = tree.getId(item).split('/').pop()
-            $.post("{{ url_for('NODE-server.delete') }}", { id: id })
-                .done(function(data) {
+            $.ajax({
+                url:"{{ url_for('browser.index') }}" + d._type + "/obj/" + d.refid,
+                type:'DELETE',
+                success: function(data) {
                     if (data.success == 0) {
                         report_error(data.errormsg, data.info);
                     } else {
@@ -53,7 +62,7 @@ function drop_server(item) {
                         }
                     }
                 }
-            )
+            })
         },
         null
     )
@@ -66,17 +75,20 @@ function rename_server(item) {
         '{{ _('Enter a new name for the server') }}', 
         tree.getLabel(item), 
         function(evt, value) {
-            var id = tree.getId(item).split('/').pop()
-            $.post("{{ url_for('NODE-server.rename') }}", { id: id, name: value })
-                .done(function(data) {
+            var d = tree.itemData(item);
+            $.ajax({
+                url:"{{ url_for('browser.index') }}" + d._type + "/obj/" + d.refid,
+                type:'PUT',
+                params: {name: value},
+                success: function(data) {
                     if (data.success == 0) {
                         report_error(data.errormsg, data.info);
                     } else {
                         tree.setLabel(item, { label: value });
                     }
                 }
-            )
+            })
         },
         null
     )
-}
\ No newline at end of file
+}
diff --git a/web/pgadmin/browser/server_groups/templates/server_groups/server_groups.js b/web/pgadmin/browser/server_groups/templates/server_groups/server_groups.js
index f1fa85d..a154012 100644
--- a/web/pgadmin/browser/server_groups/templates/server_groups/server_groups.js
+++ b/web/pgadmin/browser/server_groups/templates/server_groups/server_groups.js
@@ -5,7 +5,7 @@ function create_server_group() {
         '{{ _('Enter a name for the new server group') }}', 
         '', 
         function(evt, value) { 
-            $.post("{{ url_for('NODE-server-group.add') }}", { name: value })
+            $.post("{{ url_for('browser.index') }}server-group/obj/", { name: value })
                 .done(function(data) {
                     if (data.success == 0) {
                         report_error(data.errormsg, data.info);
@@ -37,9 +37,11 @@ function drop_server_group(item) {
         '{{ _('Delete server group?') }}',
         '{{ _('Are you sure you wish to delete the server group "{0}"?') }}'.replace('{0}', tree.getLabel(item)),
         function() {
-            var id = tree.getId(item).split('/').pop()
-            $.post("{{ url_for('NODE-server-group.delete') }}", { id: id })
-                .done(function(data) {
+            var d = tree.itemData(item);
+            $.ajax({
+                url:"{{ url_for('browser.index') }}" + d._type + "/obj/" + d.refid,
+                type:'DELETE',
+                success: function(data) {
                     if (data.success == 0) {
                         report_error(data.errormsg, data.info);
                     } else {
@@ -53,7 +55,7 @@ function drop_server_group(item) {
                         }
                     }
                 }
-            )
+            })
         },
         null
     )
@@ -66,16 +68,19 @@ function rename_server_group(item) {
         '{{ _('Enter a new name for the server group') }}', 
         tree.getLabel(item), 
         function(evt, value) {
-            var id = tree.getId(item).split('/').pop()
-            $.post("{{ url_for('NODE-server-group.rename') }}", { id: id, name: value })
-                .done(function(data) {
+            var d = tree.itemData(item);
+            $.ajax({
+                url:"{{ url_for('browser.index') }}" + d._type + "/obj/" + d.refid,
+                type:'PUT',
+                params: { name: value },
+                success: function(data) {
                     if (data.success == 0) {
                         report_error(data.errormsg, data.info);
                     } else {
                         tree.setLabel(item, { label: value });
                     }
                 }
-            )
+            })
         },
         null
     )
diff --git a/web/pgadmin/browser/templates/browser/css/node.css b/web/pgadmin/browser/templates/browser/css/node.css
index 3fea074..da56d69 100644
--- a/web/pgadmin/browser/templates/browser/css/node.css
+++ b/web/pgadmin/browser/templates/browser/css/node.css
@@ -1,3 +1,3 @@
 .icon-{{node_type}} {
-  background: url('{{ url_for('NODE-%s.static' % node_type, filename='img/%s.png' % node_type )}}') 0 0 no-repeat;
+  background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/%s.png' % node_type )}}') !important;
 }
diff --git a/web/pgadmin/browser/templates/browser/js/browser.js b/web/pgadmin/browser/templates/browser/js/browser.js
index e1aa05b..dbdf034 100644
--- a/web/pgadmin/browser/templates/browser/js/browser.js
+++ b/web/pgadmin/browser/templates/browser/js/browser.js
@@ -298,6 +298,12 @@ ALTER TABLE tickets_detail \n\
                     return $.parseJSON(payload).data;
                 }
             }
+        },
+        ajaxHook: function(item, settings) {
+            if (item != null) {
+                var d = this.itemData(item);
+                settings.url = '{{ url_for('browser.index') }}' + d._type + '/nodes/' + d.refid
+            }
         }
     });
     tree = $('#tree').aciTree('api');
diff --git a/web/pgadmin/settings/settings_model.py b/web/pgadmin/settings/settings_model.py
index 731137b..dcd6fb6 100644
--- a/web/pgadmin/settings/settings_model.py
+++ b/web/pgadmin/settings/settings_model.py
@@ -72,13 +72,28 @@ class Server(db.Model):
     """Define a registered Postgres server"""
     __tablename__ = 'server'
     id = db.Column(db.Integer, primary_key=True)
-    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
-    servergroup_id = db.Column(db.Integer, db.ForeignKey('servergroup.id'), nullable=False)
+    user_id = db.Column(
+            db.Integer,
+            db.ForeignKey('user.id'),
+            nullable=False
+            )
+    servergroup_id = db.Column(
+            db.Integer,
+            db.ForeignKey('servergroup.id'),
+            nullable=False
+            )
     name = db.Column(db.String(128), nullable=False)
     host = db.Column(db.String(128), nullable=False)
-    port = db.Column(db.Integer(), db.CheckConstraint('port >= 1024 AND port <= 65534'), nullable=False)
+    port = db.Column(
+            db.Integer(),
+            db.CheckConstraint('port >= 1024 AND port <= 65534'),
+            nullable=False)
     maintenance_db = db.Column(db.String(64), nullable=False)
     username = db.Column(db.String(64), nullable=False)
-    ssl_mode = db.Column(db.String(16), nullable=False)
-    
-    
+    ssl_mode = db.Column(
+            db.String(16),
+            db.CheckConstraint(
+                "ssl_mode IN ('allow', 'prefer', 'require', 'disable', 'verify-ca', 'verify-full')"
+                ),
+            nullable=False,
+            )
diff --git a/web/pgadmin/templates/base.html b/web/pgadmin/templates/base.html
index 5a36182..300e888 100755
--- a/web/pgadmin/templates/base.html
+++ b/web/pgadmin/templates/base.html
@@ -15,25 +15,25 @@
         <meta name="dcterms.dateCopyrighted" content="2014 - 2015">
 
         <!-- Base template stylesheets -->
-        {% if config.DEBUG %}<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.css') }}" />{% else %}<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}" />{% endif %}
-        {% if config.DEBUG %}<link rel="stylesheet" href="{{ url_for('static', filename='css/alertifyjs/alertify.css') }}" />{% else %}<link rel="stylesheet" href="{{ url_for('static', filename='css/alertifyjs/alertify.min.css') }}" />{% endif %}
-        {% if config.DEBUG %}<link rel="stylesheet" href="{{ url_for('static', filename='css/alertifyjs/themes/bootstrap.css') }}" />{% else %}<link rel="stylesheet" href="{{ url_for('static', filename='css/alertifyjs/themes/bootstrap.min.css') }}" />{% endif %}
-        {% if config.DEBUG %}<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap-theme.min.css') }}">{% else %}<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap-theme.min.css') }}">{% endif %}
-        <link rel="stylesheet" href="{{ url_for('static', filename='css/overrides.css') }}">
+        <link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.css' if config.DEBUG else 'css/bootstrap.min.css')}}"/>
+        <link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/alertifyjs/alertify.css' if config.DEBUG else 'css/alertifyjs/alertify.min.css') }}" />
+        <link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/alertifyjs/themes/bootstrap.css' if config.DEBUG else 'css/alertifyjs/themes/bootstrap.min.css') }}" />
+        <link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap-theme.min.css' if config.DEBUG else 'css/bootstrap-theme.css') }}">
+        <link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/overrides.css') }}">
         <!-- View specified stylesheets -->
         {% for stylesheet in current_app.stylesheets %}
-        <link rel="stylesheet" href="{{ stylesheet }}">
+        <link type="text/css" rel="stylesheet" href="{{ stylesheet }}">
         {% endfor %}
         <!-- Base template scripts -->
-        <script src="{{ url_for('static', filename='js/modernizr-2.6.2-respond-1.1.0.min.js') }}"></script>
-        {% if config.DEBUG %}<script src="{{ url_for('static', filename='js/jquery-1.11.2.js') }}">{% else %}<script src="{{ url_for('static', filename='js/jquery-1.11.2.min.js') }}">{% endif %}</script>
-        {% if config.DEBUG %}<script src="{{ url_for('static', filename='js/bootstrap.js') }}">{% else %}<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}">{% endif %}</script>
-        {% if config.DEBUG %}<script src="{{ url_for('static', filename='js/alertifyjs/alertify.js') }}">{% else %}<script src="{{ url_for('static', filename='js/alertifyjs/alertify.min.js') }}">{% endif %}</script>
-        <script src="{{ url_for('static', filename='js/alertifyjs/pgadmin.defaults.js') }}"></script>
+        <script type="text/javascript" src="{{ url_for('static', filename='js/modernizr-2.6.2-respond-1.1.0.min.js') }}"></script>
+        <script type="text/javascript" src="{{ url_for('static', filename='js/jquery-1.11.2.js' if config.DEBUG else 'js/jquery-1.11.2.min.js') }}"></script>
+        <script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap.js' if config.DEBUG else 'js/bootstrap.min.js') }}"></script>
+        <script type="text/javascript" src="{{ url_for('static', filename='js/alertifyjs/alertify.js' if config.DEBUG else 'js/alertifyjs/alertify.min.js') }}"></script>
+        <script type="text/javascript" src="{{ url_for('static', filename='js/alertifyjs/pgadmin.defaults.js') }}"></script>
         <!-- View specified scripts -->
 
         {% for script in current_app.javascripts %}
-        <script src="{{ script }}"></script>
+        <script type="text/javascript" src="{{ script }}"></script>
         {% endfor %}
     </head>
     <body>
diff --git a/web/pgadmin/utils/ajax.py b/web/pgadmin/utils/ajax.py
index 367c356..b5f938c 100644
--- a/web/pgadmin/utils/ajax.py
+++ b/web/pgadmin/utils/ajax.py
@@ -25,4 +25,4 @@ def make_json_response(success=1, errormsg='', info='', result={}, data={}):
     response = Response(response=json.dumps(doc),
                         status=200,
                         mimetype="text/json")
-    return response
\ No newline at end of file
+    return response
diff --git a/web/setup.py b/web/setup.py
index c7398f5..22fbf24 100644
--- a/web/setup.py
+++ b/web/setup.py
@@ -66,6 +66,19 @@ def do_setup():
         server_group = ServerGroup(user_id=user.id, name="Servers")
         db.session.merge(server_group)
 
+        # TODO:: Remove this server later
+        #        It is here to demo the server listing is workig in
+        #        browser tree.
+        server_group = ServerGroup.query.filter_by(name='Servers').first()
+
+        server = Server(
+                user_id=user.id, servergroup_id=server_group.id,
+                name='PostgreSQL 9.3', host='localhost', port=3930,
+                maintenance_db='postgres', username='asheshvashi',
+                ssl_mode='prefer'
+                )
+        db.session.merge(server)
+
         # Set the schema version
         version = Version(name='ConfigDB', value=config.SETTINGS_SCHEMA_VERSION)
         db.session.merge(version)
@@ -138,4 +151,4 @@ if os.path.isfile(config.SQLITE_PATH):
     do_upgrade()
 else:    
     print "The configuration database %s does not exist.\nEntering initial setup mode...\n" % config.SQLITE_PATH
-    do_setup()
\ No newline at end of file
+    do_setup()
-- 
Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers

Reply via email to