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.



Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

On Tue, Mar 1, 2016 at 4:56 PM, Sanket Mehta <sanket.me...@enterprisedb.com>
wrote:

> Hi Dave,
>
> For your first comment,
>
>  "When creating a new template, errors in the dialogue are not cleared -
> e.g. the "Name must not be empty" doesn't clear, and save is not enabled,
> even when a name is entered."
>
> This issue should not be handled only at FTS template level but the top
> level as it occurs in all nodes.
> I have created a bug node in kanban-chi regarding the same.
> We will take this issue as per priority and send the patch for the same
> later.
>
>
>
> Regards,
> Sanket Mehta
> Sr Software engineer
> Enterprisedb
>
> On Tue, Mar 1, 2016 at 4:34 PM, Sanket Mehta <
> sanket.me...@enterprisedb.com> wrote:
>
>> Hi Dave,
>>
>> All comments are resolved.
>> PFA the revised patch.
>>
>> Regards,
>> Sanket Mehta
>> Sr Software engineer
>> Enterprisedb
>>
>> On Thu, Feb 25, 2016 at 7:37 PM, Dave Page <dp...@pgadmin.org> wrote:
>>
>>> Hi
>>>
>>> On Tue, Feb 23, 2016 at 1:44 PM, Sanket Mehta <
>>> sanket.me...@enterprisedb.com> wrote:
>>>
>>>> Hi,
>>>>
>>>> PFA the revised patch for fts template node which includes dependency
>>>> and dependent route handling code.
>>>> Please do review it and if found proper please do commit it.
>>>>
>>>
>>> - When creating a new template, errors in the dialogue are not cleared -
>>> e.g. the "Name must not be empty" doesn't clear, and save is not enabled,
>>> even when a name is entered.
>>>
>>> - "Fts Templates" should be "FTS Templates" on the treeview.
>>>
>>> - The "Init" field should be labelled "Init Function"
>>>
>>> - The "Lexize" field should be labelled "Lexize Function"
>>>
>>> - Selecting a schema should be optional, with the default being the
>>> current schema.
>>>
>>> - 4 character indents are not consistently used in SQL templates
>>>
>>> Thanks.
>>>
>>> --
>>> 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..c266c85
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/fts_templates/__init__.py
@@ -0,0 +1,700 @@
+##########################################################################
+#
+# 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
+        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, rg_id)
+      - This function will show the properties of the selected FTS Template node
+
+    * create(gid, sid, did, rg_id)
+      - This function will create the new FTS Template object
+
+    * update(gid, sid, did, rg_id)
+      - This function will update the data for the selected FTS Template node
+
+    * delete(self, gid, sid, rg_id):
+      - This function will drop the FTS Template object
+
+    * msql(gid, sid, did, rg_id)
+      - This function is used to return modified SQL for the selected FTS Template node
+
+    * get_sql(data, rg_id)
+      - This function will generate sql from model data
+
+    * sql(gid, sid, did, rg_id):
+      - 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.
+
+    * get_functions():
+      - This function will fetch associated functions list depending on selected source
+        and target types while creating a new FTS Template node.
+
+    * dependents(gid, sid, did, lid):
+      - This function get the dependents and return ajax response for the Fts Tempalte node.
+
+    * dependencies(self, gid, sid, did, lid):
+      - 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, 'properties.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['tmplname'],
+                    icon="icon-fts_template"
+                ))
+
+        return make_json_response(
+            data=res,
+            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',
+            'tmplname'
+        ]
+
+        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['tmplname']
+            )
+            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['tmplname'],
+                    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 \
+                                'tmplname' 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 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..e46ffc3
--- /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: ['tmplname', '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',
+      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: {
+          tmplname: 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: 'tmplname', label: '{{ _('Name') }}', cell: 'string', group: 'Definition',
+          type: 'text', cellHeaderClasses: 'width_percent_50'
+        },{
+          id: 'oid', label:'{{ _('Oid') }}', cell: 'string', group: 'Definition',
+          editable: false, type: 'text', disabled: true
+        },{
+          id: 'description', label:'{{ _('Comment') }}', cell: 'string', group: 'Definition',
+          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'
+        },{
+          id: 'schema', label: '{{ _('Schema')}}', cell: 'string', group: 'Definition',
+          type: 'text', mode: ['create','edit'], node: 'schema',
+          control: 'node-list-by-id'
+        }],
+
+        /*
+         * 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('tmplname');
+          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..784f737
--- /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.tmplname and data.tmpllexize %}
+CREATE TEXT SEARCH TEMPLATE {{ conn|qtIdent(data.schema, data.tmplname) }} (
+{% 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.tmplname) }}
+      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/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..8b2a70c
--- /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,
+    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..103c7bf
--- /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\n  CREATE TEXT SEARCH TEMPLATE ' || nspname || E'.' ||  tmpl.tmplname || E'(\n' ||
+        CASE
+            WHEN tmpl.tmplinit != '-'::regclass THEN E'\tINIT = ' || tmpl.tmplinit || E',\n'
+            ELSE '' END ||
+        E'\tLEXIZE = ' || 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..935c649
--- /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.tmplname and data.tmplname != o_data.tmplname %}
+ALTER TEXT SEARCH TEMPLATE {{conn|qtIdent(o_data.schema)}}.{{conn|qtIdent(o_data.tmplname)}}
+    RENAME TO {{data.tmplname}};
+{% endif %}
+
+{#in case of rename, use new fts template name #}
+{% if data.tmplname and data.tmplname != o_data.tmplname %}
+{% set name = data.tmplname %}
+{% else %}
+{% set name = o_data.tmplname %}
+{% 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 and 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