First pass at a command-line configuration tool, a=chris

Chris Pollett [2012-09-15 02:Sep:th]
First pass at a command-line configuration tool, a=chris
Filename
configs/config.php
configs/configure_tool.php
controllers/admin_controller.php
lib/utility.php
locale/en-US/pages/bot.thtml
locale/zh-CN/configure.ini
locale/zh-CN/statistics.txt
models/source_model.php
diff --git a/configs/config.php b/configs/config.php
index 2820c1ce0..72e248f73 100644
--- a/configs/config.php
+++ b/configs/config.php
@@ -63,7 +63,7 @@ if(MAINTENANCE_MODE && $_SERVER["SERVER_ADDR"] != $_SERVER["REMOTE_ADDR"]) {
 if(!defined('WORK_DIRECTORY')) {
 /*+++ The next block of code is machine edited, change at
 your own risk, please use configure web page instead +++*/
-define('WORK_DIRECTORY', '');
+define('WORK_DIRECTORY', '/Applications/XAMPP/xamppfiles/htdocs/crawls');
 /*++++++*/
 }

diff --git a/configs/configure_tool.php b/configs/configure_tool.php
new file mode 100644
index 000000000..5a7bded8f
--- /dev/null
+++ b/configs/configure_tool.php
@@ -0,0 +1,626 @@
+<?php
+/**
+ *  SeekQuarry/Yioop --
+ *  Open Source Pure PHP Search Engine, Crawler, and Indexer
+ *
+ *  Copyright (C) 2009 - 2012  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
+ *
+ * Used to create and manipulate a profile and work directory from the
+ * command-line for Yioop.
+ *
+ * @author Chris Pollett chris@pollett.org
+ * @package seek_quarry
+ * @subpackage configs
+ * @license http://www.gnu.org/licenses/ GPL3
+ * @link http://www.seekquarry.com/
+ * @copyright 2009 - 2012
+ * @filesource
+ */
+
+if(php_sapi_name() != 'cli') {echo "BAD REQUEST"; exit();}
+
+/** Calculate base directory of script @ignore*/
+define("BASE_DIR", substr(
+    dirname(realpath($_SERVER['PHP_SELF'])), 0,
+    -strlen("/configs")));
+
+/** Load in global configuration settings */
+require_once BASE_DIR.'/configs/config.php';
+/** Loads common constants for web crawling*/
+require_once BASE_DIR."/lib/crawl_constants.php";
+
+/** Loads common constants for web crawling*/
+require_once BASE_DIR."/lib/locale_functions.php";
+
+/** Loads common utility functions*/
+require_once BASE_DIR."/lib/utility.php";
+
+mb_internal_encoding("UTF-8");
+mb_regex_encoding("UTF-8");
+
+
+$locale_tag = guessLocale();
+$locale = NULL;
+setLocaleObject($locale_tag);
+/**
+ * This tool is essentially a set of views for the
+ * logic that is done in admin_controller.php
+ */
+require_once(BASE_DIR."/controllers/admin_controller.php");
+
+
+/**
+ * Provides a command-line interface way to configure a Yioop Instance.
+ * Unlike the web interface this interface is English-only.
+ */
+class ConfigureTool
+{
+    /**
+     * Used to hold an AdminController object used to manipulate the
+     * Yioop configuration
+     * @var object
+     */
+    var $admin;
+
+    /**
+     * Holds the main menu data for the configuration tool
+     * @var array
+     */
+    var $menu = array("workDirectory" => "Create/Set Work Directory",
+        "rootPassword" => "Change root password",
+        "defaultLocale"=> "Set Default Locale",
+        "debugDisplay"=> "Debug Display Set-up",
+        "searchAccess"=> "Search Access Set-up",
+        "searchPageElementLinks" => "Search Page Elements and Links",
+        "nameServer" => "Name Server Set-up",
+        "robotSetUp"=> "Crawl Robot Set-up",
+        "quit" => "Exit program"
+    );
+
+    /**
+     * To change configuration parameters of Yioop, this program
+     * invokes AdminController methods. These methods expect, data
+     * passed to them in super globals set up as a result of an HTTP
+     * request. This program fakes the settings of these variables.
+     * To keep things simple this constructor initializes each of the
+     * relevant super globals to be empty arrays.
+     */
+    function __construct()
+    {
+        global $INDEXING_PLUGINS;
+
+        $_REQUEST = array();
+        $_POST = array();
+        $_GET = array();
+        $_SERVER = array();
+        $_SESSION = array();
+        $this->admin = new AdminController($INDEXING_PLUGINS);
+    }
+
+    /**
+     * This is the main loop where options of what the user can configure
+     * are presented, a choice is requested, and so on...
+     */
+    function loop()
+    {
+        $done = false;
+        $activities = array_keys($this->menu);
+        $activities[] = "configureMenu";
+        $state = "configureMenu";
+        while($state != "quit") {
+            if(in_array($state, $activities) ) {
+                $state = $this->$state();
+            }
+        }
+    }
+
+    /**
+     * This is used to draw the main configuration menu and ask for a
+     * user selection
+     */
+    function configureMenu()
+    {
+        $this->banner();
+        $data = $this->admin->configure();
+        e("Checking Yioop configuration...".
+            "\n===============================\n");
+        $check_status = str_replace("<br />", "\n", $data["SYSTEM_CHECK"]);
+        e($check_status."\n===============================\n");
+
+        $items = array("workDirectory" => "Create/Set Work Directory");
+        if($data["PROFILE"]) {
+            $items = $this->menu;
+        }
+        return $this->drawChooseItems($items, "configureMenu");
+
+    }
+
+    /**
+     * Used to create/change the location of this Yioop instances work
+     * directory
+     */
+    function workDirectory()
+    {
+        $this->banner();
+        $data = $this->admin->configure();
+        e("CURRENT WORK DIRECTORY: ".$data["WORK_DIRECTORY"]."\n\n");
+        e("Enter a new value:\n");
+
+        $this->prepareGlobals($data);
+        $_REQUEST["WORK_DIRECTORY"] = readLine();
+        $_REQUEST["arg"] = "directory";
+
+        $next_menu = $this->confirmChange("configure", "workDirectory");
+
+        return $next_menu;
+    }
+
+    /**
+     * Used to change the password of the root account of this Yioop Instance
+     */
+    function rootPassword()
+    {
+        $this->banner();
+        $data = $this->admin->configure();
+        if($data["PROFILE"] != 1) {
+            $_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
+            return "configureMenu";
+        }
+        e("Enter old password:");
+        $_REQUEST["oldpassword"] = readPassword();
+        e("Enter new password:");
+        $_REQUEST["newpassword"] = readPassword();
+        e("Re-Enter new password:");
+        $_REQUEST["retypepassword"] = readPassword();
+        $_SESSION['USER_ID'] = 1;
+        $_REQUEST['arg'] = "changepassword";
+        $next_menu = $this->confirmChange("manageAccount", "rootPassword");
+        return $next_menu;
+    }
+
+    /**
+     * Changes the default locale (language) used by Yioop when it cannot
+     * determine that information from the users browswer
+     */
+    function defaultLocale()
+    {
+        $this->banner();
+        $data = $this->admin->configure();
+        if($data["PROFILE"] != 1) {
+            $_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
+            return "configureMenu";
+        }
+        e("CURRENT LANGUAGE: ".$data["LANGUAGES"][
+            $data["DEFAULT_LOCALE"]]."\n\n");
+        $_SESSION = array();
+        $items = $data["LANGUAGES"];
+        $items["configureMenu"] = "Return to Main Menu";
+
+        do {
+            $choice = $this->drawChooseItems($items, "defaultLocale");
+        } while( $choice == "defaultLocale");
+
+        $this->prepareGlobals($data);
+        if($choice == "configureMenu") {
+            $_REQUEST = array();
+            $_SERVER = array();
+            return "configureMenu";
+        }
+
+        $_REQUEST["DEFAULT_LOCALE"] = $choice;
+
+        return "defaultLocale";
+    }
+
+    /**
+     * Used to configure debugging information for this Yioop instance.
+     * i.e., whether PHP notices, warnings, errors, should be displayed,
+     * whether query statistics and info should be displayed, and whether
+     * unit tests should be viewable from the web
+     */
+    function debugDisplay()
+    {
+        $this->banner();
+        $data = $this->admin->configure();
+        if($data["PROFILE"] != 1) {
+            $_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
+            return "configureMenu";
+        }
+        e("CURRENT DEBUG SETTINGS\n======================\n");
+
+        $dlevel = $data["DEBUG_LEVEL"];
+        $setting = ($dlevel & ERROR_INFO) ? "On" : "Off";
+        e("Error Info: [$setting]\n");
+        $setting = ($dlevel & QUERY_INFO) ? "On" : "Off";
+        e("Query Info: [$setting]\n");
+        $setting = ($dlevel & TEST_INFO) ? "On" : "Off";
+        e("Test Info: [$setting]\n");
+
+        $items = array("ERROR_INFO" => "Toggle Error Info",
+            "QUERY_INFO" => "Toggle Query Info",
+            "TEST_INFO" => "Toggle Test Info",
+            "configureMenu" => "Return to Main Menu");
+
+        do {
+            $choice = $this->drawChooseItems($items, "debugDisplay");
+        } while( $choice == "debugDisplay");
+        $this->prepareGlobals($data);
+
+        if($choice == "configureMenu") {
+            $_REQUEST = array();
+            $_SERVER = array();
+            return "configureMenu";
+        }
+        $flag = constant($choice);
+        $dlevel = ($dlevel & $flag) ? $dlevel - $flag : $dlevel + $flag;
+        if($dlevel & ERROR_INFO) {$_REQUEST["ERROR_INFO"] = true;}
+        if($dlevel & QUERY_INFO) {$_REQUEST["QUERY_INFO"] = true;}
+        if($dlevel & TEST_INFO) {$_REQUEST["TEST_INFO"] = true;}
+
+        return "debugDisplay";
+    }
+
+    /**
+     * Configures which methods are allowed by this Yioop instance to access
+     * search results, (via the web, via open rss search results, via the
+     * API)
+     */
+    function searchAccess()
+    {
+        $this->banner();
+        $data = $this->admin->configure();
+        if($data["PROFILE"] != 1) {
+            $_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
+            return "configureMenu";
+        }
+        e("CURRENT SEARCH ACCESS SETTINGS\n==============================\n");
+
+        $settings = array("WEB_ACCESS" => "Web",
+            "RSS_ACCESS" => "RSS", "API_ACCESS" => "API");
+        $items = array();
+        foreach($settings as $setting => $setting_string) {
+            $toggle = ($data[$setting]) ? "On" : "Off";
+            e("$setting_string: [$toggle]\n");
+            $items[$setting] = "Toggle $setting_string";
+        }
+        $items["configureMenu"] = "Return to Main Menu";
+
+        do {
+            $choice = $this->drawChooseItems($items, "searchAccess");
+        } while( $choice == "searchAccess");
+        $this->prepareGlobals($data);
+
+        if($choice == "configureMenu") {
+            $_REQUEST = array();
+            $_SERVER = array();
+            return "configureMenu";
+        }
+        $_REQUEST[$choice] = ($data[$choice]) ? false : true;
+        return "searchAccess";
+    }
+
+    /**
+     * Configures which of the various links of the SERPS page such as
+     * Cache, etc should be displayed. Also, configures whether the signin
+     * links, etc should be displayed.
+     */
+    function searchPageElementLinks()
+    {
+        $this->banner();
+        $data = $this->admin->configure();
+        if($data["PROFILE"] != 1) {
+            $_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
+            return "configureMenu";
+        }
+        e("CURRENT SEARCH PAGE ELEMENTS AND LINKS SETTINGS".
+            "\n===================================================\n");
+
+        $settings = array("WORD_SUGGEST" => "Word Suggest",
+            "SUBSEARCH_LINK"  => "Subsearch Links",
+            "SIGNIN_LINK" => "Sign-in Links", "CACHE_LINK" => "Cache Link",
+            "SIMILAR_LINK" => "Similar Link", "IN_LINK" => "Inlinks",
+            "IP_LINK"=> "IP Links");
+        $items = array();
+        foreach($settings as $setting => $setting_string) {
+            $toggle = ($data[$setting]) ? "On" : "Off";
+            e("$setting_string: [$toggle]\n");
+            $items[$setting] = "Toggle $setting_string";
+        }
+        $items["configureMenu"] = "Return to Main Menu";
+
+        do {
+            $choice = $this->drawChooseItems($items, "searchPageElementLinks");
+        } while( $choice == "searchPageElementLinks");
+        $this->prepareGlobals($data);
+
+        if($choice == "configureMenu") {
+            $_REQUEST = array();
+            $_SERVER = array();
+            return "configureMenu";
+        }
+        $_REQUEST[$choice] = ($data[$choice]) ? false : true;
+        return "searchPageElementLinks";
+    }
+
+
+    /**
+     * Configures settings relating to the location of the name server and
+     * the salt used when communicating with it. Also, configures caching
+     * mechanisms the name server should use when returning results.
+     */
+    function nameServer()
+    {
+        $this->banner();
+        $data = $this->admin->configure();
+        if($data["PROFILE"] != 1) {
+            $_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
+            return "configureMenu";
+        }
+        e("NAME SERVER SETTINGS\n====================\n");
+
+        e("Server Key: [".$data["AUTH_KEY"]."]\n");
+        e("Name Server URL: [".$data["NAME_SERVER"]."]\n");
+
+        $settings = array("USE_FILECACHE" => "Use File Cache",
+            "USE_MEMCACHE" => "Use Memcache");
+        $items = array("serverKey" => "Edit Server Key",
+            "nameServer" => "Edit Name Server Url");
+
+        foreach($settings as $setting => $setting_string) {
+            $toggle = ($data[$setting]) ? "On" : "Off";
+            e("$setting_string: [$toggle]\n");
+            $items[$setting] = "Toggle $setting_string";
+        }
+        e("\nMemcache Servers:\n=================\n".$data["MEMCACHE_SERVERS"].
+            "\n=================\n");
+
+        $items["memcacheServers"] = "Edit Memcache Servers";
+        $items["configureMenu"] = "Return to Main Menu";
+
+        do {
+            $choice = $this->drawChooseItems($items, "nameServerMenu");
+        } while( $choice == "nameServerMenu");
+        $this->prepareGlobals($data);
+
+        switch($choice)
+        {
+            case "configureMenu":
+                $_REQUEST = array();
+                $_SERVER = array();
+                return "configureMenu";
+            break;
+
+            case "serverKey":
+                e("Enter a new server key: ");
+                $_REQUEST["AUTH_KEY"] = readLine();
+            break;
+
+            case "nameServer":
+                e("Enter a new name server url: ");
+                $_REQUEST["NAME_SERVER"] = readLine();
+            break;
+
+            case "memcacheServers":
+                e("Enter memcache servers, one per line.\n".
+                  "Terminate input with a line with only '.' on it:\n");
+                $_REQUEST["MEMCACHE_SERVERS"] = readMessage();
+            break;
+
+            default:
+                $_REQUEST[$choice] = ($data[$choice]) ? false : true;
+        }
+
+        return "nameServer";
+    }
+
+    /**
+     *  Used to set up the name of this instance of the Yioop robot as well
+     *  as its description page.
+     */
+    function robotSetUp()
+    {
+        $this->banner();
+        $data = $this->admin->configure();
+        if($data["PROFILE"] != 1) {
+            $_REQUEST["MESSAGE"] = "Work directory needs to be set/created!";
+            return "configureMenu";
+        }
+        e("CRAWL ROBOT SETTINGS\n====================\n");
+
+        e("Crawl Robot Name: [".$data["USER_AGENT_SHORT"]."]\n");
+        e("Robot Instance: [".$data["ROBOT_INSTANCE"]."]\n");
+        e("\nRobot Description:\n=================\n".
+            $data["ROBOT_DESCRIPTION"] . "\n=================\n");
+
+        $items = array("robotName" => "Edit Robot Name",
+            "robotInstance" => "Edit Robot Instance",
+            "robotDescription" => "Edit Robot Description",
+            "configureMenu" => "Return to Main Menu");
+
+        do {
+            $choice = $this->drawChooseItems($items, "robotSetUp");
+        } while( $choice == "robotSetUp");
+        $this->prepareGlobals($data);
+
+        switch($choice)
+        {
+            case "configureMenu":
+                $_REQUEST = array();
+                $_SERVER = array();
+                return "configureMenu";
+            break;
+
+            case "robotName":
+                e("Enter a new robot name: ");
+                $_REQUEST["USER_AGENT_SHORT"] = readLine();
+            break;
+
+            case "robotInstance":
+                e("Enter a new robot instance value: ");
+                $_REQUEST["ROBOT_INSTANCE"] = readLine();
+            break;
+
+            case "robotDescription":
+                e("Enter a description of your web crawler robot.\n".
+                  "Terminate input with a line with only '.' on it:\n");
+                $_REQUEST["ROBOT_DESCRIPTION"] = readMessage();
+            break;
+        }
+
+        return "robotSetUp";
+    }
+
+
+    /**
+     *  Used to select to confirm, cancel, or re-enter the last profile
+     *  change
+     *
+     *  @param string admin method to call if confirmed
+     *  @param string $reenter_method , return value if reenter chosen
+     *  @return string menu name to do to next
+     */
+    function confirmChange($admin_method, $reenter_method)
+    {
+        $items = array("confirm" => "Confirm Change",
+            "reenter" => "Re-enter the information",
+            "configureMenu" => "Return to the Configure Menu");
+        $first = true;
+        do {
+            $choice = $this->drawChooseItems($items, "confirmChange");
+        } while( $choice == "confirmChange");
+
+        switch($choice)
+        {
+            case "confirm":
+                $data = $this->admin->$admin_method();
+                $_SERVER = array();
+                $_SESSION = array();
+                $_REQUEST = array();
+                $_REQUEST["MESSAGE"] = $data["MESSAGE"];
+                $next_menu = "configureMenu";
+            break;
+            case "reenter":
+                $_SERVER = array();
+                $_SESSION = array();
+                $_REQUEST = array();
+                $next_menu = $reenter_method;
+            break;
+            default:
+                $_SERVER = array();
+                $_SESSION = array();
+                $_REQUEST = array();
+                $next_menu = "configureMenu";
+        }
+        return $next_menu;
+    }
+
+    /**
+     *  Draws a list of options to the screen and gets a choice
+     *  from this list from the user.
+     *
+     *  @param array $items as associative array (return value => description)
+     *  @param string $currentView value to return if invalid choice made
+     *  @return string a choice from the user
+     */
+    function drawChooseItems($items, $currentView)
+    {
+        $choice_nums = array();
+        $i = 1;
+        e("\nAvailable Options:\n==================\n");
+        foreach($items as $name => $description) {
+            e("($i) $description\n");
+            $choice_nums[$i] = $name;
+            $i++;
+        }
+        if(isset($_REQUEST["MESSAGE"]) && $_REQUEST["MESSAGE"] != "") {
+            e("\n+++ ".$_REQUEST["MESSAGE"]." +++\n");
+            unset($_REQUEST["MESSAGE"]);
+        }
+
+        e("\nPlease choose an option:\n");
+        $user_data = strtolower(trim(readLine()));
+
+        if($user_data >= 1 && $user_data < $i) {
+            $_REQUEST["MESSAGE"] = "";
+            return $choice_nums[$user_data];
+        } else {
+            $_REQUEST["MESSAGE"] = "Invalid choice. Please choose again.";
+            return $currentView;
+        }
+    }
+
+    /**
+     *  Prints the banner used by this configuration tool
+     */
+    function banner()
+    {
+        e(chr(27) . "[2J" . chr(27) . "[;H");
+        e("\n\nYIOOP! CONFIGURATION TOOL\n");
+        e("+++++++++++++++++++++++++\n\n");
+    }
+
+    /**
+     * Sets-up the field values of the super globals used by AdminController
+     * when changing a profile or managing passwords. These particular
+     * values don't change with respect to what this tool does.
+     *
+     * @param array $data current profile state
+     */
+    function prepareGlobals($data)
+    {
+        $_SESSION = array();
+        $_REQUEST = $this->copyProfileFields($data);
+        $_REQUEST["arg"] = "profile";
+        $_REQUEST['YIOOP_TOKEN'] = "";
+        if(!isset($_SERVER['REQUEST_URI'])) {
+            if(isset($data['WEB_URI']) && $data['WEB_URI'] !="") {
+                $_SERVER['REQUEST_URI'] = $data['WEB_URI'];
+            } else {
+                e("Enter web path for Yioop instance:\n");
+                $_SERVER['REQUEST_URI'] = readLine();
+            }
+        }
+    }
+
+    /**
+     * Used to copy the contents of $data which are profile fields to a
+     * new array.
+     *
+     * @param array $data an array of profile and other fields
+     * @return array a new array containing a copy of just the profile fields
+     *      from the orginal array
+     */
+    function copyProfileFields($data)
+    {
+        $profile = array();
+        foreach($this->admin->profileModel->profile_fields as $field) {
+            if(isset($data[$field])) {
+                $profile[$field] = $data[$field];
+            }
+        }
+        return $profile;
+    }
+}
+
+
+$configure_tool = new ConfigureTool();
+$configure_tool->loop();
+
+?>
diff --git a/controllers/admin_controller.php b/controllers/admin_controller.php
index 1ad3d4361..f2d954a0d 100755
--- a/controllers/admin_controller.php
+++ b/controllers/admin_controller.php
@@ -337,9 +337,10 @@ class AdminController extends Controller implements CrawlConstants
             {
                 case "changepassword":
                     if($_REQUEST['retypepassword'] != $_REQUEST['newpassword']){
+                        $data["MESSAGE"] =
+                            tl('admin_controller_passwords_dont_match');
                         $data['SCRIPT'] .=
-                            "doMessage('<h1 class=\"red\" >".
-                            tl('admin_controller_passwords_dont_match').
+                            "doMessage('<h1 class=\"red\" >". $data["MESSAGE"].
                             "</h1>')";
                         return $data;
                     }
@@ -348,15 +349,17 @@ class AdminController extends Controller implements CrawlConstants
                     $result = $this->signinModel->checkValidSignin($username,
                     $this->clean($_REQUEST['oldpassword'], "string") );
                     if(!$result) {
+                        $data["MESSAGE"] =
+                            tl('admin_controller_invalid_old_password');
                         $data['SCRIPT'] .= "doMessage('<h1 class=\"red\" >".
-                            tl('admin_controller_invalid_old_password').
-                            "</h1>')";
+                            $data["MESSAGE"]."</h1>')";
                         return $data;
                     }
                     $this->signinModel->changePassword($username,
                         $this->clean($_REQUEST['newpassword'], "string"));
+                    $data["MESSAGE"] = tl('admin_controller_change_password');
                     $data['SCRIPT'] .= "doMessage('<h1 class=\"red\" >".
-                        tl('admin_controller_change_password')."</h1>')";
+                        $data["MESSAGE"]."</h1>')";
                 break;
                 }
         }
