_binary From 637d3c1856cdcdf69115b3ae2584351e40390272 Mon Sep 17 00:00:00 2001
From: SalilShenoy <salilshenoy@gmail.com>
Date: Tue, 5 Dec 2017 10:30:29 -0800
Subject: [PATCH] Adding Named Entity tab to Page Options with
 Add/View/Edit/Delete functions

---
 src/controllers/components/CrawlComponent.php | 104 +++++++++++++++++--
 src/css/search.css                            |  19 ++++
 src/locale/en_US/configure.ini                |  16 +++
 src/models/EntityModel.php                    | 137 ++++++++++++++++++++++++++
 src/views/elements/EditentitiesElement.php    | 117 ++++++++++++++++++++++
 src/views/elements/PageoptionsElement.php     | 137 ++++++++++++++++++++++++--
 6 files changed, 516 insertions(+), 14 deletions(-)
 create mode 100644 src/models/EntityModel.php
 create mode 100644 src/views/elements/EditentitiesElement.php

diff --git a/src/controllers/components/CrawlComponent.php b/src/controllers/components/CrawlComponent.php
index 698b7ed6..177fa6cf 100644
--- a/src/controllers/components/CrawlComponent.php
+++ b/src/controllers/components/CrawlComponent.php
@@ -1319,7 +1319,8 @@ class CrawlComponent extends Component implements CrawlConstants
         } else {
             $data['page_rules'] = "";
         }
-        $allowed_options = ['crawl_time', 'search_time', 'test_options'];
+        $allowed_options = ['crawl_time', 'search_time', 'named_entity',
+            'test_options'];
         if (isset($_REQUEST['option_type']) &&
             in_array($_REQUEST['option_type'], $allowed_options)) {
             $data['option_type'] = $_REQUEST['option_type'];
@@ -1329,25 +1330,35 @@ class CrawlComponent extends Component implements CrawlConstants
         if ($data['option_type'] == 'crawl_time') {
             $data['crawl_time_active'] = "active";
             $data['search_time_active'] = "";
+            $data['named_entity_active'] = "";
             $data['test_options_active'] = "";
             $data['SCRIPT'] .= "\nswitchTab('crawltimetab',".
-                "'searchtimetab', 'testoptionstab')\n";
+                "'searchtimetab', 'namedentitytab', 'testoptionstab')\n";
         } else if ($data['option_type'] == 'search_time') {
             $data['search_time_active'] = "active";
             $data['crawl_time_active'] = "";
+            $data['named_entity_active'] = "";
             $data['test_options_active'] = "";
             $data['SCRIPT'] .= "\nswitchTab('searchtimetab',".
-                "'crawltimetab', 'testoptionstab')\n";
+                "'crawltimetab', 'namedentitytab', 'testoptionstab')\n";
+        } else if ($data['option_type'] == 'named_entity') {
+            $data['search_time_active'] = "";
+            $data['crawl_time_active'] = "";
+            $data['named_entity_active'] = "active";
+            $data['test_options_active'] = "";
+            $data['SCRIPT'] .= "\nswitchTab('namedentitytab'," .
+                "'crawltimetab', 'searchtimetab', 'testoptionstab');\n";
         } else {
             $data['search_time_active'] = "";
             $data['crawl_time_active'] = "";
+            $data['named_entity_active'] = "";
             $data['test_options_active'] = "active";
             $data['SCRIPT'] .= "\nswitchTab('testoptionstab',".
-                "'crawltimetab', 'searchtimetab');\n";
+                "'crawltimetab', 'searchtimetab', 'namedentitytab');\n";
         }
-
         $crawl_model->setSeedInfo($seed_info);
-        if ($change == true && $data['option_type'] != 'test_options') {
+        if ($change == true && $data['option_type'] != 'test_options' &&
+            $data['option_type'] != 'named_entity') {
             return $parent->redirectWithMessage(
                 tl('crawl_component_page_options_updated'),
                 ["option_type"]);
@@ -1550,6 +1561,13 @@ class CrawlComponent extends Component implements CrawlConstants
             $data['PROCESS_TIMES']['TOTAL'] = L\changeInMicrotime($start_time);
             $data['PROCESS_TIMES'] = array_merge($data['PROCESS_TIMES'],
                 $word_lists['TIMES']);
+        } else if ($data['option_type'] == 'named_entity') {
+            $data= array_merge($data, CrawlComponent::addNamedEntites($data));
+        }
+        $languages = $parent->model("locale")->getLocaleList();
+        foreach ($languages as $language) {
+            $data['LOCALES'][$language['LOCALE_TAG']] =
+                $language['LOCALE_NAME'];
         }
         return $data;
     }
@@ -2091,4 +2109,78 @@ class CrawlComponent extends Component implements CrawlConstants
             "switchSourceType()";
         return $data;
     }
