Hi Dave,

I have found out the root cause of this issue.

This issue is occurring at your side as you are using python 2.7 so in that
case sql query returned from sql template is of type 'unicode'.
And in __init__.py file we have checked if the sql query is of string type
then only we would proceed further.

I was not getting this issue as I was using python 3.0

I have resolved this issue and some other typo errors in comments and patch
is attached with this mail.

Please do review it and let me know in case of any issues.





Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

On Tue, Mar 22, 2016 at 8:11 PM, Dave Page <dp...@pgadmin.org> wrote:

> Hi,
>
> This seems to be fine, except:
>
> - The icons were missing from the patch (I added them manually)
>
> - The issue you couldn't reproduce that I reported previously still
> exists. I created an FTS template per the attached screenshots, and
> the SQL tab shows only "Modified SQL". The template is correctly
> created though. When I edit the template, I can also successfully
> rename it and update the comment, though the SQL tab still says
> "Modified SQL".
>
>
>
> On Wed, Mar 16, 2016 at 7:10 AM, Sanket Mehta
> <sanket.me...@enterprisedb.com> wrote:
> > Hi,
> >
> > Sorry its misunderstanding from my side.
> > That node refresh on tree issue has been resolved.
> >
> > Patch is attached with this mail.
> >
> > Please do review it.
> >
> > Regards,
> > Sanket Mehta
> > Sr Software engineer
> > Enterprisedb
> >
> > On Mon, Mar 14, 2016 at 7:27 PM, Dave Page <dp...@pgadmin.org> wrote:
> >>
> >> On Mon, Mar 14, 2016 at 1:52 PM, Sanket Mehta
> >> <sanket.me...@enterprisedb.com> wrote:
> >> > Hi Dave,
> >> >
> >> > I have incorporated your changes.
> >> > I have also tried to reproduce the Issues that you have mentioned.
> >> > find the response inline.
> >> >
> >> > Also find revised patch having your suggested changes attached with
> this
> >> > mail.
> >> > Please do review it and let me know in case anything is missing.
> >> >
> >> >
> >> > Regards,
> >> > Sanket Mehta
> >> > Sr Software engineer
> >> > Enterprisedb
> >> >
> >> > On Fri, Mar 11, 2016 at 6:41 PM, Dave Page <dp...@pgadmin.org> wrote:
> >> >>
> >> >> Hi
> >> >>
> >> >> On Thu, Mar 10, 2016 at 6:28 AM, Sanket Mehta
> >> >> <sanket.me...@enterprisedb.com> wrote:
> >> >> > Hi,
> >> >> >
> >> >> > PFA the patch for fts template after replacing collectionNodeModule
> >> >> > with
> >> >> > SchemaChildModule in __init__.py
> >> >> >
> >> >> > Apart from that, there was an error while updating the name of fts
> >> >> > template
> >> >> > which I have resolved in the same.
> >> >> >
> >> >> > Please do review it and let me know in case of any changes.
> >> >>
> >> >> I've updated the patch for consistency with others. Please find
> >> >> attached. Aside from those changes, there are two issues:
> >> >>
> >> >> - When creating or editing a template the SQL pane just says
> >> >> "--modified SQL" even when all required options are specified. The
> >> >> template is created correctly though.
> >> >
> >> > I have tried to reproduce this issue on my system but could not
> >> > reproduce
> >> > it. PFA screen shorts explaining sql I got while creating and editing
> a
> >> > template.
> >> >
> >> >>
> >> >> - If a template is renamed, the treeview node is not updated.
> >> >
> >> > This is generic issue and needed to be resolved at higher level.
> >>
> >> No, I tested other nodes (database and sequence for example) and they
> >> were properly updated. It's possible *some* are broken, but it's
> >> certainly not a general issue.
> >>
> >>
> >> --
> >> Dave Page
> >> Blog: http://pgsnake.blogspot.com
> >> Twitter: @pgsnake
> >>
> >> EnterpriseDB UK: http://www.enterprisedb.com
> >> The Enterprise PostgreSQL Company
> >
> >
>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/__init__.py
new file mode 100644
index 0000000..ddd734b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/__init__.py
@@ -0,0 +1,719 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Defines views for management of Fts Template node"""
+
+import json
+from flask import render_template, make_response, current_app, request, jsonify
+from flask.ext.babel import gettext
+from pgadmin.utils.ajax import make_json_response, \
+    make_response as ajax_response, internal_server_error
+from pgadmin.browser.utils import PGChildNodeView
+from pgadmin.browser.server_groups.servers.databases.schemas.utils import SchemaChildModule
+import pgadmin.browser.server_groups.servers.databases.schemas as schemas
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class FtsTemplateModule(SchemaChildModule):
+    """
+     class FtsTemplateModule(SchemaChildModule)
+
+        A module class for FTS Template node derived from SchemaChildModule.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Method is used to initialize the FtsTemplateModule and it's base module.
+
+    * get_nodes(gid, sid, did, scid)
+      - Method is used to generate the browser collection node.
+
+    * node_inode()
+      - Method is overridden from its base class to make the node as leaf node.
+
+    * script_load()
+      - Load the module script for FTS Template, when any of the schema node is
+        initialized.
+    """
+    NODE_TYPE = 'fts_template'
+    COLLECTION_LABEL = gettext('FTS Templates')
+
+    def __init__(self, *args, **kwargs):
+        self.min_ver = None
+        self.max_ver = None
+        super(FtsTemplateModule, self).__init__(*args, **kwargs)
+
+    def get_nodes(self, gid, sid, did, scid):
+        """
+        Generate the collection node
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param scid: schema id
+        """
+        yield self.generate_browser_collection_node(scid)
+
+    @property
+    def node_inode(self):
+        """
+        Override the property to make the node as leaf node
+        """
+        return False
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for fts template, when any of the schema node is
+        initialized.
+        """
+        return schemas.SchemaModule.NODE_TYPE
+
+
+blueprint = FtsTemplateModule(__name__)
+
+
+class FtsTemplateView(PGChildNodeView):
+    """
+    class FtsTemplateView(PGChildNodeView)
+
+        A view class for FTS Tempalte node derived from PGChildNodeView. This class is
+        responsible for all the stuff related to view like create/update/delete
+        responsible for all the stuff related to view like create/update/delete
+        FTS template, showing properties of node, showing sql in sql pane.
+
+    Methods:
+    -------
+    * __init__(**kwargs)
+      - Method is used to initialize the FtsTemplateView and it's base view.
+
+    * module_js()
+      - This property defines (if javascript) exists for this node.
+        Override this property for your own logic
+
+    * check_precondition()
+      - This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+
+    * list()
+      - This function is used to list all the  nodes within that collection.
+
+    * nodes()
+      - This function will used to create all the child node within that collection.
+        Here it will create all the FTS Template nodes.
+
+    * properties(gid, sid, did, scid, tid)
+      - This function will show the properties of the selected FTS Template node
+
+    * create(gid, sid, did, scid)
+      - This function will create the new FTS Template object
+
+    * update(gid, sid, did, scid, tid)
+      - This function will update the data for the selected FTS Template node
+
+    * delete(self, gid, sid, did, scid, tid):
+      - This function will drop the FTS Template object
+
+    * msql(gid, sid, did, scid, tid)
+      - This function is used to return modified SQL for the selected FTS Template node
+
+    * get_sql(data, tid)
+      - This function will generate sql from model data
+
+    * sql(gid, sid, did, scid,  tid):
+      - This function will generate sql to show it in sql pane for the selected FTS Template node.
+
+    * get_type():
+      - This function will fetch all the types for source and target types select control.
+
+    * dependents(gid, sid, did, scid, tid):
+      - This function get the dependents and return ajax response for the Fts Tempalte node.
+
+    * dependencies(self, gid, sid, did, scid, tid):
+      - This function get the dependencies and return ajax response for the FTS Tempalte node.
+
+    """
+
+    node_type = blueprint.node_type
+
+    parent_ids = [
+        {'type': 'int', 'id': 'gid'},
+        {'type': 'int', 'id': 'sid'},
+        {'type': 'int', 'id': 'did'},
+        {'type': 'int', 'id': 'scid'}
+    ]
+    ids = [
+        {'type': 'int', 'id': 'tid'}
+    ]
+
+    operations = dict({
+        'obj': [
+            {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+            {'get': 'list', 'post': 'create'}
+        ],
+        'children': [{
+            'get': 'children'
+        }],
+        'delete': [{'delete': 'delete'}],
+        'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+        'sql': [{'get': 'sql'}],
+        'msql': [{'get': 'msql'}, {'get': 'msql'}],
+        'stats': [{'get': 'statistics'}],
+        'dependency': [{'get': 'dependencies'}],
+        'dependent': [{'get': 'dependents'}],
+        'module.js': [{}, {}, {'get': 'module_js'}],
+        'get_lexize': [{'get': 'get_lexize'}, {'get': 'get_lexize'}],
+        'get_init': [{'get': 'get_init'}, {'get': 'get_init'}],
+    })
+
+    def _init_(self, **kwargs):
+        self.conn = None
+        self.template_path = None
+        self.manager = None
+        super(FtsTemplateView, self).__init__(**kwargs)
+
+    def module_js(self):
+        """
+        This property defines whether javascript exists for this node.
+        """
+        return make_response(
+            render_template(
+                "fts_template/js/fts_templates.js",
+                _=gettext
+            ),
+            200, {'Content-Type': 'application/x-javascript'}
+        )
+
+    def check_precondition(f):
+        """
+        This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+        """
+
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            self = args[0]
+            self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
+                kwargs['sid'])
+            self.conn = self.manager.connection(did=kwargs['did'])
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    gettext(
+                        "Connection to the server has been lost!"
+                    )
+                )
+            # we will set template path for sql scripts depending upon server version
+            ver = self.manager.version
+            if ver >= 90100:
+                self.template_path = 'fts_template/sql/9.1_plus'
+            return f(*args, **kwargs)
+
+        return wrap
+
+    @check_precondition
+    def list(self, gid, sid, did, scid):
+        sql = render_template(
+            "/".join([self.template_path, 'properties.sql']),
+            scid=scid
+        )
+        status, res = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        return ajax_response(
+            response=res['rows'],
+            status=200
+        )
+
+    @check_precondition
+    def nodes(self, gid, sid, did, scid):
+        res = []
+        sql = render_template(
+            "/".join([self.template_path, 'nodes.sql']),
+            scid=scid
+        )
+        status, rset = self.conn.execute_2darray(sql)
+        if not status:
+            return internal_server_error(errormsg=rset)
+
+        for row in rset['rows']:
+            res.append(
+                self.blueprint.generate_browser_node(
+                    row['oid'],
+                    did,
+                    row['name'],
+                    icon="icon-fts_template"
+                ))
+
+        return make_json_response(
+            data=res,
+            status=200
+        )
+
+    @check_precondition
+    def node(self, gid, sid, did, scid, tid):
+        res = []
+        sql = render_template(
+            "/".join([self.template_path, 'nodes.sql']),
+            tid=tid
+        )
+        status, rset = self.conn.execute_2darray(sql)
+        if not status:
+            return internal_server_error(errormsg=rset)
+
+        for row in rset['rows']:
+            return make_json_response(
+                data=self.blueprint.generate_browser_node(
+                        row['oid'],
+                        did,
+                        row['name'],
+                        icon="icon-fts_template"
+                    ),
+                status=200
+            )
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, tid):
+        sql = render_template(
+            "/".join([self.template_path, 'properties.sql']),
+            scid=scid,
+            tid=tid
+        )
+        status, res = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        return ajax_response(
+            response=res['rows'][0],
+            status=200
+        )
+
+    @check_precondition
+    def create(self, gid, sid, did, scid):
+        """
+        This function will creates new the fts_template object
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param scid: schema id
+        """
+
+        # Mandatory fields to create a new fts template
+        required_args = [
+            'tmpllexize',
+            'schema',
+            'name'
+        ]
+
+        data = request.form if request.form else json.loads(
+            request.data.decode())
+        for arg in required_args:
+            if arg not in data:
+                return make_json_response(
+                    status=410,
+                    success=0,
+                    errormsg=gettext(
+                        "Couldn't find the required parameter (%s)." % arg
+                    )
+                )
+        try:
+            # Fetch schema name from schema oid
+            sql = render_template("/".join([self.template_path, 'schema.sql']),
+                                  data=data,
+                                  conn=self.conn,
+                                  )
+
+            status, schema = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=schema)
+
+            # replace schema oid with schema name before passing to create.sql
+            # to generate proper sql query
+            new_data = data.copy()
+            new_data['schema'] = schema
+            sql = render_template("/".join([self.template_path, 'create.sql']),
+                                  data=new_data,
+                                  conn=self.conn,
+                                  )
+            status, res = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            # we need fts_template id to to add object in tree at browser,
+            # below sql will give the same
+            sql = render_template(
+                "/".join([self.template_path, 'properties.sql']),
+                name=data['name']
+            )
+            status, tid = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=tid)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    tid,
+                    did,
+                    data['name'],
+                    icon="icon-fts_template"
+                )
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def update(self, gid, sid, did, scid, tid):
+        """
+        This function will update text search template object
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param scid: schema id
+        :param tid: fts tempate id
+        """
+        data = request.form if request.form else json.loads(
+            request.data.decode())
+
+        # Fetch sql query to update fts template
+        sql = self.get_sql(gid, sid, did, scid, data, tid)
+        try:
+            if sql and sql.strip('\n') and sql.strip(' '):
+                status, res = self.conn.execute_scalar(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                return make_json_response(
+                    success=1,
+                    info="FTS Template updated",
+                    data={
+                        'id': tid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did,
+                        'scid': scid
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': tid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did,
+                        'scid': scid
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, tid):
+        """
+        This function will drop the fts_template object
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param scid: schema id
+        :param tid: fts tempate id
+        """
+        # Below will decide if it's simple drop or drop with cascade call
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+
+        try:
+            # Get name for template from tid
+            sql = render_template("/".join([self.template_path, 'delete.sql']),
+                                  tid=tid)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            # Drop fts template
+            result = res['rows'][0]
+            sql = render_template("/".join([self.template_path, 'delete.sql']),
+                                  name=result['name'],
+                                  schema=result['schema'],
+                                  cascade=cascade
+                                  )
+
+            status, res = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info=gettext("FTS template dropped"),
+                data={
+                    'id': tid,
+                    'sid': sid,
+                    'gid': gid,
+                    'did': did,
+                    'scid': scid
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def msql(self, gid, sid, did, scid, tid=None):
+        """
+        This function returns modified SQL
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param scid: schema id
+        :param tid: fts tempate id
+        """
+        data = request.args
+
+        # Fetch sql query for modified data
+        sql = self.get_sql(gid, sid, did, scid, data, tid)
+
+        if isinstance(sql, str) and sql and sql.strip('\n') and sql.strip(' '):
+            return make_json_response(
+                data=sql,
+                status=200
+            )
+        else:
+            return make_json_response(
+                data="--modified SQL",
+                status=200
+            )
+
+    def get_sql(self, gid, sid, did, scid, data, tid=None):
+        """
+        This function will return SQL for model data
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param scid: schema id
+        :param tid: fts tempate id
+        """
+        try:
+            # Fetch sql for update
+            if tid is not None:
+                sql = render_template(
+                    "/".join([self.template_path, 'properties.sql']),
+                    tid=tid,
+                    scid=scid
+                )
+
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                old_data = res['rows'][0]
+
+                # If user has changed the schema then fetch new schema directly
+                # using its oid otherwise fetch old schema name using fts template oid
+                sql = render_template(
+                    "/".join([self.template_path, 'schema.sql']),
+                    data=data)
+
+                status, new_schema = self.conn.execute_scalar(sql)
+                if not status:
+                    return internal_server_error(errormsg=new_schema)
+
+                # Replace schema oid with schema name
+                new_data = data.copy()
+                if 'schema' in new_data:
+                    new_data['schema'] = new_schema
+
+                # Fetch old schema name using old schema oid
+                sql = render_template(
+                    "/".join([self.template_path, 'schema.sql']),
+                    data=old_data)
+
+                status, old_schema = self.conn.execute_scalar(sql)
+                if not status:
+                    return internal_server_error(errormsg=old_schema)
+
+                # Replace old schema oid with old schema name
+                old_data['schema'] = old_schema
+
+                sql = render_template(
+                    "/".join([self.template_path, 'update.sql']),
+                    data=new_data, o_data=old_data
+                )
+                # Fetch sql query for modified data
+            else:
+                # Fetch schema name from schema oid
+                sql = render_template("/".join([self.template_path, 'schema.sql']),
+                                      data=data)
+
+                status, schema = self.conn.execute_scalar(sql)
+                if not status:
+                    return internal_server_error(errormsg=schema)
+
+                # Replace schema oid with schema name
+                new_data = data.copy()
+                new_data['schema'] = schema
+
+                if 'tmpllexize' in new_data and \
+                                'name' in new_data and \
+                                'schema' in new_data:
+                    sql = render_template("/".join([self.template_path,
+                                                    'create.sql']),
+                                          data=new_data,
+                                          conn=self.conn
+                                          )
+                else:
+                    sql = "-- incomplete definition"
+            return str(sql.strip('\n'))
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_lexize(self, gid, sid, did, scid, tid=None):
+        """
+        This function will return lexize functions list for fts template
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param scid: schema id
+        :param tid: fts tempate id
+        """
+        data = request.args
+        sql = render_template("/".join([self.template_path, 'functions.sql']),
+                              lexize=True)
+        status, rset = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=rset)
+
+        # Empty set is added before actual list as initially it will be visible
+        # at lexize select control while creating a new fts template
+        res = [{'label': '', 'value': ''}]
+        for row in rset['rows']:
+            res.append({'label': row['proname'],
+                        'value': row['proname']})
+        return make_json_response(
+            data=res,
+            status=200
+        )
+
+    @check_precondition
+    def get_init(self, gid, sid, did, scid, tid=None):
+        """
+        This function will return init functions list for fts template
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param scid: schema id
+        :param tid: fts tempate id
+        """
+        data = request.args
+        sql = render_template("/".join([self.template_path, 'functions.sql']),
+                              init=True)
+        status, rset = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=rset)
+
+        # We have added this to map against '-' which is coming from server
+        res = [{'label': '', 'value': '-'}]
+        for row in rset['rows']:
+            res.append({'label': row['proname'],
+                        'value': row['proname']})
+        return make_json_response(
+            data=res,
+            status=200
+        )
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, tid):
+        """
+        This function will reverse generate sql for sql panel
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param scid: schema id
+        :param tid: fts tempate id
+        """
+        try:
+            sql = render_template(
+                "/".join([self.template_path, 'sql.sql']),
+                tid=tid,
+                scid=scid,
+                conn=self.conn
+            )
+            status, res = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(
+                    _(
+                        "ERROR: Couldn't generate reversed engineered Query for the FTS template!\n{0}").format(
+                        res
+                    )
+                )
+
+            if res is None:
+                return gone(
+                    _(
+                        "ERROR: Couldn't generate reversed engineered Query for FTS template node!")
+                )
+
+            return ajax_response(response=res)
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def dependents(self, gid, sid, did, scid, tid):
+        """
+        This function get the dependents and return ajax response
+        for the FTS Template node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: FTS Template ID
+        """
+        dependents_result = self.get_dependents(self.conn, tid)
+        return ajax_response(
+                response=dependents_result,
+                status=200
+                )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, scid, tid):
+        """
+        This function get the dependencies and return ajax response
+        for the FTS Template node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            scid: Schema ID
+            tid: FTS Tempalte ID
+        """
+        dependencies_result = self.get_dependencies(self.conn, tid)
+        return ajax_response(
+                response=dependencies_result,
+                status=200
+                )
+
+FtsTemplateView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/img/coll-fts_template.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/img/coll-fts_template.png
new file mode 100644
index 0000000000000000000000000000000000000000..fabd47da7ae27b04b0bfd38a7b1ee9f6c1a74d2c
GIT binary patch
literal 601
zcmY+9Ye-W86vyuj!!5?7zDS=7f?QnJEZv8iwXIEZ*mjEw%H7=uILpipL87b$mOaoY
zG^59ICOS;*K|&8hv}qE{A&!ucGBAp8clWjXUN`;JhyVGV1Lp_lv{kzg=yjWQ008J+
zPMb$%;lC8DQ|mUP!K;F=J_y-WCa0Pc@5*zzR4zBCD6@+4NtUN2X;PBj=W_GWZkwVc
zWch<AzUKL-I1VYwXB<By%TpNUmZUKhtrUb892b%08Ci}?(zqzT6~$3Oc+T^K9M{LP
zw=t|r6yJ#AD?u3H`54DNK@cm;cGGmLAiNZWVV)o0xM()}fMvTF<`%=;K+*ji*OSfO
zXW34M>7eOret#)NwPdsRSoRLXw9|AOMO~(-3nbZ`$%Op=GMc^u!)A&)55r(46ZH9t
z38IdoE|KIpl5EOk8VRDFAOh($o=&3+LQOz@UK8@Ty?}N33$6~@2IsM$O6O`EAM5t0
zO_$470Ts<5J~XUxtOtF59LHe{`S}eE>l)5RWAIXicVhB-Uu&D8O228Z^XA1!$H>+G
zyJb#mUa!MbdE9ZteNwX}Qs9a94)(<Qh8}A-&gKmyQ_1<c-Jm&72b|DB$SHpSuZL^%
zpB44T7vibJ;&@V940wM0S^B-a^8HtSaIAB*efUv})mI1s6I0UwKrb31UlSXe)DyRc
z>_<Z=9KtL}Bc=i{fhC}^#AF0ZAQNaY@3fdpw}Yh?5PZ0@Ble$g`V3l&H?Ij``Kq99
Yn=Rn3HDRXf@SrLHF1y<{w9gm*3#c75djJ3c

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/img/fts_template.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/static/img/fts_template.png
new file mode 100644
index 0000000000000000000000000000000000000000..eb780bf0bb0f5d777e8a72720b5ed0e2ad48bc45
GIT binary patch
literal 696
zcmY+CS4>j@6ozkcq>wQ5!GLk#CLzU3S)oQ!Yr$eFU<-t$cHKq-h(bd^Q2~h-MIjMo
zBO)RJ1T=0%>WGXeVM%<^_+&IudiTD!Ug61y|NQ4q&iC@?)TAUG@by{m0|0<;qE2g&
zJm8<?E2J5s@HYa0>`;n6HC~D{V`Wn#u^!KY$1^XAv!XaHik}2wQV_;?{=MCv=JCwg
z?M6|Yf}q2KFb+Y<JpTrQ^c?p>6lX;7qaaKO!Uvvz%k!fg_mbnDvg{+49p?FWJn!PT
zR~+|@WgoNb1BSV8vuSC%o99P3?m5doVcCZaGsrM~G<}z*Z&Oq|NjBSTI+h(`n0}h>
zq3JG)>Y%6=lDuiLs0iXJ!wfLYJ(|8lQMV|nl_VQU@;X6W!|}^Fei6f}Xu8vCjU&k>
zl58M|I)b=@<Ck#!0)|zX%}Nw4CCM8EQBM%HIPS#pY7DEyu(K#yhN2}1Qj8#lI9_A1
zM4{+uvl%p*6fj(1GKIqMaSS_;qGu4qfgq<4<OB@o!EmnIZFjqEZnt$+xwl3tROT=w
z83FZ=FLo(-pVVa*ORif|!bD!2v~(qE_oW8TSxqjWRxfwS*P1M=z1F2im-r=x?Q{Y$
z{<}AiR&>@6%eCr;WPOgta4;(&-EXUFOUO`XZ}&h~Uyt9KfZ6H!*@c<Ub1`7Gd^|9y
zwW*~!J0mM||H}OQKfgC_Xm2}`^EKFOaZ|_9Z*i~BiL%Oo%Fgfg)5EZHy!U6IQ=Q+I
ztuEI%EQTT<ZE%zV+`BCzbkDAsI`95tav4ziD`;WBxc%P;?Qq0rIBaGIq_P%4k^xFE
z98`oW6<~O(5>!P-s3N06!6+37b}WYV{Rb2j+H&n>%K#X=1W4DBDlmE(m~2cNkN_Yt
LK1n;MF;)Eq048yN

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/js/fts_templates.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/js/fts_templates.js
new file mode 100644
index 0000000..7961edf
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/js/fts_templates.js
@@ -0,0 +1,133 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+  // Extend the collection class for fts template
+  if (!pgBrowser.Nodes['coll-fts_template']) {
+    var fts_templates = pgAdmin.Browser.Nodes['coll-fts_template'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'fts_template',
+        label: '{{ _('FTS Templates') }}',
+        type: 'coll-fts_template',
+        columns: ['name', 'description']
+      });
+  };
+
+  // Extend the node class for fts template
+  if (!pgBrowser.Nodes['fts_template']) {
+    pgAdmin.Browser.Nodes['fts_template'] = pgAdmin.Browser.Node.extend({
+      parent_type: ['schema', 'catalog'],
+      type: 'fts_template',
+      canDrop: true,
+      canDropCascade: true,
+      label: '{{ _('FTS Templates') }}',
+      hasSQL: true,
+      hasDepends: true,
+      Init: function() {
+
+        // Avoid multiple registration of menus
+        if (this.initialized)
+          return;
+
+        this.initialized = true;
+
+        // Add context menus for fts template
+        pgBrowser.add_menus([{
+          name: 'create_fts_template_on_schema', node: 'schema', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('FTS Template...') }}',
+          icon: 'wcTabIcon icon-fts_template', data: {action: 'create'}
+          },{
+          name: 'create_fts_template_on_coll', node: 'coll-fts_template', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('FTS Template...') }}',
+          icon: 'wcTabIcon icon-fts_template', data: {action: 'create'}
+          },{
+          name: 'create_fts_template', node: 'fts_template', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('FTS Template...') }}',
+          icon: 'wcTabIcon icon-fts_template', data: {action: 'create'}
+          }]);
+
+      },
+
+      // Defining backform model for fts template node
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,      // Fts template name
+          description: undefined,   // Comment on template
+          schema: undefined,        // Schema name to which template belongs
+          tmplinit: undefined,      // Init function for fts template
+          tmpllexize: undefined     // Lexize function for fts template
+        },
+        initialize: function() {
+            pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+            if (_.isUndefined(this.get('schema'))) {
+                this.set('schema', this.node_info.schema._id);
+            }
+        },
+        // Defining schema for fts template
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', cell: 'string',
+          type: 'text', cellHeaderClasses: 'width_percent_50'
+        },{
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          editable: false, type: 'text', disabled: true, mode:['properties']
+        },{
+          id: 'schema', label: '{{ _('Schema')}}', cell: 'string',
+          type: 'text', mode: ['create','edit'], node: 'schema',
+          control: 'node-list-by-id'
+        },{
+          id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline', cellHeaderClasses: 'width_percent_50'
+        },{
+          id: 'tmplinit', label: '{{ _('Init Function')}}', group: '{{ _('Definition') }}',
+          type: 'text', disabled: function(m) { return !m.isNew(); },
+          control: 'node-ajax-options', url: 'get_init'
+        },{
+          id: 'tmpllexize', label: '{{ _('Lexize Function')}}', group: '{{ _('Definition') }}',
+          type: 'text', disabled: function(m) { return !m.isNew(); },
+          control: 'node-ajax-options', url: 'get_lexize'
+        }],
+
+        /*
+         * Triggers control specific error messages for template name,
+         * lexize function and schema, if any one of them is not specified
+         * while creating new fts template
+         */
+        validate: function(keys){
+          var name = this.get('name');
+          var lexize = this.get('tmpllexize');
+          var schema = this.get('schema');
+
+          // Validate fts template name
+          if (_.isUndefined(name) || _.isNull(name) || String(name).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = '{{ _('Name must be specified!') }}';
+            this.errorModel.set('name', msg);
+            return msg;
+          }
+
+          // Validate lexize function control
+          else if (_.isUndefined(lexize) || _.isNull(lexize) || String(lexize).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = '{{ _('Lexize function must be selected!') }}';
+            this.errorModel.set('tmpllexize', msg);
+            return msg;
+          }
+
+          // Validate schema for fts template
+          else if (_.isUndefined(schema) || _.isNull(schema) || String(schema).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = '{{ _('Schema must be selected!') }}';
+            this.errorModel.set('schema', msg);
+            return msg;
+          }
+          else this.errorModel.clear();
+
+          this.trigger('on-status-clear');
+          return null;
+        }
+      })
+    });
+  }
+
+return pgBrowser.Nodes['coll-fts_template'];
+});
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..3a290b3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/create.sql
@@ -0,0 +1,11 @@
+{# CREATE TEXT SEARCH TEMPLATE Statement #}
+{% if data and data.schema and data.name and data.tmpllexize %}
+CREATE TEXT SEARCH TEMPLATE {{ conn|qtIdent(data.schema, data.name) }} (
+{% if data.tmplinit and data.tmplinit != '-'%}    INIT = {{data.tmplinit}},{% endif %}
+    LEXIZE = {{data.tmpllexize}}
+);
+{# Description for TEXT SEARCH TEMPLATE #}
+{% if data.description %}
+COMMENT ON TEXT SEARCH TEMPLATE {{ conn|qtIdent(data.schema, data.name) }}
+      IS {{ data.description|qtLiteral }};
+{% endif %}{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..cca8dc4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/delete.sql
@@ -0,0 +1,23 @@
+{# FETCH TEXT SEARCH TEMPLATE NAME Statement #}
+{% if tid %}
+SELECT
+    t.tmplname AS name,
+    (
+    SELECT
+        nspname
+    FROM
+        pg_namespace
+    WHERE
+        oid = t.tmplnamespace
+    ) as schema
+FROM
+    pg_ts_template t LEFT JOIN pg_description d
+    ON d.objoid=t.oid AND d.classoid='pg_ts_template'::regclass
+WHERE
+    t.oid = {{tid}}::OID;
+{% endif %}
+
+{# DROP TEXT SEARCH TEMPLATE Statement #}
+{% if schema and name %}
+DROP TEXT SEARCH TEMPLATE {{conn|qtIdent(schema)}}.{{conn|qtIdent(name)}} {% if cascade %}CASCADE{%endif%};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/functions.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/functions.sql
new file mode 100644
index 0000000..2fe9e02
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/functions.sql
@@ -0,0 +1,23 @@
+{# FETCH lexize functions for TEXT SEARCH TEMPLATE #}
+{% if lexize %}
+SELECT
+    proname, nspname
+FROM
+    pg_proc JOIN pg_namespace n ON n.oid=pronamespace
+WHERE
+    prorettype=2281
+    AND proargtypes='2281 2281 2281 2281'
+ORDER BY proname;
+{% endif %}
+
+{# FETCH init functions for TEXT SEARCH TEMPLATE #}
+{% if init %}
+SELECT
+    proname, nspname
+FROM
+    pg_proc JOIN pg_namespace n ON n.oid=pronamespace
+WHERE
+    prorettype=2281 and proargtypes='2281'
+ORDER BY
+    proname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..850247e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/nodes.sql
@@ -0,0 +1,12 @@
+SELECT
+    oid, tmplname as name
+FROM
+    pg_ts_template tmpl
+WHERE
+{% if scid %}
+    tmpl.tmplnamespace = {{scid}}::OID
+{% elif tid %}
+    tmpl.oid = {{tid}}::OID
+{% endif %}
+
+ORDER BY name
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..2b5a95f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/properties.sql
@@ -0,0 +1,27 @@
+{# Get properties for FTS TEMPLATE #}
+SELECT
+    tmpl.oid,
+    tmpl.tmplname as name,
+    tmpl.tmplinit,
+    tmpl.tmpllexize,
+    description,
+    tmpl.tmplnamespace AS schema
+FROM
+    pg_ts_template tmpl
+    LEFT OUTER JOIN pg_description des
+ON
+    (
+    des.objoid=tmpl.oid
+    AND des.classoid='pg_ts_template'::regclass
+    )
+WHERE
+{% if scid %}
+    tmpl.tmplnamespace = {{scid}}::OID
+{% elif name %}
+    tmpl.tmplname = {{name|qtLiteral}}
+{% endif %}
+{% if tid %}
+    AND tmpl.oid = {{tid}}::OID
+{% endif %}
+ORDER BY
+    tmpl.tmplname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/schema.sql
new file mode 100644
index 0000000..bf7ddb3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/schema.sql
@@ -0,0 +1,19 @@
+{# SCHEMA name FETCH statement #}
+{% if data.schema %}
+SELECT
+    nspname
+FROM
+    pg_namespace
+WHERE
+    oid = {{data.schema}}::OID
+
+{% elif data.id %}
+SELECT
+    nspname
+FROM
+    pg_namespace nsp
+    LEFT JOIN pg_ts_template ts
+    ON ts.tmplnamespace = nsp.oid
+WHERE
+    ts.oid = {{data.id}}::OID
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/sql.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/sql.sql
new file mode 100644
index 0000000..6661078
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/sql.sql
@@ -0,0 +1,41 @@
+{# Reverse engineered sql for FTS TEMPLATE #}
+SELECT
+    array_to_string(array_agg(sql), E'\n\n') as sql
+FROM
+    (
+    SELECT
+        E'-- Text Search Template: ' || nspname || E'.' || tmpl.tmplname ||
+        E'\n\n-- DROP TEXT SEARCH TEMPLATE ' || nspname || E'.' || tmpl.tmplname ||
+        E'\n\nCREATE TEXT SEARCH TEMPLATE ' || nspname || E'.' ||  tmpl.tmplname || E' (\n' ||
+        CASE
+            WHEN tmpl.tmplinit != '-'::regclass THEN E'    INIT = ' || tmpl.tmplinit || E',\n'
+            ELSE '' END ||
+        E'    LEXIZE = ' || tmpl.tmpllexize || E'\n);' ||
+        CASE
+            WHEN a.description IS NOT NULL THEN
+                E'\n\nCOMMENT ON TEXT SEARCH TEMPLATE ' || nspname || E'.' || tmpl.tmplname ||
+                E' IS ' || pg_catalog.quote_literal(description) || E';'
+            ELSE ''  END as sql
+FROM
+    pg_ts_template tmpl
+    LEFT JOIN (
+        SELECT
+            des.description as description,
+            des.objoid as descoid
+        FROM
+            pg_description des
+        WHERE
+            des.objoid={{tid}}::OID AND des.classoid='pg_ts_template'::regclass
+    ) a ON (a.descoid = tmpl.oid)
+    LEFT JOIN (
+        SELECT
+            nspname,
+            nsp.oid as noid
+        FROM
+            pg_namespace nsp
+        WHERE
+            oid = {{scid}}::OID
+    ) b ON (b.noid = tmpl.tmplnamespace)
+WHERE
+    tmpl.oid={{tid}}::OID
+) as c;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..6209805
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/templates/fts_template/sql/9.1_plus/update.sql
@@ -0,0 +1,22 @@
+{# UPDATE statement for TEXT SEARCH TEMPLATE #}
+{% if data %}
+{% if data.name and data.name != o_data.name %}
+ALTER TEXT SEARCH TEMPLATE {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(o_data.name)}}
+    RENAME TO {{data.name}};
+{% endif %}
+
+{#in case of rename, use new fts template name #}
+{% if data.name and data.name != o_data.name %}
+{% set name = data.name %}
+{% else %}
+{% set name = o_data.name %}
+{% endif %}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER TEXT SEARCH TEMPLATE {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
+    SET SCHEMA {{data.schema}};
+{% endif %}
+{% if data.description != o_data.description %}
+COMMENT ON TEXT SEARCH TEMPLATE {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(name)}}
+    IS {{ data.description|qtLiteral }};
+{% endif %}
+{% endif %}
\ No newline at end of file
-- 
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