@@ -2232,8 +2235,8 @@ class AdminController extends Controller implements CrawlConstants
             $data['LANGUAGES'][$language['LOCALE_TAG']] =
                 $language['LOCALE_NAME'];
         }
-        if(isset($_POST['lang'])) {
-            $data['lang'] = $this->clean($_POST['lang'], "string");
+        if(isset($_REQUEST['lang'])) {
+            $data['lang'] = $this->clean($_REQUEST['lang'], "string");
             $profile['DEFAULT_LOCALE'] = $data['lang'];
             setLocaleObject($data['lang']);
         }
@@ -2242,7 +2245,7 @@ class AdminController extends Controller implements CrawlConstants
         $data['SCRIPT'] = "";

         $data['PROFILE'] = false;
-
+        $data['MESSAGE'] = "";

         if(isset($_REQUEST['WORK_DIRECTORY'])) {
             $dir =
@@ -2258,9 +2261,11 @@ class AdminController extends Controller implements CrawlConstants
                     $data['PROFILE'] = false;
             }
             if($data['PROFILE'] == false) {
+                $data["MESSAGE"] =
+                    tl('admin_controller_configure_use_absolute_path');
                 $data['SCRIPT'] .= "doMessage('<h1 class=\"red\" >".
-                    tl('admin_controller_configure_use_absolute_path').
-                    "</h1>');" . "setTimeout('window.location.href= ".
+                    $data["MESSAGE"]. "</h1>');" .
+                    "setTimeout('window.location.href= ".
                     "window.location.href', 3000);";
                 $data['WORK_DIRECTORY'] = $dir;
                 return $data;
@@ -2290,10 +2295,11 @@ class AdminController extends Controller implements CrawlConstants
                             $data['WORK_DIRECTORY']));
                     $this->profileModel->setWorkDirectoryConfigFile(
                         $data['WORK_DIRECTORY']);