+
+    public function addNamedEntites($data)
+    {
+        $parent = $this->parent;
+        $entity_model = $parent->model("entity");
+        $locale_model = $parent->model("locale");
+        if (!isset($_REQUEST['arg'])) {
+
+            if (!empty($_REQUEST['ENTITY_NAME']) &&
+                !empty($_REQUEST['LOCALE_TAG'])) {
+                $entity = $_REQUEST['ENTITY_NAME'];
+                $locale = $_REQUEST['LOCALE_TAG'];
+                $entity_model->addentity($entity, $locale);
+            } else {
+                $file_name = $_FILES['ENTITY_FILE']['tmp_name'];
+                $locale = $_REQUEST['LOCALE_TAG'];
+                $entity_model->addEntities($file_name, $locale);
+            }
+        } else {
+            switch ($_REQUEST['arg']) {
+                case 'viewentities':
+                    if (!isset($_REQUEST['selectlocale'])) {
+                        break;
+                    }
+                    $selectlocale = $_REQUEST['selectlocale'];
+                    $paging = false;
+                    $data["leftorright"] =
+                        (L\getLocaleDirection() == 'ltr') ? "right": "left";
+                    $data['PREVIOUS_ACTIVITY'] = "pageOptions";
+                    if (isset($_REQUEST['previous_activity']) &&
+                        in_array($_REQUEST['previous_activity'], [
+                        "security", "searchSources"])) {
+                            $data['PREVIOUS_ACTIVITY'] =
+                                $_REQUEST['previous_activity'];
+                    }
+                    $data['ELEMENT'] = "editentities";
+                    $data['CURRENT_LOCALE_NAME'] =
+                        $locale_model->getLocaleName($selectlocale);
+                    $data['CURRENT_LOCALE_TAG'] = $selectlocale;
+                    $data['ENTITIES'] = $entity_model->viewall($selectlocale);
+                    $data['NUM_ENTITIES_SHOW'] = 100;
+                    $data['TOTAL_ENTITIES'] = count($data['ENTITIES']);
+                    $data['LIMIT'] = (isset($_REQUEST['limit'])) ?
+                        min($parent->clean($_REQUEST['limit'], 'int'),
+                        $data['TOTAL_ENTITIES']) : 0;
+                    $data['ENTITIES'] = array_slice($data['ENTITIES'],
+                        $data['LIMIT'], $data['NUM_ENTITIES_SHOW']);
+                    break;
+
+                case 'editentities':
+                    if (isset($_REQUEST['OLD_VALUE']) &&
+                        isset($_REQUEST['EN']) &&
+                        isset($_REQUEST['selectlocale'])) {
+                        $old_values = $_REQUEST['OLD_VALUE'];
+                        $entities = $_REQUEST['EN'];
+                        $locale = $_REQUEST['selectlocale'];
+                        $entity_model->bulkEditEntities($old_values, $entities,
+                            $locale);
+                    }
+                    break;
+
+                case 'deleteentities':
+                    if (isset($_REQUEST['value']) &&
+                        isset($_REQUEST['selectlocale'])) {
+                        $entity = $_REQUEST['value'];
+                        $locale = $_REQUEST['selectlocale'];
+                        $entity_model->deleteentity($entity, $locale);
+                    }
+                    break;
+            }
+        }
+
+        return $data;
+    }
 }
diff --git a/src/css/search.css b/src/css/search.css
index 10efc71d..c50fa4b1 100755
--- a/src/css/search.css
+++ b/src/css/search.css
@@ -3129,3 +3129,22 @@ p.start-ad
     min-height: 0.5in;
     width: 3in;
 }
+
+.entity-table
+{
+    font-size:13pt;
+}
+.html-ltr .entity-table,
+.html-ltr .entity-table td,
+.html-ltr .entity-table th
+{
+    border: 1px ridge black;
+    margin-left: 0.2in;
+}
+.html-rtl .entity-table,
+.html-rtl .entity-table td,
+.html-rtl .entity-table th
+{
+    border: 1px ridge black;
+    margin-right: 0.2in;
+}
diff --git a/src/locale/en_US/configure.ini b/src/locale/en_US/configure.ini
index cf05464e..e6aca5c3 100644
--- a/src/locale/en_US/configure.ini
+++ b/src/locale/en_US/configure.ini
@@ -752,6 +752,16 @@ editclassifier_element_load = "Load"
 editclassifier_element_finalize = "Finalize"
 editclassifier_element_no_documents = "No Documents"
 ;
+; EditentitiesElement.php
+editenties_element_back_to_page_options = "Back"
+editentities_element_edit_entites = "Edit Entities : %s"
+editentities_element_no_entities = "No Entities added for this Locale!"
+editentities_element_entity_name = "Entity Name"
+editentities_element_action = "Action"
+editentities_confirm_delete = "Are you sure you want to delete?"
+editentities_element_delete = "Delete"
+editentities_element_save = "Save"
+;
 ; EditlocalesElement.php
 editlocales_element_back_to_manage = "Back"
 editlocales_element_edit_locale = "Edit Locale: %s"
@@ -1146,6 +1156,7 @@ moreoptions_element_tools = "Tools"
 ; PageoptionsElement.php
 pageoptions_element_crawl_time = "Crawl Time"
 pageoptions_element_search_time = "Search Time"
+pageoptions_element_named_entity_add = "Named Entity"
 pageoptions_element_test_options = "Test Options"
 pageoptions_element_load_options = "Get Page Options From:"
 pageoptions_element_page_range = "Byte Range to Download (0 - Value):"
@@ -1200,6 +1211,11 @@ pageoptions_element_qa_time = "Question Answer Extraction:"
 pageoptions_element_total_time = "Total Time:"
 pageoptions_element_time_seconds = "%s seconds"
 pageoptions_element_plugin_back = "Back"
+pageoptions_element_named_entity = "Add Named Entity"
+pageoptions_element_page_entity = "Entity"
+pageoptions_element_page_file = "File"
+pageoptions_element_localename = "Name"
+pageoptions_element_tag = "Tag"
 ;
 ; QuerystatsElement.php
 querystats_element_back_to_manage = "Back"