+                    $data["MESSAGE"] =
+                        tl('admin_controller_configure_work_dir_set');
                     $data['SCRIPT'] .=
                         "doMessage('<h1 class=\"red\" >".
-                        tl('admin_controller_configure_work_dir_set').
-                        "</h1>');setTimeout(".
+                        $data["MESSAGE"]. "</h1>');setTimeout(".
                         "'window.location.href=window.location.href', 3000);";
                 } else if ($data['PROFILE'] &&
                     strlen($data['WORK_DIRECTORY']) > 0) {
@@ -2311,18 +2317,20 @@ class AdminController extends Controller implements CrawlConstants
                             $data['WORK_DIRECTORY'], array(), $profile)) {
                             if($this->profileModel->setWorkDirectoryConfigFile(
                                 $data['WORK_DIRECTORY'])) {
+                        $data["MESSAGE"] =
+                            tl('admin_controller_configure_work_profile_made');
                                 $data['SCRIPT'] .=
                                     "doMessage('<h1 class=\"red\" >".
-                             tl('admin_controller_configure_work_profile_made').
-                                    "</h1>');" .
+                                    $data["MESSAGE"]. "</h1>');" .
                                     "setTimeout('window.location.href= ".
                                     "window.location.href', 3000);";
                             } else {
                                 $data['PROFILE'] = false;
+                        $data["MESSAGE"] =
+                            tl('admin_controller_configure_no_set_config');
                                 $data['SCRIPT'] .=
                                     "doMessage('<h1 class=\"red\" >".
-                             tl('admin_controller_configure_no_set_config').
-                                    "</h1>');" .
+                                    $data["MESSAGE"] . "</h1>');" .
                                     "setTimeout('window.location.href= ".
                                     "window.location.href', 3000);";
                             }
@@ -2330,18 +2338,21 @@ class AdminController extends Controller implements CrawlConstants
                             $this->profileModel->setWorkDirectoryConfigFile(
                                 $data['WORK_DIRECTORY']);
                             $data['PROFILE'] = false;
+                        $data["MESSAGE"] =
+                            tl('admin_controller_configure_no_create_profile');
                             $data['SCRIPT'] .=
                                 "doMessage('<h1 class=\"red\" >".
-                            tl('admin_controller_configure_no_create_profile').
+                                $data["MESSAGE"].
                                 "</h1>'); setTimeout('window.location.href=".
                                 "window.location.href', 3000);";
                         }
                     } else {
                         $this->profileModel->setWorkDirectoryConfigFile(
                             $data['WORK_DIRECTORY']);
+                        $data["MESSAGE"] =
+                            tl('admin_controller_configure_work_dir_invalid');
                         $data['SCRIPT'] .=
-                            "doMessage('<h1 class=\"red\" >".
-                        tl('admin_controller_configure_work_dir_invalid').
+                            "doMessage('<h1 class=\"red\" >". $data["MESSAGE"].
                                 "</h1>');".
                             "setTimeout('window.location.href=".
                             "window.location.href', 3000);";
@@ -2350,9 +2361,10 @@ class AdminController extends Controller implements CrawlConstants
                 } else {
                     $this->profileModel->setWorkDirectoryConfigFile(
                         $data['WORK_DIRECTORY']);
+                    $data["MESSAGE"] =
+                        tl('admin_controller_configure_work_dir_invalid');
                     $data['SCRIPT'] .=
-                        "doMessage('<h1 class=\"red\" >".
-                            tl('admin_controller_configure_work_dir_invalid').
+                        "doMessage('<h1 class=\"red\" >". $data["MESSAGE"] .
                             "</h1>');" .
                         "setTimeout('window.location.href=".
                         "window.location.href', 3000);";
@@ -2361,13 +2373,13 @@ class AdminController extends Controller implements CrawlConstants
             break;
             case "profile":
                 foreach($this->profileModel->profile_fields as $field) {
-                    if(isset($_POST[$field])) {
+                    if(isset($_REQUEST[$field])) {
                         if($field != "ROBOT_DESCRIPTION" &&
                             $field != "MEMCACHE_SERVERS") {
                             $clean_field =
-                                $this->clean($_POST[$field], "string");
+                                $this->clean($_REQUEST[$field], "string");
                         } else {
-                            $clean_field = $_POST[$field];
+                            $clean_field = $_REQUEST[$field];
                         }
                         if($field == "NAME_SERVER" &&
                             $clean_field[strlen($clean_field) -1] != "/") {
@@ -2395,11 +2407,11 @@ class AdminController extends Controller implements CrawlConstants
                 }
                 $data['DEBUG_LEVEL'] = 0;
                 $data['DEBUG_LEVEL'] |=
-                    (isset($_POST["ERROR_INFO"])) ? ERROR_INFO : 0;
+                    (isset($_REQUEST["ERROR_INFO"])) ? ERROR_INFO : 0;
                 $data['DEBUG_LEVEL'] |=
-                    (isset($_POST["QUERY_INFO"])) ? QUERY_INFO : 0;
+                    (isset($_REQUEST["QUERY_INFO"])) ? QUERY_INFO : 0;
                 $data['DEBUG_LEVEL'] |=
-                    (isset($_POST["TEST_INFO"])) ? TEST_INFO : 0;
+                    (isset($_REQUEST["TEST_INFO"])) ? TEST_INFO : 0;
                 $profile['DEBUG_LEVEL'] = $data['DEBUG_LEVEL'];

                 $old_profile =
@@ -2428,9 +2440,10 @@ class AdminController extends Controller implements CrawlConstants
                     }
                 }
                 if($db_problem) {
+                    $data['MESSAGE'] =
+                        tl('admin_controller_configure_no_change_db');
                     $data['SCRIPT'] .=
-                        "doMessage('<h1 class=\"red\" >".
-                        tl('admin_controller_configure_no_change_db').
+                        "doMessage('<h1 class=\"red\" >". $data['MESSAGE'].
                         "</h1>');";
                     $data['DBMS'] = $old_profile['DBMS'];
                     $data['DB_NAME'] = $old_profile['DB_NAME'];
@@ -2442,9 +2455,10 @@ class AdminController extends Controller implements CrawlConstants

                 if($this->profileModel->updateProfile(
                 $data['WORK_DIRECTORY'], $profile, $old_profile)) {
+                    $data['MESSAGE'] =
+                        tl('admin_controller_configure_profile_change');
                     $data['SCRIPT'] =
-                        "doMessage('<h1 class=\"red\" >".
-                        tl('admin_controller_configure_profile_change').
+                        "doMessage('<h1 class=\"red\" >". $data['MESSAGE'].
                         "</h1>');";

                         if($old_profile['DEBUG_LEVEL'] !=
@@ -2456,9 +2470,10 @@ class AdminController extends Controller implements CrawlConstants
                         }
                 } else {
                     $data['PROFILE'] = false;
+                    $data["MESSAGE"] =
+                        tl('admin_controller_configure_no_change_profile');
                     $data['SCRIPT'] .=
-                        "doMessage('<h1 class=\"red\" >".
-                        tl('admin_controller_configure_no_change_profile').
+                        "doMessage('<h1 class=\"red\" >". $data["MESSAGE"].
                         "</h1>');";
                     break;
                 }
diff --git a/lib/utility.php b/lib/utility.php
index c314499b0..8695f7e6e 100755
--- a/lib/utility.php
+++ b/lib/utility.php
@@ -810,4 +810,53 @@ function e($text)
 {
     echo $text;
 }
+
+/**
+ * Used to read a line of input from the command-line
+ * @return string from the command-line
+ */
+function readLine()
+{
+    $stdin = fopen('php://stdin', 'r');
+    $line = fgets($stdin);
+    $line = rtrim($line);
+    fclose($stdin);
+    return $line;
+}
+
+
+/**
+ * Used to read a line of input from the command-line
+ * (on unix machines without echoing it)
+ * @return string from the command-line
+ */
+function readPassword()
+{
+    system('stty -echo');
+    $line = readLine();
+    if(!strstr(PHP_OS, "WIN")) {
+        e(str_repeat("*", strlen($line))."\n");
+    }
+    system('stty echo');
+
+    return $line;
+}
+
+/**
+ * Used to read a several lines from the terminal up until
+ * a last line consisting of just a "."
+ * @return string from the command-line
+ */
+function readMessage()
+{
+    $message = "";
+    $line = "";
+    do {
+        $message .= $line;
+        $line = readLine()."\n";
+    } while(rtrim($line) != ".");
+
+    return rtrim($message);
+}
+
 ?>
diff --git a/locale/en-US/pages/bot.thtml b/locale/en-US/pages/bot.thtml
index 123df7e1b..2114742bf 100755
--- a/locale/en-US/pages/bot.thtml
+++ b/locale/en-US/pages/bot.thtml
@@ -1,5 +1,5 @@
-title=Bot
-description=Describes the web crawler used with
-web site
-END_HEAD_VARS
+title=Bot
+description=Describes the web crawler used with
+web site
+END_HEAD_VARS
 Please Describe Your Robot
\ No newline at end of file
diff --git a/locale/zh-CN/configure.ini b/locale/zh-CN/configure.ini
index 92b654d94..1a53e72f8 100755
--- a/locale/zh-CN/configure.ini
+++ b/locale/zh-CN/configure.ini
@@ -35,10 +35,10 @@ admin_controller_login_successful = "登入成功"
 admin_controller_login_failed = "登入失敗"
 ;
 ; admin_controller.php line: 144
-admin_controller_login_to_config = ""
+admin_controller_login_to_config = "登入管理介面"
 ;
 ; admin_controller.php line: 148
-admin_controller_status_updates_stopped = ""
+admin_controller_status_updates_stopped = "管理員終止更新"
 ;
 ; admin_controller.php line: 341
 admin_controller_passwords_dont_match = "密碼錯誤"
@@ -92,7 +92,7 @@ admin_controller_rolename_deleted = "刪除暱稱"
 admin_controller_select_rolename = "暱稱"
 ;
 ; admin_controller.php line: 593
-admin_controller_select_activityname = ""
+admin_controller_select_activityname = "選擇活動名稱"
 ;
 ; admin_controller.php line: 626
 admin_controller_rolename_exists = "此暱稱已存在"
@@ -110,19 +110,19 @@ admin_controller_rolename_deleted = "刪除暱稱"
 admin_controller_rolename_doesnt_exists = "無此暱稱"
 ;
 ; admin_controller.php line: 667
-admin_controller_activityname_doesnt_exists = ""
+admin_controller_activityname_doesnt_exists = "活動名稱不存在"
 ;
 ; admin_controller.php line: 677
-admin_controller_activity_added = ""
+admin_controller_activity_added = "活動名稱已加入"
 ;
 ; admin_controller.php line: 683
 admin_controller_rolename_doesnt_exists = "無此暱稱"
 ;
 ; admin_controller.php line: 690
-admin_controller_activityname_doesnt_exists = ""
+admin_controller_activityname_doesnt_exists = "活動名稱不存在"
 ;
 ; admin_controller.php line: 702
-admin_controller_activity_deleted = ""
+admin_controller_activity_deleted = "活動已刪除"
 ;
 ; admin_controller.php line: 744
 admin_controller_starting_new_crawl = "開始新的搜尋"
@@ -143,19 +143,19 @@ admin_controller_delete_crawl_success = "刪除搜尋,需要一段時間更新"
 admin_controller_delete_crawl_fail = "刪除搜尋失敗"
 ;
 ; admin_controller.php line: 874
-admin_controller_set_index = ""
+admin_controller_set_index = "設置索引"
 ;
 ; admin_controller.php line: 891
-admin_controller_use_below = ""
+admin_controller_use_below = "以下使用者"
 ;
 ; admin_controller.php line: 892
-admin_controller_use_defaults = ""
+admin_controller_use_defaults = "使用者預設"
 ;
 ; admin_controller.php line: 894
-admin_controller_use_below = ""
+admin_controller_use_below = "以下使用者"
 ;
 ; admin_controller.php line: 897
-admin_controller_previous_crawl = ""
+admin_controller_previous_crawl = "前一搜尋"
 ;
 ; admin_controller.php line: 988
 admin_controller_breadth_first = "深度優先"
@@ -164,31 +164,31 @@ admin_controller_breadth_first = "深度優先"
 admin_controller_page_importance = "網頁重要性"
 ;
 ; admin_controller.php line: 1094
-admin_controller_urls_injected = ""
+admin_controller_urls_injected = "插入網址"
 ;
 ; admin_controller.php line: 1105
-admin_controller_update_seed_info = ""
+admin_controller_update_seed_info = "更新種子資訊"
 ;
 ; admin_controller.php line: 1185
-admin_controller_select_crawl = ""
+admin_controller_select_crawl = "搜尋選擇"
 ;
 ; admin_controller.php line: 1186
 admin_controller_default_crawl = ""
 ;
 ; admin_controller.php line: 1188
-admin_controller_select_crawl = ""
+admin_controller_select_crawl = "搜尋選擇"
 ;
 ; admin_controller.php line: 1190
 admin_controller_default_crawl = ""
 ;
 ; admin_controller.php line: 1217
-admin_controller_unnamed = ""
+admin_controller_unnamed = "未命名"
 ;
 ; admin_controller.php line: 1222
 admin_controller_mix_created = ""
 ;
 ; admin_controller.php line: 1231
-admin_controller_set_index = ""
+admin_controller_set_index = "設置索引"
 ;
 ; admin_controller.php line: 1241
 admin_controller_mix_doesnt_exists = ""
@@ -197,163 +197,163 @@ admin_controller_mix_doesnt_exists = ""
 admin_controller_mix_deleted = ""
 ;
 ; admin_controller.php line: 1285
-editmix_element_add_crawls = ""
+editmix_element_add_crawls = "增加索引"
 ;
 ; admin_controller.php line: 1287
-editmix_element_num_results = ""
+editmix_element_num_results = "結果數量"
 ;
 ; admin_controller.php line: 1288
-editmix_element_del_grp = ""
+editmix_element_del_grp = "刪除群組"
 ;
 ; admin_controller.php line: 1289
-editmix_element_weight = ""
+editmix_element_weight = "元素重量"
 ;
 ; admin_controller.php line: 1290
-editmix_element_name = ""
+editmix_element_name = "元素名稱"
 ;
 ; admin_controller.php line: 1291
-editmix_add_keywords = ""
+editmix_add_keywords = "增加關鍵字"
 ;
 ; admin_controller.php line: 1292
-editmix_element_actions = ""
+editmix_element_actions = "元素活動"
 ;
 ; admin_controller.php line: 1293
-editmix_add_query = ""
+editmix_add_query = "增加查詢"
 ;
 ; admin_controller.php line: 1294
-editmix_element_delete = ""
+editmix_element_delete = "刪除元素"
 ;
 ; admin_controller.php line: 1346
 admin_controller_mix_saved = ""
 ;
 ; admin_controller.php line: 1418
-admin_controller_recrawl_never = ""
+admin_controller_recrawl_never = "取消重新搜尋"
 ;
 ; admin_controller.php line: 1419
-admin_controller_recrawl_1day = ""
+admin_controller_recrawl_1day = "每日重新搜尋"
 ;
 ; admin_controller.php line: 1420
-admin_controller_recrawl_2day = ""
+admin_controller_recrawl_2day = "兩日重新搜尋"
 ;
 ; admin_controller.php line: 1421
-admin_controller_recrawl_3day = ""
+admin_controller_recrawl_3day = "三日重新搜尋"
 ;
 ; admin_controller.php line: 1422
-admin_controller_recrawl_7day = ""
+admin_controller_recrawl_7day = "一週重新搜尋"
 ;
 ; admin_controller.php line: 1423
-admin_controller_recrawl_14day = ""
+admin_controller_recrawl_14day = "兩週重新搜尋"
 ;
 ; admin_controller.php line: 1478
-admin_controller_page_options_updated = ""
+admin_controller_page_options_updated = "更新頁面選項"
 ;
 ; admin_controller.php line: 1510
-admin_controller_results_editor_update = ""
+admin_controller_results_editor_update = "編輯者更新結果"
 ;
 ; admin_controller.php line: 1524
-admin_controller_edited_pages = ""
+admin_controller_edited_pages = "編輯頁面"
 ;
 ; admin_controller.php line: 1537
-admin_controller_results_editor_need_url = ""
+admin_controller_results_editor_need_url = "需要網址"
 ;
 ; admin_controller.php line: 1543
-admin_controller_results_editor_page_updated = ""
+admin_controller_results_editor_page_updated = "更新頁面"
 ;
 ; admin_controller.php line: 1556
-admin_controller_results_editor_page_loaded = ""
+admin_controller_results_editor_page_loaded = "載入頁面"
 ;
 ; admin_controller.php line: 1602
 admin_controller_select_machine = ""
 ;
 ; admin_controller.php line: 1674
-admin_controller_machine_added = ""
+admin_controller_machine_added = "加入"
 ;
 ; admin_controller.php line: 1681
-admin_controller_machine_exists = ""
+admin_controller_machine_exists = "已存在"
 ;
 ; admin_controller.php line: 1685
-admin_controller_machine_incomplete = ""
+admin_controller_machine_incomplete = "未完成"
 ;
 ; admin_controller.php line: 1694
-admin_controller_machine_doesnt_exists = ""
+admin_controller_machine_doesnt_exists = "不存在"
 ;
 ; admin_controller.php line: 1711
-admin_controller_stop_service_first = ""
+admin_controller_stop_service_first = "停止服務"
 ;
 ; admin_controller.php line: 1724
-admin_controller_machine_deleted = ""
+admin_controller_machine_deleted = "刪除"
 ;
 ; admin_controller.php line: 1772
-admin_controller_no_machine_log = ""
+admin_controller_no_machine_log = "無紀錄"
 ;
 ; admin_controller.php line: 1801
-admin_controller_machine_servers_updated = ""
+admin_controller_machine_servers_updated = "服務更新"
 ;
 ; admin_controller.php line: 1805
-admin_controller_machine_no_action = ""
+admin_controller_machine_no_action = "無動作"
 ;
 ; admin_controller.php line: 1836
-admin_controller_select_localename = ""
+admin_controller_select_localename = "選擇當地語言"
 ;
 ; admin_controller.php line: 1880
-admin_controller_locale_added = ""
+admin_controller_locale_added = "增加語言"
 ;
 ; admin_controller.php line: 1887
-admin_controller_localename_doesnt_exists = ""
+admin_controller_localename_doesnt_exists = "語言不存在"
 ;
 ; admin_controller.php line: 1896
-admin_controller_localename_deleted = ""
+admin_controller_localename_deleted = "刪除語言"
 ;
 ; admin_controller.php line: 1905
-admin_controller_select_staticpages = ""
+admin_controller_select_staticpages = "靜態頁面選取"
 ;
 ; admin_controller.php line: 1924
-admin_controller_staticpage_updated = ""
+admin_controller_staticpage_updated = "靜態頁面更新"
 ;
 ; admin_controller.php line: 1951
-admin_controller_localestrings_updated = ""
+admin_controller_localestrings_updated = "語言更新"
 ;
 ; admin_controller.php line: 2008
-admin_controller_php_version = ""
+admin_controller_php_version = "php版本"
 ;
 ; admin_controller.php line: 2016
-admin_controller_no_write_config_php = ""
+admin_controller_no_write_config_php = "php未寫入"
 ;
 ; admin_controller.php line: 2021
-admin_controller_no_write_work_dir = ""
+admin_controller_no_write_work_dir = "未寫入目錄"
 ;
 ; admin_controller.php line: 2026
-admin_controller_post_size_small = ""
+admin_controller_post_size_small = "張貼小容量"
 ;
 ; admin_controller.php line: 2032
-admin_controller_missing_required = ""
+admin_controller_missing_required = "缺少必要項目"
 ;
 ; admin_controller.php line: 2055
-admin_controller_missing_optional = ""
+admin_controller_missing_optional = "缺少選擇項目"
 ;
 ; admin_controller.php line: 2060
-admin_controller_check_passed = ""
+admin_controller_check_passed = "通過檢查"
 ;
 ; admin_controller.php line: 2065
-admin_controller_using_local_config = ""
+admin_controller_using_local_config = "使用當地語言"
 ;
 ; admin_controller.php line: 2090
-admin_controller_media_kind = ""
+admin_controller_media_kind = "多媒體類別"
 ;
 ; admin_controller.php line: 2091
-admin_controller_video = ""
+admin_controller_video = "影片"
 ;
 ; admin_controller.php line: 2092
-admin_controller_rss_feed = ""
+admin_controller_rss_feed = "Rss"
 ;
 ; admin_controller.php line: 2106
 admin_controller_sources_indexes = ""
 ;
 ; admin_controller.php line: 2159
-admin_controller_media_source_added = ""
+admin_controller_media_source_added = "增加多媒體"
 ;
 ; admin_controller.php line: 2167
-admin_controller_media_source_deleted = ""
+admin_controller_media_source_deleted = "刪除多媒體"
 ;
 ; admin_controller.php line: 2183
 admin_controller_subsearch_added = ""
@@ -362,22 +362,22 @@ admin_controller_subsearch_added = ""
 admin_controller_subsearch_deleted = ""
 ;
 ; admin_controller.php line: 2261
-admin_controller_configure_use_absolute_path = ""
+admin_controller_configure_use_absolute_path = "使用絕對路徑"
 ;
 ; admin_controller.php line: 2294
-admin_controller_configure_work_dir_set = ""
+admin_controller_configure_work_dir_set = "工作目錄配置"
 ;
 ; admin_controller.php line: 2306
-admin_controller_name_your_bot = ""
+admin_controller_name_your_bot = "取名"
 ;
 ; admin_controller.php line: 2315
-admin_controller_configure_work_profile_made = ""
+admin_controller_configure_work_profile_made = "工作設置已建立"
 ;
 ; admin_controller.php line: 2323
-admin_controller_configure_no_set_config = ""
+admin_controller_configure_no_set_config = "無設置"
 ;
 ; admin_controller.php line: 2334
-admin_controller_configure_no_create_profile = ""
+admin_controller_configure_no_create_profile = "未建立簡歷"
 ;
 ; admin_controller.php line: 2343
 admin_controller_configure_work_dir_invalid = ""
diff --git a/locale/zh-CN/statistics.txt b/locale/zh-CN/statistics.txt
index a1aadfcd7..187cb44f0 100755
--- a/locale/zh-CN/statistics.txt
+++ b/locale/zh-CN/statistics.txt
@@ -1 +1 @@
-d:16;
\ No newline at end of file
+d:32;
\ No newline at end of file
diff --git a/models/source_model.php b/models/source_model.php
index cb5390e84..36b81c87d 100644
--- a/models/source_model.php
+++ b/models/source_model.php
@@ -275,7 +275,7 @@ class SourceModel extends Model
         foreach($feeds as $feed) {
             $dom = new DOMDocument();
             @$dom->loadXML($feed[CrawlConstants::PAGE]);
-            $lang = DEFAULT_LOCALE;
+            $lang = "";
             if(!isset($feed["LANGUAGE"]) || $feed["LANGUAGE"] == "") {
                 $languages = $dom->getElementsByTagName('language');
                 if($languages && is_object($languages) &&
@@ -341,7 +341,7 @@ class SourceModel extends Model
                 if(isset($feed[$item['SOURCE_NAME']])) {
                     $lang = $feed[$item['SOURCE_NAME']]['LANGUAGE'];
                 } else {
-                    $lang = DEFAULT_LOCALE;
+                    $lang = "";
                 }
                 $phrase_string = $item["TITLE"] . " ". $item["DESCRIPTION"];
                 $word_lists = PhraseParser::extractPhrasesInLists(
@@ -352,10 +352,12 @@ class SourceModel extends Model
                     UrlParser::getHost($item["LINK"])."/",true), 1);
                 $meta_ids = array("media:news", "media:news:".
                     urlencode($source_name));
-                $lang_parts = explode("-", $lang);
-                $meta_ids[] = 'lang:'.$lang_parts[0];
-                if(isset($lang_parts[1])){
-                    $meta_ids[] = 'lang:'.$lang;
+                if($lang != "") {
+                    $lang_parts = explode("-", $lang);
+                    $meta_ids[] = 'lang:'.$lang_parts[0];
+                    if(isset($lang_parts[1])){
+                        $meta_ids[] = 'lang:'.$lang;
+                    }
                 }
                 $feed_shard->addDocumentWords($doc_keys, $item['PUBDATE'],
                     $word_lists, $meta_ids, true, false);
@@ -416,10 +418,12 @@ class SourceModel extends Model
             $raw_guid."d". substr(crawlHash(
             UrlParser::getHost($item["link"])."/",true), 1);
         $meta_ids = array("media:news", "media:news:".urlencode($source_name));
-        $lang_parts = explode("-", $lang);
-        $meta_ids[] = 'lang:'.$lang_parts[0];
-        if(isset($lang_parts[1])){
-            $meta_ids[] = 'lang:'.$lang;
+        if($lang != "") {
+            $lang_parts = explode("-", $lang);
+            $meta_ids[] = 'lang:'.$lang_parts[0];
+            if(isset($lang_parts[1])){
+                $meta_ids[] = 'lang:'.$lang;
+            }
         }
         $feed_shard->addDocumentWords($doc_keys, $item['pubDate'], $word_lists,
             $meta_ids, true, false);
ViewGit