diff --git a/src/models/EntityModel.php b/src/models/EntityModel.php
new file mode 100644
index 00000000..9d9e2ccc
--- /dev/null
+++ b/src/models/EntityModel.php
@@ -0,0 +1,137 @@
+<?php
+/**
+ * SeekQuarry/Yioop --
+ * Open Source Pure PHP Search Engine, Crawler, and Indexer
+ *
+ * Copyright (C) 2009 - 2017  Chris Pollett chris@pollett.org
+ *
+ * LICENSE:
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * END LICENSE
+ *
+ * @author Chris Pollett chris@pollett.org
+ * @license http://www.gnu.org/licenses/ GPL3
+ * @link http://www.seekquarry.com/
+ * @copyright 2009 - 2017
+ * @filesource
+ */
+namespace seekquarry\yioop\models;
+
+use seekquarry\yioop\configs as C;
+use seekquarry\yioop\library as L;
+
+/**
+ * This class is used to handle
+ * database statements related adding entities
+ *
+ * @author Salil Shenoy
+ */
+class EntityModel extends Model
+{
+    /**
+     * Add an entity to the database using provided string
+     *
+     * @param string $entity the enityt to be added
+     * @param string $locale the locale to which the entity belongs
+     */
+    public function addEntity($entity, $locale)
+    {
+        $db = $this->db;
+        $sql = "INSERT INTO LEXICON(TERM, PART_OF_SPEECH, LOCALE) VALUES
+         (?,?,?)";
+        $result = $db->execute($sql, [$entity, "EN", $locale]);
+    }
+    /**
+     * Bulk upload entities to the database from a file for 
+     * the given locale
+     *
+     * @param file $file_name containing entities uploaded to Yioop
+     * @param string $locale locale to which the entities belong
+     */
+    public function addEntities($file_name, $locale)
+    {
+        $db = $this->db;
+        $lines = file($file_name);
+        $sql = "INSERT INTO LEXICON(TERM, PART_OF_SPEECH, LOCALE) VALUES
+         (?,?,?)";
+        foreach ($lines as $line) {
+            $result = $db->execute($sql, [$line, "EN", $locale]);     
+        }
+    }
+    /**
+     * Bulk edit entities already present in the database from the View 
+     * Entities page the given locale.
+     *
+     * @param array $orignal_values of entities present in Yioop
+     * @param array $updated_values captured when the user clicks Save button
+     * @param string $locale locale to which the entities belong
+     */
+    public function bulkEditEntities($orignal_values, $updated_values, $locale)
+    {
+        $db = $this->db;
+        $sql = "UPDATE LEXICON SET TERM = ? WHERE TERM = ? AND LOCALE= ?";
+        /* The following code gets only the entites which are updated 
+         *  For example,
+         *  $orignal_values = [0 => 'New Jersey', 1 => 'California']
+         *  $update_values = [ 0 => 'New York', 1 => 'California']
+         *  $old_values = [0 => 'New Jersey']
+         *  $new_values = [0 => 'New York']
+         */
+        $old_values = array_diff($orignal_values, $updated_values);
+        $new_values = array_diff($updated_values, $orignal_values);
+        $count = count($new_values);
+        if ($count > 0) {
+            foreach ($new_values as $key => $value) {
+                $new_value = $new_values[$key];
+                $old_value = $old_values[$key];
+                $db->execute($sql, [$new_value, $old_value, $locale]);
+                $count--;
+                if ($count == 0)
+                    break;
+            }
+        }
+    }
+    /**
+     * Shows all the entities foe that locale, you can then edit or delete
+     * entites from this page
+     *
+     * @param string $locale locale to which the entities belong
+     */
+    public function viewall($locale) 
+    {
+        $entities = [];
+        $db = $this->db;
+        $sql = "SELECT term FROM LEXICON WHERE LOCALE = ? AND
+            PART_OF_SPEECH = 'EN'";
+        $result = $db->execute($sql, [$locale]);
+        while ($row = $db->fetchArray($result)){
+            $entities[] = $row['TERM'];
+        }
+        return $entities;
+    }
+    /**
+     * Delete the entity for the locale
+     *
+     * @param string $entity entity to delete
+     * @param string $locale locale to which the entities belong
+     */
+    public function deleteentity($entity, $locale)
+    {
+        $db = $this->db;
+        $sql = "DELETE FROM LEXICON WHERE TERM = ? AND LOCALE = ?";
+        $db->execute($sql, [$entity, $locale]);
+    }
+}
diff --git a/src/views/elements/EditentitiesElement.php b/src/views/elements/EditentitiesElement.php
new file mode 100644
index 00000000..316185b9
--- /dev/null
+++ b/src/views/elements/EditentitiesElement.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * SeekQuarry/Yioop --
+ * Open Source Pure PHP Search Engine, Crawler, and Indexer
+ *
+ * Copyright (C) 2009 - 2017  Chris Pollett chris@pollett.org
+ *
+ * LICENSE:
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * END LICENSE
+ *
+ * @author Chris Pollett chris@pollett.org
+ * @license http://www.gnu.org/licenses/ GPL3
+ * @link http://www.seekquarry.com/
+ * @copyright 2009 - 2017
+ * @filesource
+ */
+namespace seekquarry\yioop\views\elements;
+
+use seekquarry\yioop as B;
+use seekquarry\yioop\configs as C;
+
+/**
+ * Element responsible for displaying the form where users can input string
+ * translations for a given locale
+ *
+ * @author Salil Shenoy
+ */
+class EditentitiesElement extends Element
+{
+    /**
+     * Draws a form with strings which represent entities for the given 
+     * locale
+     *
+     * @param array $data contains entity strings
+     */
+    public function render($data)
+    {
+        $admin_url = htmlentities(B\controllerUrl('admin', true));
+        $base_url = $admin_url ."a=pageOptions&amp;".
+            C\CSRF_TOKEN . "=" . $data[C\CSRF_TOKEN] . "&amp;selectlocale=" .
+            $data['CURRENT_LOCALE_TAG'];
+        ?>
+        <div class="current-activity">
+        <div class="<?=$data['leftorright'] ?>">
+        <a href="<?=$admin_url . 'a='.$data['PREVIOUS_ACTIVITY'].
+            "&amp;".C\CSRF_TOKEN.'='.$data[C\CSRF_TOKEN] ?>"
+        ><?= tl('editenties_element_back_to_page_options')?></a>
+        </div>
+        <h2><?= tl('editentities_element_edit_entites',
+            $data['CURRENT_LOCALE_NAME']) ?>
+        </h2>
+        <form id="editEntityForm" method='post'>
+        <input type="hidden" name="c" value="admin" />
+        <input type="hidden" name="<?= C\CSRF_TOKEN ?>" value="<?=
+            $data[C\CSRF_TOKEN] ?>" />
+        <input type="hidden" name="a" value="pageOptions" />
+        <input type="hidden" name="arg" value="editentities" />
+        <input type="hidden" name="selectlocale" value="<?=
+            $data['CURRENT_LOCALE_TAG'] ?>" />
+        <input type="hidden" id='option-type' name="option_type" value="<?=
+            $data['option_type'] ?>" />
+        <div class="slight-pad">
+        <?php
+        if ($data['ENTITIES'] == []) {
+            e("<h3 class='red'>". tl('editentities_element_no_entities').
+                "</h3>");
+        }
+        ?>
+        <table class="entity-table">
+        <tr>
+        <th><?= tl('editentities_element_entity_name') ?></th>
+        <th><?= tl('editentities_element_action') ?></th>
+        </tr>
+        <?php
+        $i = 0;
+        $delete_url = $base_url . "&amp;arg=deleteentities&option_type=named_entity";
+        foreach ($data['ENTITIES'] as $entity) {
+            e("<tr><td style='display:none;'><input type='hidden' ".
+             "value='$entity' id=$i name=OLD_VALUE[$i]");
+            e("<tr><td><input type='text' ". "value='$entity'
+                id=$i name=EN[$i]/></td>");
+            $i++;
+            e("<td><a name=$entity href='$base_url"
+                ."&amp;arg=deleteentities&amp;option_type=named_entity&amp;value=$entity"."'");?>
+            onclick='javascript:return confirm("<?php
+            e(tl('editentities_confirm_delete')); ?>");'><?php
+            e(tl("editentities_element_delete")."</a></td></tr>");
+        }
+        ?>
+        </table>
+        <?php
+            $this->view->helper("pagination")->render($base_url,
+                $data['LIMIT'], $data['NUM_ENTITIES_SHOW'],
+                $data['TOTAL_ENTITIES']);
+        ?>
+        <div class="center slight-pad"><button class="button-box"
+            id="save-entity-button" value="save"
+            type="submit"><?=tl('editentities_element_save') ?></button></div>
+        </form>
+        </div>
+    <?php
+    }   
+}
diff --git a/src/views/elements/PageoptionsElement.php b/src/views/elements/PageoptionsElement.php
index 3f031df9..292fd1e7 100644
--- a/src/views/elements/PageoptionsElement.php
+++ b/src/views/elements/PageoptionsElement.php
@@ -31,6 +31,7 @@
 namespace seekquarry\yioop\views\elements;
 
 use seekquarry\yioop\configs as C;
+use seekquarry\yioop as B;
 
 /**
  * This element is used to render the Page Options admin activity
@@ -51,20 +52,30 @@ class PageOptionsElement extends Element
     {
     ?>
         <div class="current-activity">
-        <form id="pageoptionsForm" method="post" action='?'>
+        <form id="pageoptionsForm" enctype="multipart/form-data"
+         method="post" action='?'>
         <ul class='tab-menu-list'>
         <li><a href="javascript:
-                switchTab('crawltimetab', 'searchtimetab', 'testoptionstab');"
+                switchTab('crawltimetab', 'searchtimetab', 'namedentitytab',
+                    'testoptionstab');"
             id='crawltimetabitem'
             class="<?= $data['crawl_time_active'] ?>"><?=
             tl('pageoptions_element_crawl_time')?></a></li>
         <li><a href="javascript:
-                switchTab('searchtimetab', 'crawltimetab', 'testoptionstab');"
+                switchTab('searchtimetab', 'crawltimetab', 'namedentitytab',
+                    'testoptionstab');"
             id='searchtimetabitem'
             class="<?= $data['search_time_active'] ?>"><?=
             tl('pageoptions_element_search_time')?></a></li>
         <li><a href="javascript:
-                switchTab('testoptionstab', 'crawltimetab', 'searchtimetab');"
+                switchTab('namedentitytab', 'crawltimetab', 'searchtimetab',
+                    'testoptionstab');"
+            id='namedentitytabitem'
+            class="<? = $data['named_entity_active'] ?>"><?=
+            tl('pageoptions_element_named_entity_add') ?></a></li>
+        <li><a href="javascript:
+                switchTab('testoptionstab', 'crawltimetab', 'searchtimetab',
+                'namedentitytab');"
             id='testoptionstabitem'
             class="<?= $data['test_options_active'] ?>"><?=
             tl('pageoptions_element_test_options') ?></a></li>
@@ -355,6 +366,70 @@ class PageOptionsElement extends Element
                 value="<?= $data['SERVER_ALPHA'] ?>" /></td></tr>
         </table>
         </div>
+        <div id='namedentitytab'>
+        <h2><?= tl('pageoptions_element_named_entity') ?></h2>
+        <input name='ENTITY_ADD_RADIO' type='radio' id='add-manual'
+            onclick='on();', checked='checked' />
+        <label for='ENTITY_ADD_RADIO'> Add Entity </label>
+        <input name='ENTITY_ADD_RADIO' type='radio' id='upload-file'
+        onclick='off();'/>
+        <label for='ENTITY_ADD_RADIO'> Upload File </label>
+        <table id = 'ADD_ENTITY'>
+        <tr id='hidethis1'>
+        <td><label for='ENTITY_NAME'>
+        <?= tl('pageoptions_element_page_entity') ?>
+        </label></td>
+        <td><input name='ENTITY_NAME' type='text' class='wide-field'
+            value = "" />
+        </td>
+        </tr>
+        <tr id='hidethis2' style="display:none">
+        <td><label for='ENTITY_FILE'>
+        <?= tl('pageoptions_element_page_file') ?>
+        </label></td>
+        <td><input type='file' name='ENTITY_FILE'></td>
+        </tr>
+        <tr>
+        <td><label for='LOCALE_TAG'> Locale: </label></td>
+        <td>
+        <?php
+        $locale_list = [];
+        foreach ($data['LOCALES'] as $locale_tag => $locale_name) {
+            $locale_list[] = $locale_tag;
+        }
+        $this->view->helper("options")->render("LOCALE_TAG",
+            "LOCALE_TAG", array_combine($locale_list, $locale_list),
+            $locale_list);
+        ?>
+        </td>
+        </tr>
+        <tr>
+        <td></td>
+        <td class='center' id = 'save-entity-button'>
+        <button class="button-box" type="submit">Save</button>
+        </td>
+        </tr>
+        </table>
+        <h2> View All Entities </h2>
+        <table class="entity-table">
+        <tr>
+        <th><?= tl('pageoptions_element_localename') ?></th>
+        <th colspan="2"><?= tl('pageoptions_element_tag') ?></th>
+        <?php
+        $context = "";
+        $admin_url = htmlentities(B\controllerUrl('admin', true));
+        $base_url = $admin_url . C\CSRF_TOKEN."=" . $data[C\CSRF_TOKEN].
+            "&amp;a=pageOptions&amp;option_type=named_entity";
+        foreach ($data['LOCALES'] as $locale_tag => $locale_name) {
+            e("<tr><td><a href='$base_url".
+                "&amp;arg=viewentities&amp;selectlocale=".$locale_tag.
+                "' >".$locale_name."</a></td>");
+            e("<td>$locale_tag</td></tr>");
+        }
+        ?>
+        </tr>
+        </table>
+        </div>
         <div id='testoptionstab'>
         <h2><?= tl('pageoptions_element_test_page') ?>
         <?= $this->view->helper("helpbutton")->render(
@@ -459,11 +534,15 @@ class PageOptionsElement extends Element
         }
         ?>
         <script type="text/javascript">
-        function switchTab(newtab, oldtab, oldtab2)
+        function switchTab(newtab, oldtab, oldtab2, oldtab3)
         {
+            var btn1 = document.getElementById('save-entity-button');
+            var btn2 = document.getElementById('page-button');
+
             setDisplay(newtab, true);
             setDisplay(oldtab, false);
             setDisplay(oldtab2, false);
+            setDisplay(oldtab3, false);
             ntab = elt(newtab + "item");
             if (ntab) {
                 ntab.className = 'active';
@@ -476,11 +555,37 @@ class PageOptionsElement extends Element
             if (otab2) {
                 otab2.className = '';
             }
+            otab3 = elt(oldtab3 + "item");
+            if (otab3) {
+                otab3.className = '';
+            }
             ctype = elt('option-type');
             if (ctype) {
-                ctype.value = (newtab == 'crawltimetab')
-                    ? 'crawl_time' : ((newtab == 'searchtimetab') ?
-                    'search_time' : 'test_options' );
+                switch (newtab) {
+                    case 'crawltimetab':
+                        ctype.value = 'crawl_time';
+                        btn1.style.display = "none"
+                        btn2.style.display = "block";
+                        break;
+
+                    case 'searchtimetab':
+                        ctype.value = 'search_time';
+                        btn1.style.display = "none"
+                        btn2.style.display = "block";
+                        break;
+
+                    case 'namedentitytab':
+                        ctype.value = 'named_entity';
+                        btn1.style.display = "block"
+                        btn2.style.display = "none";
+                        break;
+
+                    default:
+                        ctype.value = 'test_options';
+                        btn1.style.display = "none"
+                        btn2.style.display = "block";
+                }
+
                 if (ctype.value == 'test_options') {
                     elt('page-button').innerHTML =
                         '<?= tl('pageoptions_element_run_tests') ?>';
@@ -492,6 +597,22 @@ class PageOptionsElement extends Element
                 }
             }
         }
+
+        function off() {
+            var e = document.getElementById("hidethis1");
+            if (e)
+                e.style.display = 'none';
+            e = document.getElementById("hidethis2");
+            e.style.display = 'table-row';
+        }
+
+        function on() {
+            var e = document.getElementById("hidethis1");
+            if (e)
+                e.style.display = 'table-row';
+            e = document.getElementById("hidethis2");
+            e.style.display = 'none';
+        }
         </script>
     <?php
     }
-- 
2.15.0.windows.1

