<?php
/**
 * SeekQuarry/Yioop --
 * Open Source Pure PHP Search Engine, Crawler, and Indexer
 *
 * Copyright (C) 2009 - 2026  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 <https://www.gnu.org/licenses/>.
 *
 * END LICENSE
 *
 * Used to set the configuration settings of the Yioop/SeekQuarry project.
 *
 * Some settings can be set in the Page Options and Server Settings
 * and Appearance activities. Other settings can be overridden by making
 * a LocalConfig.php file in the same folder as this file and using the
 * same namespace.  If a setting in this file is created using nsdefine
 * it is unlikely that it is safe to override. If it is created using
 * nsconddefine it should be fair game for tweaking in the LocalConfig.php
 * file
 * @author Chris Pollett chris@pollett.org
 * @license https://www.gnu.org/licenses/ GPL3
 * @link https://www.seekquarry.com/
 * @copyright 2009 - 2026
 * @filesource
 */
namespace seekquarry\yioop\configs;

/**
 * So can autoload classes. We try to use the autoloader that
 * Composer would define but if that fails we use a default autoloader
 */
if (file_exists(__DIR__ . "/../../vendor/autoload.php")) {
    require_once __DIR__ . "/../../vendor/autoload.php";
} else {
    spl_autoload_register(function ($class) {
        /* project-specific namespace prefix */
        $prefix = 'seekquarry\\yioop\\tests';
        /* does the class use the namespace prefix? */
        $len = strlen($prefix);
        if (strncmp($prefix, $class, $len) !== 0) {
            $prefix = 'seekquarry\\yioop';
            $len = strlen($prefix);
            /* no, move to the next registered autoloader */
            if (strncmp($prefix, $class, $len) !== 0) {
                $prefix = 'seekquarry\\atto';
                $len = strlen($prefix);
                if (strncmp($prefix, $class, $len) !== 0) {
                    return;
                } else {
                    $check_dirs = [WORK_DIRECTORY . "/app/library/atto_servers",
                        BASE_DIR . "/library/atto_servers"];
                }
            } else {
                $check_dirs = [WORK_DIRECTORY . "/app", BASE_DIR];
            }
        } else {
            $check_dirs = [PARENT_DIR . "/tests"];
        }
        /* get the relative class name */
        $relative_class = substr($class, $len);
        /* use forward-slashes, add ./php */
        $unixify_class_name = "/".str_replace('\\', '/', $relative_class) .
            '.php';
        foreach ($check_dirs as $dir) {
            $file = $dir . $unixify_class_name;
            if (file_exists($file)) {
                require $file;
                break;
            }
        }
    });
}
/**
 * yioop_error_handler writes out what PHP was doing when something went
 * wrong, in place of PHP's own handling, and is used when the error box
 * was ticked in the configure menu.
 *
 * @param int $errno the level of the error raised, as an integer
 * @param string $errstr the error message
 * @param string $errfile the filename the error occurred in
 * @param int $errline the line number of the error
 */
function yioop_error_handler($errno, $errstr, $errfile, $errline)
{
    $num_lines_of_backtrace = 5;
    $error_types = [
        E_NOTICE => 'NOTICE:', E_WARNING => 'WARNING:'];
    $type = (isset($error_types[$errno])) ? $error_types[$errno]:
        "PHP OTHER ERROR";
    echo "<pre>\n";
    echo "$type $errstr at line $errline in $errfile\n";
    $backtrace = debug_backtrace();
    array_shift($backtrace);
    $shown = 0;
    $in_or_called = "in";
    foreach ($backtrace as $call) {
        $function = "";
        if (isset($call['class'])) {
            $function .= $call['class']."->";
        }
        if (isset($call['function'])) {
            $function .= $call['function'];
        }
        $line = (isset($call['line'])) ? $call['line'] : "";
        $file = (isset($call['file'])) ? $call['file'] : "";
        echo "  $in_or_called $function, line $line".
            " in $file \n";
        $in_or_called = "called from";
        $shown++;
        if ($shown >= $num_lines_of_backtrace) {
            break;
        }
    }
    if (count($backtrace) > $num_lines_of_backtrace) {
        echo "...\n";
    }
    echo "</pre>";
}
/**
 * nsdefine gives a constant a value in the namespace Yioop's settings
 * live in, seekquarry\yioop\configs, so a name defined here does not
 * meet a name of its own spelling elsewhere.
 *
 * @param string $constant the name of the constant to define
 * @param $value the value to give it
 */
function nsdefine($constant, $value)
{
    define("seekquarry\\yioop\\configs\\" . $constant, $value);
}
/**
 * nsdefined says whether a constant already has a value in the namespace
 * Yioop's settings live in.
 *
 * @param string $constant the constant to check if defined
 * @return bool whether or not it was
 */
function nsdefined($constant)
{
    return defined("seekquarry\\yioop\\configs\\" . $constant);
}
/**
 * nsconddefine gives a constant a value in the namespace Yioop's
 * settings live in, and leaves it alone where it already has one. Most
 * of this file uses this, so a site may set anything for itself in its
 * own LocalConfig.php and have that setting stand.
 *
 * @param string $constant the name of the constant to define
 * @param $value the value to give it
 */
function nsconddefine($constant, $value)
{
    if (!defined("seekquarry\\yioop\\configs\\" . $constant)) {
        define("seekquarry\\yioop\\configs\\" . $constant, $value);
    }
}
/**
 * Reads or overrides a profile setting through a per-process cache so a value
 * changed while the server is running is seen without a restart. On the first
 * read of a label the cache is seeded from the boot constant of the same name;
 * a later call with $set true replaces the cached value. updateProfile makes
 * that call for every field it rewrites into Profile.php, so a read through
 * p('NAME') returns the new value at once where code that reads the boot
 * constant C\NAME directly would still see the value fixed at startup.
 *
 * @param string $label name of the profile setting, e.g. 'USE_MODERATION'
 * @param mixed $value value to store when $set is true
 * @param bool $set true to store $value under $label, false to read $label
 * @return mixed current cached value for $label
 * @throws \Exception when reading a label that is neither cached nor a
 *     defined constant, or when a set is attempted from anywhere other
 *     than ProfileModel::updateProfile
 */
function p($label, $value = 0, $set = false)
{
    static $profile = [];
    if ($set) {
        /* A set is only allowed from updateProfile, which rewrites
           Profile.php, so the cache acts like a constant that changes
           only when the profile itself changes rather than from any
           caller. */
        $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1] ?? [];
        if (($caller['function'] ?? '') !== 'updateProfile' ||
            !str_ends_with($caller['class'] ?? '', '\\ProfileModel')) {
            throw new \Exception("p() may only set a profile value from " .
                "ProfileModel::updateProfile");
        }
        $profile[$label] = $value;
    } else if (!array_key_exists($label, $profile)) {
        if (nsdefined($label)) {
            $profile[$label] = constant(__NAMESPACE__ . "\\" . $label);
        } else {
            throw new \Exception("Profile constant $label not defined");
        }
    }
    return $profile[$label];
}
/**
 * Version number for generator meta tag
 * @var int
 */
nsconddefine('GENERATOR_STRING', "Yioop");
/**
 * Version number for upgrade database function
 * @var int
 */
nsdefine('DATABASE_VERSION', 128);
/**
 * Largest total running time, in seconds, the server gives one git service
 * request. Building a pack walks a repository's history and works out
 * differences, which takes longer than rendering a page, so it is allowed
 * more than the ordinary limit rather than the limit being loosened for
 * every request.
 */
nsconddefine('GIT_SERVICE_WORK_SECONDS', 300.0);

/**
 * Minimum Version fo Yioop for which keyword ad script
 * still works with this version
 * @var int
 */
nsdefine('MIN_AD_VERSION', 36);
/**
 * Version number for upgrading locale resource folders and for upgrading
 * public and help wikis
 */
nsdefine('RESOURCES_WIKI_VERSION', 13);
/**
 * initializeBaseUrlAndCurrentWorkingDirectory works out where Yioop is
 * running from and what the fixed path part of its links is, writing the
 * second into SHORT_BASE_URL. Only the path is settled here. Which host
 * a link names varies from one request to the next and baseUrl() builds
 * that part when a link is wanted. Where Yioop is run from the command
 * line as its own web server, through index.php, this also moves the
 * working directory to where that script sits and writes SERVER_NAME
 * from the address the server was started on, since no request has been
 * made yet to take a host from.
 */
function initializeBaseUrlAndCurrentWorkingDirectory()
{
    global $argv;
    $path_info = pathinfo($_SERVER['SCRIPT_NAME']);
    if (php_sapi_name() == 'cli') {
        if (defined('seekquarry\\yioop\\BASE_INDEX_ENTRY')) {
            \seekquarry\yioop\processCommandLine();
            if (!empty($path_info['dirname']) && $path_info['dirname'] != '.') {
                chdir($path_info['dirname']);
                $path_info['dirname'] = '.';
                $_SERVER['DOCUMENT_ROOT'] = '';
                $_SERVER['PHP_SELF'] = 'index.php';
                $_SERVER['SCRIPT_NAME'] = 'index.php';
                $_SERVER['SCRIPT_FILENAME'] = 'index.php';
                $_SERVER['PATH_TRANSLATED'] = 'index.php';
                $_SERVER['PWD'] = $path_info['dirname'];
            }
            /*
                When the server is launched with a full url rather than
                a bare port, carry that url's host into SERVER_NAME. No
                HTTP request has arrived yet at start-up, so without this
                a base url built before the first request would fall back
                to localhost even though the operator named the real
                address to serve on. A per-request name from the web
                server in front of Yioop still overrides this once a
                request arrives.
             */
            if (!empty($argv[3]) && intval($argv[3]) <= 0) {
                $url_parts = @parse_url($argv[3]);
                if (!empty($url_parts['host']) &&
                    empty($_SERVER['SERVER_NAME'])) {
                    $_SERVER['SERVER_NAME'] = $url_parts['host'];
                }
            }
        } else {
            /* This situation happens for some command line tools like
               QueryTool which can run fine without a base url being
               defined
             */
            return;
        }
    }
    /*
        A SERVER_NAME pinned in a SERVER_CONTEXT from LocalConfig.php is
        the operator's chosen host; carry it into $_SERVER so a base url
        built outside a request uses it. A per-request name from the web
        server in front of Yioop still overrides this once a request
        arrives.
     */
    if (nsdefined('SERVER_CONTEXT')) {
        $context = SERVER_CONTEXT;
        if (!empty($context['SERVER_NAME'])) {
            $_SERVER['SERVER_NAME'] = $context['SERVER_NAME'];
        }
    }
    $dir_name = $path_info["dirname"];
    if ($dir_name == ".") {
        $dir_name = "";
    }
    $extra_slash = ($dir_name == '/') ? "" : '/';
    if ((!defined("seekquarry\\yioop\\configs\\REDIRECTS_ON") || !REDIRECTS_ON)
        && !str_contains($dir_name, "src")) {
        $extra_slash .= "src/";
    }
    nsdefine("SHORT_BASE_URL", $dir_name . $extra_slash);
}
/**
 * defaultServerName is the best guess for this server's own host name
 * when no request has
 * supplied one. This is the host baseUrl() falls back to for links
 * built outside a web request -- command-line tools, the mail
 * daemon, or the boot that happens before the first request -- so
 * that, for example, an account-activation link in outbound mail
 * names a real host rather than localhost.
 *
 * It prefers the first name the secure server is configured to
 * serve (the SECURE_DOMAINS entry an operator may add to
 * SERVER_CONTEXT), then the operating-system host name reported by
 * gethostname (which works the same on Unix-like systems and
 * Windows), and only uses localhost when neither is available.
 *
 * @return string a host name suitable for building the base url
 */
function defaultServerName()
{
    if (nsdefined('SERVER_CONTEXT')) {
        $context = SERVER_CONTEXT;
        if (!empty($context['SECURE_DOMAINS'])) {
            $domains = $context['SECURE_DOMAINS'];
            if (is_string($domains)) {
                $domains = array_values(array_filter(array_map('trim',
                    preg_split('/[\s,]+/', $domains))));
            }
            if (!empty($domains[0])) {
                return $domains[0];
            }
        }
    }
    $host = gethostname();
    return (is_string($host) && $host !== "") ? $host : "localhost";
}
/**
 * baseUrl gives the whole address this Yioop install is reached at for
 * the request being served: the scheme, the host now serving the request, any
 * non-standard port, and the path Yioop lives under. The host comes
 * from $_SERVER['SERVER_NAME'], which whichever web server sits in
 * front of Yioop -- the bundled atto WebSite, Apache, or nginx --
 * sets per request, so on a server answering to several domains this
 * returns links on the domain the visitor actually used. Outside a
 * request, where no host is known, it uses defaultServerName().
 *
 * This is a function rather than a fixed constant because the host
 * (and scheme) can differ from one request to the next; only the
 * path part, SHORT_BASE_URL, is fixed. Command-line tools that never
 * set up that path get NAME_SERVER, which is the fallback the old
 * BASE_URL constant carried.
 *
 * @return string the base URL up to where a query string begins, for
 *      example https://example.com/ or http://host:8080/src/
 */
function baseUrl()
{
    if (php_sapi_name() == 'cli' &&
        !defined('seekquarry\\yioop\\BASE_INDEX_ENTRY')) {
        return nsdefined('NAME_SERVER') ? NAME_SERVER : "/";
    }
    $server_name = !empty($_SERVER['SERVER_NAME']) ?
        $_SERVER['SERVER_NAME'] : defaultServerName();
    if (str_contains($server_name, ":") && $server_name[0] != '[') {
        $server_name = "[$server_name]";
    }
    $server_port = $_SERVER['HTTP_X_FORWARDED_PORT'] ??
        ($_SERVER['SERVER_PORT'] ?? '');
    /*
        Apache's mod_ssl sets $_SERVER['HTTPS'] to "off" on a plain
        request rather than leaving it unset, so the literal "off" is
        screened out. When the request carries no HTTPS flag at all --
        a command-line context, or the atto server before its first
        request -- a TLS context configured for the atto server (an
        ssl key in SERVER_CONTEXT) is taken to mean the site is
        reached over https. That fallback is gated on IS_OWN_WEB_SERVER
        so that under Apache or nginx, where HTTPS is set per request,
        the mere availability of a certificate never forces https onto
        a plain request.
     */
    $https_on = !empty($_SERVER['HTTPS']) &&
        strtolower((string)$_SERVER['HTTPS']) !== 'off';
    if (!$https_on && !isset($_SERVER['HTTPS']) &&
        nsdefined('SERVER_CONTEXT') &&
        nsdefined('IS_OWN_WEB_SERVER') && IS_OWN_WEB_SERVER) {
        $context = SERVER_CONTEXT;
        $https_on = !empty($context['ssl']);
    }
    $http = ($https_on || $server_port == 443) ? "https://" : "http://";
    $port = "";
    if (($http == "http://" && $server_port !== '' && $server_port != 80) ||
        ($http == "https://" && $server_port !== '' &&
        $server_port != 443)) {
        $port = ":" . $server_port;
    }
    return $http . $server_name . $port . SHORT_BASE_URL;
}
/*
    pcre is an external library to php which can cause Yioop
    to seg fault if given instances of reg expressions with
    large recursion depth on a string.
    https://bugs.php.net/bug.php?id=47376
    The goal here is to cut off these problems before they happen.
    We do this in config.php because it is included in most Yioop
    files.
 */
ini_set('pcre.recursion_limit', 3000);
ini_set('pcre.backtrack_limit', 1000000);
    /** Calculate base directory of script
     * @ignore
     */
nsconddefine("BASE_DIR", str_replace("\\", "/", realpath(__DIR__ ."/../")));
nsconddefine("PARENT_DIR",  substr(BASE_DIR, 0, -strlen("/src")));
nsconddefine("TEST_DIR",  PARENT_DIR . '/tests');
if (file_exists(BASE_DIR . "/configs/LocalConfig.php")) {
    /** Include any locally specified defines (could use as an alternative
     *    way to set work directory) */
    require_once(BASE_DIR . "/configs/LocalConfig.php");
}
/**
 * Whether a git service request records what it received. When on, one
 * line per request is appended to git_trace.log in the log folder saying
 * how long the body was, what its first bytes are, what the client said it
 * encoded them as, and how the first line of it read. It is off by
 * default: it is for tracking down a client whose requests arrive as
 * something other than what they should be, and it says nothing about a
 * request's contents beyond its shape.
 */
nsconddefine('GIT_SERVICE_TRACE', false);
if (!defined("seekquarry\\yioop\\configs\\REDIRECTS_ON")) {
    if (!empty($_SERVER['YIOOP_REDIRECTS_ON']) ||
        !empty($_SERVER['REDIRECT_YIOOP_REDIRECTS_ON']) ||
        php_sapi_name() == 'cli' ||
        (nsdefined("IS_OWN_WEB_SERVER") && IS_OWN_WEB_SERVER)) {
        define("seekquarry\\yioop\\configs\\REDIRECTS_ON", true);
    } else {
        define("seekquarry\\yioop\\configs\\REDIRECTS_ON", false);
    }
}
initializeBaseUrlAndCurrentWorkingDirectory();
/** Yioop Namespace*/
nsdefine('NS', "seekquarry\\yioop\\");
/** configs sub-namespace */
nsdefine('NS_CONFIGS', NS . "configs\\");
/** controllers sub-namespace */
nsdefine('NS_CONTROLLERS', NS . "controllers\\");
/** components sub-namespace */
nsdefine('NS_COMPONENTS', NS_CONTROLLERS . "components\\");
/** executables sub-namespace */
nsdefine('NS_EXEC', NS . "executables\\");
/** library sub-namespace */
nsdefine('NS_LIB', NS . "library\\");
/** jobs sub-namespace */
nsdefine('NS_JOBS', NS_LIB . "media_jobs\\");
/** Models sub-namespace */
nsdefine('NS_MODELS', NS . "models\\");
/** datasources sub-namespace */
nsdefine('NS_DATASOURCES', NS_MODELS . "datasources\\");
/** archive_bundle_iterators sub-namespace */
nsdefine('NS_ARCHIVE', NS_LIB . "archive_bundle_iterators\\");
/** indexing_plugins sub-namespace */
nsdefine('NS_PLUGINS', NS_LIB . "indexing_plugins\\");
/** indexing_plugins sub-namespace */
nsdefine('NS_PROCESSORS', NS_LIB . "processors\\");
/** indexing_plugins sub-namespace */
nsdefine('NS_COMPRESSORS', NS_LIB . "compressors\\");
/** text summarizer sub-namespace */
nsdefine('NS_SUMMARIZERS', NS_LIB . "summarizers\\");
/** locale sub-namespace */
nsdefine('NS_LOCALE', NS . "locale\\");
/** views sub-namespace */
nsdefine('NS_VIEWS', NS . "views\\");
/** elements sub-namespace */
nsdefine('NS_ELEMENTS', NS_VIEWS . "elements\\");
/** helpers sub-namespace */
nsdefine('NS_HELPERS', NS_VIEWS . "helpers\\");
/** layouts sub-namespace */
nsdefine('NS_LAYOUTS', NS_VIEWS . "layouts\\");
/** tests sub-namespace */
nsdefine('NS_TESTS', NS . "tests\\");
/** Don't display any query info*/
nsdefine('NO_DEBUG_INFO', 0);
/** bit of DEBUG_LEVEL used to indicate test cases should be displayable*/
nsdefine('TEST_INFO', 1);
/** bit of DEBUG_LEVEL used to indicate query statistics should be displayed*/
nsdefine('QUERY_INFO', 2);
/** bit of DEBUG_LEVEL used to indicate php messages should be displayed*/
nsdefine('ERROR_INFO', 4);
/** Maintenance mode restricts access to local machine*/
nsconddefine("MAINTENANCE_MODE", false);
/**
 * How often, in whole seconds, the built-in (atto) web server writes a
 * memory-usage line to the error log while it runs, or 0 to leave this
 * off. It is a switchable diagnostic for tracking slow memory growth in
 * the always-on server: set it to a small number of seconds in
 * LocalConfig.php to turn the sampling on. Each line gives current and
 * peak memory in megabytes alongside the live connection, reading-stream,
 * writing-stream, session and timer counts.
 */
nsconddefine("WEBSITE_MEMORY_SAMPLE_PERIOD", 0);
/** Constant used to indicate lasting an arbitrary number of seconds */
nsdefine('FOREVER', -2);
/** Constant used to indicate most recent occurrence of an impression type */
nsdefine('MOST_RECENT_VIEW', -4);
/** Number of seconds in a day*/
nsdefine('ONE_DAY', 86400);
/** Number of seconds in a week*/
nsdefine('ONE_WEEK', 604800);
/** Number of seconds in a 30 day month */
nsdefine('ONE_MONTH', 2592000);
/** Number of seconds in a 365 day year */
nsdefine('ONE_YEAR',  31536000);
/** Number of seconds in an hour */
nsdefine('ONE_HOUR', 3600);
/** Number of seconds in a minute */
nsdefine('ONE_MINUTE', 60);
/** Number of seconds in a second */
nsdefine('ONE_SECOND', 1);
/**
 * How long an account-activation email link stays valid. After this
 * many seconds the signed token in the link no longer verifies and the
 * person must request a fresh activation mail.
 */
nsconddefine('ACTIVATION_LINK_TIMEOUT', 2 * ONE_DAY);
/**
 * How long an emailed one-time sign-in code stays valid, in seconds.
 * After this the code no longer verifies and the person must request a
 * fresh one. Kept short because it signs the member straight in.
 */
nsconddefine('SIGNIN_CODE_TIMEOUT', 15 * ONE_MINUTE);
/**
 * How long, in seconds, a visitor is first locked out of a page guarded
 * by a captcha or recovery answer once they have used up their free tries
 * (see CAPTCHA_FREE_TRIES). Each further wrong answer doubles this wait,
 * up to the forget age, so a careless person waits only a short time but a
 * determined guesser is slowed down quickly.
 */
nsconddefine('CAPTCHA_TIME_OUT', 15 * ONE_SECOND);
/**
 * How many times a visitor may get a captcha or recovery answer wrong, or
 * reload such a guarded page, before the first lock-out (CAPTCHA_TIME_OUT)
 * begins. Set high enough that an ordinary person reloading the page a few
 * times is never locked out by accident.
 */
nsconddefine('CAPTCHA_FREE_TRIES', 5);
/**
 * Number of characters in an emailed one-time sign-in code. Eight
 * characters drawn from an unambiguous 31-symbol alphabet give about
 * forty bits, which a single-use, rate-limited, short-lived code does
 * not need to exceed.
 */
nsconddefine('SIGNIN_CODE_LENGTH', 8);
/**
 * How many wrong guesses an emailed sign-in code tolerates before it is
 * thrown away, so a stolen-but-unknown code cannot be brute-forced
 * within its lifetime.
 */
nsconddefine('SIGNIN_CODE_MAX_TRIES', 5);
/** Number of microseconds in a second, used when splitting a
    fractional-seconds duration into the whole-seconds and microseconds
    parts stream_select takes */
nsdefine('MICROSECONDS_PER_SECOND', 1000000);
/** Maximum number of messages to retrieve for summarization */
nsdefine('MAX_SUMMARIZE_MESSAGES', 10000);
/** URL endpoint for LLM API service used for AI summarization
 * For example, LM studio api url (http://localhost:1234/v1/chat/completions)
 */
nsconddefine('LLM_API_URL', '');
/** Model name to use for LLM API requests - for example, "aya-23-8b" */
nsconddefine('LLM_MODEL', '');
/** Whether to use conjunctive search queries or disjunctive */
nsconddefine('USE_CONJUNCTIVE_QUERY', false);
/** setting Profile.php to something else in LocalConfig.php allows one to have
 *  two different yioop instances share the same work_directory but maybe have
 *  different configuration settings. This might be useful if one was
 *  production and one was more dev.
 */
nsconddefine('PROFILE_FILE_NAME', "/Profile.php");
nsconddefine('MAINTENANCE_MESSAGE', <<<EOD
This Yioop! installation is undergoing maintenance, please come back later!
EOD
);
if (MAINTENANCE_MODE && !empty($_SERVER["SERVER_ADDR"]) &&
    !empty($_SERVER["REMOTE_ADDR"]) &&
    $_SERVER["SERVER_ADDR"] != $_SERVER["REMOTE_ADDR"]) {
    echo MAINTENANCE_MESSAGE;
    exit();
}

/**
 * Work Directory (where Yioop stores stuff as it is running) to use during
 * initial configuration and if the user doesn't opt for a custom location.
 */
nsdefine('DEFAULT_WORK_DIRECTORY', PARENT_DIR . "/work_directory");

if (!nsdefined('WORK_DIRECTORY')) {
/*+++ The next block of code is machine edited, change at
your own risk, please use configure web page instead +++*/
nsdefine('WORK_DIRECTORY', DEFAULT_WORK_DIRECTORY);
/*++++++*/
/* end machine edited code */
}
/** Locale dir to use in case LOCALE_DIR does not exist yet or is
 * missing some file
 */
nsdefine('FALLBACK_LOCALE_DIR', BASE_DIR . "/locale");
/** Directory for local versions of web app classes*/
nsconddefine('APP_DIR', WORK_DIRECTORY . "/app");
/**
 * Directory to place files such as dictionaries that will be
 * converted to Bloom filter using token_tool.php. Similarly,
 * can be used to hold files which will be used to prepare
 * a file to assist in crawling or serving search results
 */
nsconddefine('PREP_DIR', WORK_DIRECTORY . "/prepare");
/**
 * Directory for story Crawl Indexes, Crawl Queues, and Fetcher data,
 * queries cache, trends cache
 */
nsconddefine('CACHE_DIR', WORK_DIRECTORY . "/cache");
/**
 * Directory where fetcher writes to (usually just the  CACHE_DIR, but might
 * want to set different if you are worried about a drive getting too many
 * writes)
 */
nsconddefine('FETCHER_CACHE_DIR', CACHE_DIR);
/**
 * Directory used to hold none yioop web crawls and archives like warc files
 */
nsconddefine('ARCHIVES_DIR', WORK_DIRECTORY . "/archives");
/**
 * Queries cache directory
 */
nsconddefine('QUERIES_DIR', CACHE_DIR . "/queries");
/**
 * Trends cache directory
 */
nsconddefine('TRENDS_DIR', CACHE_DIR . "/trends");
/**
 * Directory used for holding temporary files needed during page downloads, etc.
 */
nsconddefine('TEMP_DIR', WORK_DIRECTORY . "/temp");
/**
 * Directory used to store information about the schedules to download next
 * and files for meta information about inter-process communication
 */
nsconddefine('SCHEDULES_DIR', WORK_DIRECTORY . "/schedules");
/**
 * File used by media jobs when they need to store data for processing
 */
nsconddefine('JOBS_DIR', SCHEDULES_DIR . "/jobs");
/**
 * Directory used by the web page classifiers classes
 */
nsconddefine('CLASSIFIERS_DIR', WORK_DIRECTORY . "/classifiers");
/**
 * Directory holding security material that must not be web
 * served: the TLS certificate and private key referenced by
 * SERVER_CONTEXT, and the DKIM signing key pair. The private
 * files in it are kept mode 0600 and the directory 0700.
 */
nsconddefine('SECURITY_DIR', WORK_DIRECTORY . "/security");
/**
 * Path to the TLS certificate chain (PEM) the secure server
 * launcher binds port 443 with, and the path the ACME subsystem
 * writes renewed certificates to. Defaults under SECURITY_DIR;
 * override per deployment to match the managed certificate's name
 * (for example a domain-specific fullchain file).
 */
nsconddefine('SECURE_CERT_FILE', SECURITY_DIR . "/server.fullchain.pem");
/**
 * Path to the private key (PEM) paired with SECURE_CERT_FILE.
 */
nsconddefine('SECURE_KEY_FILE', SECURITY_DIR . "/server.key");
/*
   When the operator has not pinned a SERVER_CONTEXT in LocalConfig.php
   but a secure certificate and its key are already present on disk --
   for example the pair Auto SSL Certs manages at SECURE_CERT_FILE and
   SECURE_KEY_FILE -- derive the TLS server context from those files
   automatically, so the secure web and mail ports can find the
   certificate without the operator repeating its location in a config
   line. A SERVER_CONTEXT set in LocalConfig.php still wins, since it is
   defined before this runs.
 */
if (!nsdefined('SERVER_CONTEXT') && file_exists(SECURE_CERT_FILE) &&
    file_exists(SECURE_KEY_FILE)) {
    nsdefine('SERVER_CONTEXT', ['ssl' => [
        'local_cert' => SECURE_CERT_FILE,
        'local_pk' => SECURE_KEY_FILE,
        /* Advertise the configured handshake protocols so the TLS
           listener offers HTTP/2 (and HTTP/1.1) through ALPN. Without
           this the socket negotiates no ALPN and every browser falls
           back to HTTP/1.1 even when HTTP/2 is enabled in Server
           Settings. Older profiles that predate the ALPN_PROTOCOLS
           field fall back to the same default the field carries. */
        'alpn_protocols' => nsdefined('ALPN_PROTOCOLS') ?
            ALPN_PROTOCOLS : "h2,http/1.1",
    ]]);
}
/**
 * Directory the ACME subsystem writes HTTP-01 challenge tokens to.
 * The secure launcher's port-80 acme-challenge route reads a token
 * file from here and returns its contents (the key authorization)
 * to the validating certificate authority. The key authorization
 * is not secret, but the directory is kept under WORK_DIRECTORY and
 * not web-served as a directory; only the specific token route
 * exposes individual entries.
 */
nsconddefine('ACME_CHALLENGE_DIR', WORK_DIRECTORY . "/acme/challenges");
/**
 * Directory holding the local MailSite mail store: per-user
 * maildirs and the message index/search companion files. Kept out
 * of DATA_DIR so the SQLite default databases and the mail store do
 * not share a folder. Overridable, following the same convention as
 * the other top-level work-directory folders.
 */
nsconddefine('MAIL_DIR', WORK_DIRECTORY . "/mail");
/**
 * How full a store may get before what it holds is written out to make
 * room, as a count of items. Below zero means no such limit, and the
 * store grows until whatever holds it says otherwise.
 */
nsconddefine('OVERFLOW_THRESHOLD', -1);

/** Account RECOVERY_MODE value to indicate no non admin account recovery */
nsdefine('NO_RECOVERY', 0);
/**
 * Account RECOVERY_MODE value to indicate recovery via email address on file
 * with Yioop
 */
nsdefine('EMAIL_RECOVERY', 1);
/**
 * Account RECOVERY_MODE value for an offline or airgapped install that has
 * no outgoing mail: the member recovers their account by answering a
 * personal question they chose and answered when registering, rather than
 * by clicking a link sent to their email address.
 */
nsdefine('QUESTIONS_RECOVERY', 2);
/**
 * AUTH_METHOD value meaning members sign in with password accounts stored
 * locally by Yioop itself (only a one-way hash of each password is kept).
 */
nsdefine('LOCAL_AUTHENTICATION', 0);
/**
 * AUTH_METHOD value meaning an organization's LDAP directory authenticates
 * members in place of Yioop's own accounts.
 */
nsdefine('LDAP_AUTHENTICATION', 1);
/**
 * AUTH_METHOD value meaning LDAP was chosen as the sign-in method but its
 * settings are not yet complete (a server, suffix, or base DN is missing,
 * two accounts share an email, or the root account has no email). The site
 * stores this instead of LDAP_AUTHENTICATION so that, after a restart, it
 * remembers the choice yet keeps using locally stored passwords until an
 * operator finishes the setup, rather than trying to reach a directory it
 * cannot use. It becomes LDAP_AUTHENTICATION once the settings are valid.
 */
nsdefine('LDAP_AUTHENTICATION_PENDING', 2);
if (file_exists(WORK_DIRECTORY . PROFILE_FILE_NAME)) {
    if ((file_exists(WORK_DIRECTORY . "/locale/en-US") &&
        !file_exists(WORK_DIRECTORY . "/locale/en_US"))
        || (file_exists(WORK_DIRECTORY . "/app/locale/en-US") &&
        !file_exists(WORK_DIRECTORY . "/app/locale/en_US"))) {
        $old_profile = file_get_contents(WORK_DIRECTORY . PROFILE_FILE_NAME);
        $new_profile = preg_replace('/\<\?php/', "<?php\n".
            "namespace seekquarry\\yioop\\configs;\n",
            $old_profile);
        $new_profile = preg_replace("/(define(?:d?))\(/", 'ns$1(',
            $new_profile);
        file_put_contents(WORK_DIRECTORY . PROFILE_FILE_NAME, $new_profile);
    }
    require_once WORK_DIRECTORY . PROFILE_FILE_NAME;
    nsdefine('PROFILE', true);
    if (is_dir(APP_DIR . "/locale")) {
        nsdefine('LOCALE_DIR', WORK_DIRECTORY . "/app/locale");
    } else {
        /** @ignore */
        nsdefine('LOCALE_DIR', FALLBACK_LOCALE_DIR);
    }
    if (NAME_SERVER == 'http://' || NAME_SERVER == 'https://') {
        nsdefine("FIX_NAME_SERVER", true);
    }
    if ((!nsdefined('LIBRARY') || !LIBRARY)) {
        /**
         * Directory used to store logs of crawl, web app, and media updater
         * activities
         */
        nsconddefine('LOG_DIR', WORK_DIRECTORY . "/log");
        /**
         * Directory used to store sqlite files (when yioop database is sqlite)
         * and certain bloom filter
         */
        nsconddefine('DATA_DIR', WORK_DIRECTORY . "/data");
    }
} else {
    if ((!isset( $_SERVER['SERVER_NAME']) ||
        $_SERVER['SERVER_NAME']!=='localhost')
        && !nsdefined("NO_LOCAL_CHECK") && !nsdefined("WORK_DIRECTORY")
        && php_sapi_name() != 'cli' &&
        !nsdefined("IS_OWN_WEB_SERVER")) {
        echo "SERVICE AVAILABLE ONLY VIA LOCALHOST UNTIL CONFIGURED";
        exit();
    }
    /** @ignore */
    nsconddefine('PROFILE', false);
    nsdefine('RECOVERY_MODE', EMAIL_RECOVERY);
    nsconddefine('DEBUG_LEVEL', NO_DEBUG_INFO);
    nsdefine('USE_FILECACHE', false);
    nsdefine('WEB_ACCESS', true);
    nsdefine('RSS_ACCESS', true);
    nsdefine('API_ACCESS', true);
    nsdefine('REGISTRATION_TYPE', 'disable_registration');
    nsdefine('MAIL_SENDER', 'bot');
    nsdefine('MAIL_SERVER', '');
    nsdefine('MAIL_PORT', '');
    nsdefine('MAIL_USERNAME', '');
    nsdefine('MAIL_PASSWORD', '');
    nsdefine('MAIL_SECURITY', '');
    nsdefine('MEDIA_MODE', 'name_server');
    nsdefine('DBMS', 'Sqlite3');
    nsdefine('DB_NAME', "public_default");
    nsdefine('DB_USER', '');
    nsdefine('DB_PASSWORD', '');
    nsdefine('DB_HOST', '');
    nsdefine('PRIVATE_DBMS', 'Sqlite3');
    nsdefine('PRIVATE_DB_USER', '');
    nsdefine('PRIVATE_DB_PASSWORD', '');
    nsdefine('PRIVATE_DB_HOST', '');
    nsdefine('PRIVATE_DB_NAME', "private_default");
    /** @ignore */
    nsdefine('LOCALE_DIR', FALLBACK_LOCALE_DIR);
    nsdefine('INDEX_FILE_MEMORY_LIMIT', "250M");
    /** @ignore */
    nsdefine('LOG_DIR', BASE_DIR . "/log");
    /** @ignore */
    nsdefine('DATA_DIR', BASE_DIR . "/data");
    nsdefine('NAME_SERVER', "http://localhost/");
    nsdefine('USER_AGENT_SHORT', "NeedsNameBot");
    nsdefine('DEFAULT_LOCALE', "en-US");
    nsdefine('AUTH_KEY', 0);
    nsdefine('USE_PROXY', false);
    nsdefine('TOR_PROXY', '127.0.0.1:9150');
    nsdefine('PROXY_SERVERS', null);
    nsdefine('DEFAULT_CONTINUOUS_SCROLL', true);
    nsdefine('WORD_CLOUD', true);
    nsdefine('MORE_RESULT', true);
    nsdefine('WORD_SUGGEST', true);
    nsdefine('CACHE_LINK', true);
    nsdefine('SIMILAR_LINK', true);
    nsdefine('IN_LINK', true);
    nsdefine('IP_LINK', true);
    nsdefine('RESULT_SCORE', true);
    nsdefine('SERP_FAVICONS', true);
    nsdefine('SIGNIN_LINK', true);
    nsdefine('SUBSEARCH_LINK', true);
    /**
     *  Bonus to add to relevance score if the hostname contains the search
     *  term. If hostname has 1 keyword bonus is 6, if host has 3 keywords
     *  each would get 2.
     */
    nsdefine('HOST_KEYWORD_BONUS', 6);
    /**
     *  Bonus to add to relevance score if the title contains the search
     *  term. If the title has 1 term bonus would be 5 if 10 terms then 0.5
     *  each
     */
    nsdefine('TITLE_BONUS', 5);
    /**
     *  Bonus to add to relevance score if the url path contains the search
     *  term. If the path has 1 term bonus would be 3 if 3 terms then 1
     *  each
     */
    nsdefine('PATH_KEYWORD_BONUS', 3);
    /**
     *  Bonus to add to doc rank score if the url is a company level domain
     */
    nsdefine('CLD_URL_BONUS', 2);
    /**
     *  Bonus to add to doc rank score if the url is a a hostname
     */
    nsdefine('HOST_URL_BONUS', 0.5);
    /**
     *  User rank scores for a document are normalized between 0 and 1, this is
     *  the weighting factor to multiply that basic score before adding it
     *  to the overall score used for ranking
     */
    nsdefine('USER_RANK_BONUS', 5);
    /**
     *  Bonus to add to doc rank score if the url points to a wikipedia page
     */
    nsdefine('WIKI_BONUS', 0.5);
    /**
     *  Bonus to add to doc rank score based on the number of '/' present in
     *  the url, based on the idea that the lesser the count, the closer
     *  the url is to the root page
     */
    nsdefine('NUM_SLASHES_BONUS', 0.5);
    /**
     * If that many exist, the minimum number of results to get
     * and group before trying to compute the top x (say 10) results
     */
    nsdefine('MIN_RESULTS_TO_GROUP', 200);
    nsdefine('BACKGROUND_COLOR', "#FFFFFF");
    nsdefine('FOREGROUND_COLOR', "#FFFFFF");
    nsdefine('SIDEBAR_COLOR', "#F8F8F8");
    nsdefine('TOPBAR_COLOR', "#F5F5FF");
    nsdefine('MONETIZATION_TYPE', "no_monetization");
    nsdefine('AD_LOCATION','none');
    nsdefine('SEARCH_ANALYTICS_MODE', true);
    nsdefine('GROUP_ANALYTICS_MODE', true);
    nsdefine('DIFFERENTIAL_PRIVACY', false);
}

/**
 * Address put in the Reply-To header of mail Yioop sends to people, such
 * as account-activation messages. Defaults to the sender (bot) mailbox,
 * which is already monitored; an operator can point this at a staffed
 * support address instead so replies reach a person.
 */
nsconddefine('MAIL_REPLY_TO', MAIL_SENDER);
/**
 * How members sign in: with the accounts Yioop manages itself
 * (LOCAL_AUTHENTICATION) or against an organization's LDAP directory
 * (LDAP_AUTHENTICATION). Defaults to Yioop's own accounts; the admin
 * Security activity stores the chosen value in the profile.
 */
nsconddefine('AUTH_METHOD', LOCAL_AUTHENTICATION);
/**
 * Smallest number of characters a locally stored password may have. Only
 * meaningful when AUTH_METHOD is LOCAL_AUTHENTICATION; the admin sets it in
 * the Security activity.
 */
nsconddefine('PASSWORD_MIN_LEN', 8);
/**
 * Whether a locally stored password must contain a lower-case letter.
 */
nsconddefine('PASSWORD_REQUIRE_LOWERCASE', false);
/**
 * Whether a locally stored password must contain an upper-case letter.
 */
nsconddefine('PASSWORD_REQUIRE_UPPERCASE', false);
/**
 * Whether a locally stored password must contain a digit.
 */
nsconddefine('PASSWORD_REQUIRE_DIGIT', false);
/**
 * Whether a locally stored password must contain a punctuation symbol.
 */
nsconddefine('PASSWORD_REQUIRE_SYMBOL', false);
/**
 * Characters a locally stored password may never contain, even though
 * punctuation symbols are otherwise allowed. The single and double quote
 * are forbidden by default because, if one ever reached a form value
 * attribute or a query without being escaped, it could break the
 * surrounding markup or statement.
 */
nsconddefine('PASSWORD_FORBIDDEN_CHARS', "'\"");
/**
 * Comma-separated list of the LDAP directory servers Yioop tries when
 * AUTH_METHOD is LDAP_AUTHENTICATION. Each sign-in picks one of them at
 * random for redundancy. The admin sets this in the Security activity's
 * Authentication panel rather than in LocalConfig.php; an empty default
 * means no directory is configured yet.
 */
nsconddefine('LDAP_CONTROLLERS', '');
/**
 * Text appended to a member's directory name to form the full name the
 * LDAP server binds with, for example "@example.com" so that a member
 * named "jane" binds as "jane@example.com". Only meaningful when
 * AUTH_METHOD is LDAP_AUTHENTICATION; set in the Authentication panel.
 */
nsconddefine('LDAP_ACCOUNT_SUFFIX', '');
/**
 * The directory subtree the LDAP search for a member's email starts
 * from, for example "dc=example,dc=com". Only meaningful when
 * AUTH_METHOD is LDAP_AUTHENTICATION; set in the Authentication panel.
 */
nsconddefine('LDAP_BASE_DN', '');


/** ignore */
nsconddefine('PRIVATE_DBMS', 'Sqlite3');
nsconddefine('PRIVATE_DB_USER', '');
nsconddefine('PRIVATE_DB_PASSWORD', '');
nsconddefine('PRIVATE_DB_HOST', '');
nsconddefine('PRIVATE_DB_NAME', "private_default");
/**
 * Mail Services configuration. The MAIL_MODE constant selects
 * which mail features Yioop offers; the other MAIL_* constants
 * below are only meaningful when MAIL_MODE is something other
 * than 'disabled'. The MailSite SMTP/IMAP listener is wired up
 * under Manage Machines in a later phase; this group of
 * constants just records the operator's intent so the rest of
 * the mail subsystem can read them. Set via nsconddefine so
 * defaults take effect on existing installs whose Profile.php
 * was written before these constants existed.
 */
nsconddefine('MAIL_MODE', 'disabled');
nsconddefine('MAIL_DOMAINS', '');
nsconddefine('MAIL_HOST_NAME', '');
nsconddefine('MAIL_SMTP_PORT', 25);
nsdefine('MAIL_SUBMISSION_PORT_STANDARD', 587);
nsconddefine('MAIL_SUBMISSION_PORT', MAIL_SUBMISSION_PORT_STANDARD);
nsconddefine('MAIL_SMTPS_PORT', 465);
nsconddefine('MAIL_IMAP_PORT', 143);
nsconddefine('MAIL_IMAPS_PORT', 993);
/**
 * When on, a member account may not use an email address on any of
 * this site's own mail domains (the domains in MAIL_DOMAINS). The
 * site's bot address is always refused regardless of this setting;
 * this only extends the refusal to ordinary same-domain addresses.
 * Off by default so an in-house install where everyone is on the
 * site's domain still lets people register with their own address.
 */
nsconddefine('MAIL_REFUSE_LOCAL_DOMAIN_ACCOUNTS', false);
/** Single mail delivery-security posture, chosen in Server
 *  Settings, replacing a separate allow-insecure flag and MTA-STS
 *  mode. One of:
 *   - 'insecure': cleartext connections are allowed, mail that
 *      arrives without TLS is delivered normally, and no MTA-STS
 *      policy is published.
 *   - 'spam': cleartext AUTH and login are refused, a 'testing'
 *      MTA-STS policy is published, and inbound mail that arrived
 *      without TLS is filed in Junk unless its sender is on the
 *      receiving user's personal allow-list.
 *   - 'require': cleartext AUTH and login are refused and an
 *      'enforce' MTA-STS policy is published, asking senders to
 *      refuse cleartext delivery.
 *  When no certificate is configured in SERVER_CONTEXT the secure
 *  ports cannot bind, so the effective posture is forced to
 *  'insecure' regardless of the stored value. */
nsconddefine('MAIL_DELIVERY_SECURITY', 'spam');
/** Whether to evaluate DMARC on inbound mail and act on a failure.
 *  When true, a message whose From domain publishes a DMARC policy
 *  and which has neither an aligned SPF pass nor an aligned DKIM
 *  pass is filed in Junk for a quarantine policy and refused for a
 *  reject policy; a none policy and domains that publish no policy
 *  are delivered normally. When false, DMARC is not consulted. */
nsconddefine('MAIL_DMARC_ENFORCE', false);
/** Selects how the secure SMTP/IMAP ports negotiate TLS. When
 *  false (the default) they use implicit TLS: the connection is
 *  encrypted from the first byte, as on the conventional 465 and
 *  993 ports. When true they start in cleartext and require the
 *  client to issue STARTTLS before MAIL FROM or AUTH, as on the
 *  conventional submission port 587. Either way a certificate
 *  must be configured for the secure ports to bind at all. */
nsconddefine('MAIL_USE_STARTTLS', false);
/** DKIM selector: the label under _domainkey in DNS where the
 *  signing public key is published, as
 *  <selector>._domainkey.<domain>, and named in the DKIM-Signature
 *  header's s= tag on outbound mail. The default carries a fixed
 *  datestamp so that publishing a new key under a new selector
 *  (key rotation) is just a matter of changing this value and
 *  regenerating; it must stay constant once mail is live, since it
 *  has to match the published DNS record, so it is a fixed literal
 *  rather than a value recomputed from the current date. */
nsconddefine('MAIL_DKIM_SELECTOR', 'yioop20260602');
/* Bounds for how long a DNS-derived mail-security record (a DKIM
 *  public key, a DMARC policy, and so on) is cached after it is
 *  looked up. The DNS record's own TTL is honored but clamped to
 *  this range so a very short TTL does not defeat caching and a very
 *  long one does not pin a record that has since changed. A lookup
 *  that finds nothing is cached for the miss time so a missing
 *  record is not re-queried on every message view. */
nsconddefine('MAIL_RECORD_CACHE_MAX', ONE_DAY);
nsconddefine('MAIL_RECORD_CACHE_MIN', ONE_HOUR);
nsconddefine('MAIL_RECORD_CACHE_MISS', ONE_HOUR);
/* Seconds the mail server waits for a name server to answer a
 *  DNS lookup (a DKIM key, an SPF or DMARC record, a mail exchanger)
 *  before giving up and treating the domain as having no such record.
 *  It bounds how long one lookup can hold up delivery: the server does
 *  the wait cooperatively so other connections keep moving, but a
 *  domain whose DNS never answers must still be abandoned rather than
 *  left to stall. */
nsconddefine('MAIL_DNS_TIMEOUT', 5);
/* Name server the mail server falls back to for its own DNS lookups
 *  when the system resolver file (/etc/resolv.conf) lists none. The
 *  loopback address uses a resolver running on the same machine. */
nsconddefine('MAIL_DNS_FALLBACK_SERVER', '127.0.0.1');
nsconddefine('MAIL_EXTERNAL', false);
nsconddefine('MAIL_LOG_ENABLED', false);
/* Internal Web Server (secure launcher) settings. Like the mail
 * settings above these are PROFILE-backed: the operator edits them
 * in Server Settings, updateProfile writes them into Profile.php as
 * nsdefine, and that file is loaded above before this point, so a
 * saved value is already defined and these nsconddefine defaults
 * only fill in installs whose Profile.php predates the field. The
 * secure launcher reads them as constants at process start, before
 * any database access, which is why they live here and not only in
 * the profile table. ACME_ON toggles the certificate subsystem;
 * SECURE_DOMAINS is the newline-or-comma-separated list of names
 * the managed certificate covers (parsed to a list where used, the
 * way MAIL_DOMAINS is); WWW_USER is the user to drop to after
 * binding 80 and 443; ALPN_PROTOCOLS is the handshake protocol
 * list. */
nsconddefine('ACME_ON', false);
nsconddefine('SECURE_DOMAINS', '');
nsconddefine('WWW_USER', "");
nsconddefine('ALPN_PROTOCOLS', "h2,http/1.1");
/** When true, MailSite and the compose-form pre-send validation
 *  relax their address checks so MAIL FROM, RCPT TO, and the To
 *  / Cc / Bcc fields accept bare IP-literal domains
 *  (test@127.0.0.1) and single-label hosts (user@localhost)
 *  that strict RFC validation rejects. Also enables an
 *  alternative outbound port fallback (configured via
 *  MAIL_TEST_MODE_FALLBACK_PORT below) so a deployment whose
 *  network egress blocks port 25 can route through a forwarder
 *  on a higher port. Intended for developer test rigs;
 *  production installs leave this off. The null reverse-path
 *  '<>' is accepted regardless of this setting (RFC 5321 sec
 *  4.5.5: DSN/bounce notifications must always flow). */
nsconddefine('MAIL_TEST_MODE', false);
/** Outbound submission port the MailSite-to-peer-MTA path falls
 *  back to after a TCP-level failure on port 25, used only when
 *  MAIL_TEST_MODE is true. Zero (the default) means no fallback
 *  -- port 25 only. Set to the port of an smtp_tunnel.php
 *  instance (typically 5252) to reach an upstream SMTP server
 *  through a tunnel when the local network egress blocks 25.
 *  Replaces an earlier unconditional 25 -> 587 fallback that
 *  was speculative (the upstream did not in fact accept
 *  unauthenticated submission on 587). */
nsconddefine('MAIL_TEST_MODE_FALLBACK_PORT', 0);
/** Socket-connect-and-read timeout, in seconds, for IMAP and
 *  StartTLS handshakes. 5 was the previous hardcoded default;
 *  in practice that was tight for cold-cache Dovecot installs
 *  where the first session of the day takes longer than that
 *  to accept the TCP connection and complete the TLS
 *  handshake. Apple Mail and other professional IMAP clients
 *  use 30+ seconds; 15 is a middle ground that doesn't hang
 *  the UI very long when the server is truly unreachable. */
nsconddefine('MAIL_IMAP_CONNECT_TIMEOUT', 15);
/** Maximum send attempts for one scheduled-send message. Once a
 *  scheduled message has been tried this many times without
 *  success it stays in the failed state until the user cancels
 *  or reschedules. Set conservatively low so a permanent failure
 *  (auth rejection, undeliverable recipient) doesn't loop forever
 *  retrying the same broken send. */
nsconddefine('MAIL_SCHEDULED_MAX_ATTEMPTS', 3);
/** When true, every Mail page load runs a quick dispatch pass on
 *  due scheduled messages. When false (typical for installs with
 *  a cron job pointing at MachineController::scheduledMailDispatch),
 *  page loads stay fast even when there's a delivery backlog. */
nsconddefine('MAIL_SCHEDULED_LAZY_DISPATCH', true);
/** Socket-read timeout, in seconds, for the IMAP SORT command.
 *  SORT on very large mailboxes (tens of thousands of messages
 *  upward) can run well past the default per-command timeout, so
 *  the Mail activity raises it just for SORT. */
nsconddefine('MAIL_SORT_TIMEOUT', 30);
/** Maximum folder size, in messages, that the Mail activity will
 *  sort PHP-side when the IMAP server does not advertise the SORT
 *  extension (Gmail is the common example). Above this size the
 *  envelope-header FETCH required for a PHP-side sort by subject
 *  or from would take too long; the column header reverts to plain
 *  text and the folder stays on the default newest-first view.
 *  Date sort is unaffected -- it uses sequence-id order and needs
 *  no envelope fetch regardless of folder size. */
nsconddefine('MAIL_PHP_SORT_MAX', 2000);
/** Time-to-live, in seconds, for the cached unread-mail count
 *  that drives the badge on the social-controls mail icon. The
 *  count is computed by issuing an IMAP STATUS lookup against
 *  each configured account's default folder, which is fast but
 *  not free; the session cache short-circuits that lookup on
 *  every subsequent page load until the TTL expires.
 *  Invalidation on viewMessage and bulkAction keeps the badge
 *  responsive when the user is the one changing read state. */
nsconddefine('MAIL_UNREAD_BADGE_TTL', 300);
/** Path-only version of the base url, used to build relative links.
 *  The definition below only takes effect for some command line
 *  tools; for web requests it is set from the script path at start-up.
 */
nsconddefine('SHORT_BASE_URL', NAME_SERVER);
/** Relative url to website logos of different sizes */
nsconddefine('LOGO_SMALL', "resources/yioop-small.png");
nsconddefine('LOGO_MEDIUM', "resources/yioop-medium.png");
nsconddefine('LOGO_LARGE', "resources/yioop-large.png");
/** Url for website favicon */
nsconddefine('FAVICON', "favicon.ico");
/** Timezone for website */
nsconddefine('TIMEZONE', 'America/Los_Angeles');
/* name of the cookie used to manage the session
   (store language and perpage settings), define CSRF token
 */
nsconddefine('SESSION_NAME', "yioopbiscuit");
/* Name the site goes by: it heads each page's title and labels the logo.
   A routed domain may be given a name of its own on the Appearance
   activity, and this is what every other host the site answers on is
   called.
 */
nsconddefine('SITE_NAME', "Yioop");
/* Short phrase saying what the site is, shown before the site's name in
   the title of a page that gives itself no title. A routed domain may be
   given a motto of its own on the Appearance activity, and this is what
   every other host the site answers on says.
 */
nsconddefine('SITE_MOTTO', "This Search Engine");
/* Whether the site offers light and dark modes. Like the other
   appearance fields this is profile-backed: the admin toggles it in
   Appearance, updateProfile writes it into Profile.php as an nsdefine
   and updates the p() cache, and this nsconddefine only fills in an
   install whose Profile.php predates the field, so the toggle takes
   effect without a server restart. Off leaves the site in its single
   built-in appearance. */
nsconddefine('USE_COLOR_SCHEME', false);
/* Name of the folder under the app css folder holding the themes an
   admin has added. Every domain a site serves picks the theme it wears
   from these, so there is one folder rather than one per domain; it sits
   apart from the app css folder itself so that what an admin has added is
   separable from the stylesheets Yioop ships.
 */
nsconddefine('DEFAULT_THEME_FOLDER', "default");
nsconddefine('CSRF_TOKEN', "YIOOP_TOKEN");
/* max number of devices a user can simultaneously be logged in from */
nsconddefine('MAX_DEVICES', 5);
nsconddefine('RESOURCE_CACHE_TIME', "" . ONE_HOUR);
nsconddefine('AUTOLOGOUT', "" . ONE_HOUR);
/**
 * Whether a failure while a request is handled is let out of the web
 * server rather than caught. Caught, it is written down and only the one
 * connection is let go, so the server carries on for everyone else. Let
 * out, it ends the server, which is what someone tracking such a failure
 * down wants to see, since a request that answers with nothing gives
 * little away.
 */
nsconddefine('NO_WRAP_REQUESTS', false);
nsconddefine('COOKIE_LIFETIME', "" . ONE_YEAR);
/* secure cookies are cookies that can only be sentt over https */
nsconddefine('SECURE_COOKIE', ((baseUrl() == NAME_SERVER) &&
    !empty($_SERVER['HTTPS'])) ? true : false);
/* SameSite policy for the session cookie: Lax lets a top-level navigation
   carry it, so following a link keeps a person signed in, while withholding
   it from cross-site sub-requests, which mitigates CSRF */
nsconddefine('SAME_SITE_COOKIE', "Lax");
/** locations that ads can be placed in search result pages */
nsconddefine('AD_LOCATION', "none");
/** branch name a new git repository page starts its empty repository on */
nsconddefine('GIT_DEFAULT_BRANCH', "master");
/** largest single object a client may push into a git page, 0 for no cap */
nsconddefine('GIT_MAX_BLOB_SIZE', "0");
/** largest pack a client may push into a git page, 0 for no cap */
nsconddefine('GIT_MAX_PUSH_SIZE', "0");
/**
 * Default cap in bytes on the resources a single wiki page may use, including
 * a git page's repository, with -1 meaning no cap. This is the site-wide
 * analog of the per-role Resource Memory per Page limit and replaces the
 * retired GIT_MAX_REPO_SIZE setting.
 */
nsconddefine('MAX_PAGE_RESOURCE_MEMORY', -1);
/**
 * EDITABLE_RESOURCE_EXTENSIONS lists the file name endings that a file
 * kept beside a wiki page may be opened in the writing box under. A
 * reader clicks a file in a page's folder and the site shows its
 * contents in a text area; a file whose ending is not in this list is
 * handed over to be kept instead, since showing the bytes of a picture
 * or a video in a text area and saving them back would ruin the file.
 * The three screens that offer the writing box read this one list, so
 * the mark that opens a file and the code that opens it always agree.
 * The ending is compared as it is written, so an upper-case ending
 * belongs here in upper case.
 */
nsdefine('EDITABLE_RESOURCE_EXTENSIONS', ['bash', 'c', 'cc', 'conf',
    'cpp', 'csh', 'css', 'csv', 'h', 'html', 'ini', 'java', 'js', 'json',
    'ksh', 'md', 'P', 'php', 'pl', 'py', 'rb', 'rs', 'sh', 'sql', 'srt',
    'tex', 'toml', 'ts', 'txt', 'xml', 'yaml', 'yml', 'zsh']);
/**
 * EDITABLE_IMAGE_EXTENSIONS lists the file name endings that a picture
 * kept beside a wiki page may be opened for drawing on under. A reader
 * clicks such a file in a page's folder and the site draws it on a
 * canvas the writer may change, rather than in the writing box, since a
 * picture is not text. The ending is compared in lower case. A picture
 * kept in one of these endings is written back in the same ending when
 * saved, so a kind a browser canvas cannot write back is left out.
 */
/**
 * MAX_NEW_IMAGE_SIDE is the largest a side of a new picture may be, in
 * pixels, when a writer asks the file list for one. It keeps a mistyped
 * size from asking the server for a picture too large to make.
 */
nsdefine('MAX_NEW_IMAGE_SIDE', 4000);
/**
 * MESSAGES_PRESENCE_BANDS gives how long ago, in seconds, somebody was
 * last at their messages screen for each mark shown beside their name,
 * from the freshest to the stalest. A contact seen within the first is
 * shown as there, within the second as lately there, and so on; one seen
 * longer ago than the last gets the plain mark. Each edge is where the
 * matching part of the screen's asking schedule ends, so a contact whose
 * screen is asking on that part cannot read staler than its mark: five
 * asks three seconds apart reach fifteen seconds, five more fifteen apart
 * reach ninety, two thirty apart reach a hundred and fifty, and two a
 * minute apart reach two hundred and seventy.
 */
nsdefine('MESSAGES_PRESENCE_BANDS', ['here' => 15, 'lately' => 90,
    'a-while' => 150, 'long' => 270]);
nsdefine('EDITABLE_IMAGE_EXTENSIONS', ['png', 'jpg', 'jpeg', 'gif',
    'webp', 'bmp']);
date_default_timezone_set(TIMEZONE);
if ((DEBUG_LEVEL & ERROR_INFO) == ERROR_INFO) {
    error_reporting(-1);
} else {
    error_reporting(0);
}
/** if true tests are displayable */
nsdefine('DISPLAY_TESTS', ((DEBUG_LEVEL & TEST_INFO) == TEST_INFO));
/** if true query statistics are displayed */
nsconddefine('QUERY_STATISTICS', ((DEBUG_LEVEL & QUERY_INFO) == QUERY_INFO));
/*
 * Various groups and user ids. These must be nsdefined before the
 * profile check and return below
 */
/** ID of the root user */
nsdefine('ROOT_ID', 1);
/**User name of the root user. If you want to change this, change
 the value in LocalConfig.php, then run the Createdb.php script. You
 should do this before you have much data in your system. */
nsconddefine('ROOT_USERNAME', "root");
/** Role of the root user */
nsdefine('ADMIN_ROLE', 1);
/** Default role of an active user */
nsdefine('USER_ROLE', 2);
/** Default role of an advertiser */
nsdefine('BUSINESS_ROLE', 3);
/** Default role of a bot user */
nsdefine('BOT_ROLE', 4);
/** Default role of a developer, holding only the Feeds and Wikis activity */
nsdefine('DEVELOPER_ROLE', 5);
/** ID of the group to which all Yioop users belong */
nsdefine('PUBLIC_GROUP_ID', 2);
/** ID of the non logged in person will be */
nsdefine('PUBLIC_USER_ID', 2);
/** ID of the group to which all Yioop Help Wiki articles belong */
nsdefine('HELP_GROUP_ID', 3);
/**
 * What the group holding the help articles is called. A site's groups
 * are numbered in the order they were made, so which number the help
 * group has depends on how the site was set up; its name does not.
 * Anything looking for the help articles asks for this name and falls
 * back on the number above only where no group answers to it.
 */
nsconddefine('HELP_GROUP_NAME', 'Help');
/**
 * ID of the group to search sidebar wiki pages and edited search results
 * belong.
 */
nsconddefine('SEARCH_GROUP_ID', 4);
/*ID of the group for moderators */
nsconddefine('MODERATION_GROUP_ID', 5);
/**
 * Fallback flag threshold, used when a site has not set its own
 * MODERATION_FLAG_THRESHOLD yet. This is a plain constant rather than a
 * profile setting, so a blank saved threshold still resolves to a sensible
 * number instead of an empty value.
 */
nsdefine('MODERATION_FLAG_THRESHOLD_DEFAULT', 3);
/**
 * Number of times a post needs to be flagged before it is sent to
 * the moderation group
 */
nsconddefine('MODERATION_FLAG_THRESHOLD',
    MODERATION_FLAG_THRESHOLD_DEFAULT);
/**
 * Whether flagged group items are routed to the moderation group at all. When
 * false, reaching the flag threshold no longer sends an item to moderation,
 * turning group moderation off site-wide.
 */
nsconddefine('USE_MODERATION', true);
/**
 * Constant used to indicate a thread in the moderation group that does not
 * refer to a flagged item. (A general discussion thread)
 */
nsdefine('MODERATION_GENERAL', 0);
/**
 * Constant used to indicate a moderation item has been flagged threshold
 * number of times but not yet moderated
 */
nsdefine('MODERATION_FLAGGED', 1);
/**
 * Constant used to indicate the moderation item was moderated and
 * cleared (deemed okay)
 */
nsdefine('MODERATION_CLEARED', 2);
/**
 * Constant used to indicate the moderation item was moderated and
 * blocked/removed
 */
nsdefine('MODERATION_REMOVED', 3);
/** Length of advertisement name string */
nsdefine('ADVERTISEMENT_NAME_LEN', 25);
/** Length of advertisement text description */
nsdefine('ADVERTISEMENT_TEXT_LEN', 35);
/** Length of advertisement keywords */
nsdefine('ADVERTISEMENT_KEYWORD_LEN', 60);
/** Length of advertisement date */
nsdefine('ADVERTISEMENT_DATE_LEN', 20);
/** Length of advertisement destination */
nsdefine('ADVERTISEMENT_DESTINATION_LEN', 60);
/** value used to create advertisement*/
nsdefine('ADVERTISEMENT_ACTIVE_STATUS', 1);
/** value used to stop advertisement campaign */
nsdefine('ADVERTISEMENT_DEACTIVATED_STATUS',2);
/** value used to admin suspend advertisement campaign */
nsdefine('ADVERTISEMENT_SUSPENDED_STATUS',3);
/** value used to indicate campaign completed successfully */
nsdefine('ADVERTISEMENT_COMPLETED_STATUS',4);
/*
    The timing constants below are normally defined further down, in the
    section that only runs once the instance has a profile. They are
    repeated here, ahead of the not-yet-configured early return, because
    a fresh install runs the server (and, for a background start, its
    auto-restart after the work directory is created) before any profile
    exists. Without these the daemon keep-alive and the restart-status
    page reference constants that are not defined yet and the process
    stops. The definitions are conditional, so the copies further down
    simply have no effect once these are set.
 */
nsconddefine('PROCESS_TIMEOUT', 15 * ONE_MINUTE);
/**
 * How recently a daemon lock file that records no process id must have
 * been written for the daemon to still be treated as running. The
 * normal case records the process id in the lock and is checked
 * directly, with no waiting; this short grace only applies to a lock
 * with no recorded id (one left by an older release, or written where
 * the process id could not be read). It is kept much shorter than
 * PROCESS_TIMEOUT so a stale lock left by a crashed or Ctrl-C-ended
 * server does not block the next start for long.
 */
nsconddefine('STALE_LOCK_TIMEOUT', ONE_MINUTE);
nsconddefine('RESTART_STATUS_POLL_TIME', 5000);
nsconddefine('OWN_SERVER_DAEMON_DRAIN_TIMEOUT', 15);
/** Number of random bytes used to make a site's AUTH_KEY secret, 256 bits */
nsdefine('AUTH_KEY_NUM_BYTES', 32);
if (!PROFILE) {
    return;
}
/*+++ End machine generated code, feel free to edit the below as desired +++*/
/** This is the User-Agent name the crawler provides
 * a web-server it is crawling
 */
if (defined("seekquarry\\yioop\\configs\REDIRECTS_ON") &&
    REDIRECTS_ON) {
    nsconddefine('USER_AGENT',
        'Mozilla/5.0 (compatible; '.USER_AGENT_SHORT.'; +'.NAME_SERVER.'bot)');
    $name_server_url = NAME_SERVER;
} else {
    $base_url = baseUrl();
    $name_server_url =  (NAME_SERVER . "src/" == $base_url ||
        NAME_SERVER . "/src/" == $base_url ||
        str_ends_with(NAME_SERVER, "src/") ) ? $base_url : NAME_SERVER;
    nsconddefine('USER_AGENT',
        'Mozilla/5.0 (compatible; ' .
        USER_AGENT_SHORT.'; +' . NAME_SERVER . 'bot.php)');
}
/**
 * This is the User-Agent name the crawler provides for hosts
 * in the list USER_AGENT_ALTERNATIVE_HOSTS
 */
nsconddefine('USER_AGENT_ALTERNATIVE', "Mozilla/5.0");
/**
 * An array of hosts to use the alternative user agent for
 */
nsconddefine('USER_AGENT_ALTERNATIVE_HOSTS', []);
/**
 * SEARCHBAR_PATH is the address the description of this site's search is
 * served from, which a browser reads to offer this site as a search of
 * its own. It is built from the address of the name server. Set it in
 * LocalConfig.php to serve that description from somewhere else.
 */
nsconddefine('SEARCHBAR_PATH', $name_server_url . "yioopbar.xml");
/** maximum size of a log file before it is rotated */
nsconddefine("MAX_LOG_FILE_SIZE", 5000000);
/** number of log files to rotate amongst */
nsconddefine("NUMBER_OF_LOG_FILES", 5);
/**
 * how long in seconds to keep a cache of a robot.txt
 * file before re-requesting it
 */
nsconddefine('CACHE_ROBOT_TXT_TIME', ONE_DAY);
/**
 * how long in seconds to keep a cache of dns address
 * file before re-requesting it
 */
nsconddefine('CACHE_DNS_TIME', ONE_DAY);
/**
 * QueueServer cache's in ram up to this many robots.txt files
 * to speed up checking if a url is okay to crawl. All robots.txt
 * files are kept on disk, but might be slower to access if not in cache.
 */
nsconddefine('SIZE_ROBOT_TXT_CACHE', 1000);
/**
 * crawl.ini robots_txt behavior value used to indicate that robots.txt
 * files should always be followed
 */
nsdefine('ALWAYS_FOLLOW_ROBOTS', 1);
/**
 * crawl.ini robots_txt behavior value used to indicate that robots.txt
 * files should always be  followed except if the page is a landing page, in
 * which case it is okay to crawl it.
 */
nsdefine('ALLOW_LANDING_ROBOTS', 2);
/**
 * crawl.ini robots_txt behavior value used to indicate that robot.txt files
 * can be ignored and not followed.
 */
nsdefine('IGNORE_ROBOTS', 3);
/**
 * Whether the scheduler should track ETag and Expires headers.
 * If you want to turn this off set the variable to false in
 * LocalConfig.php
 */
nsconddefine('USE_ETAG_EXPIRES', true);
/**
 * if the robots.txt has a Crawl-delay larger than this
 * value don't crawl the site.
 * maximum value for this is 255
 */
nsconddefine('MAXIMUM_CRAWL_DELAY', 64);
/** maximum fraction of URLS in the Queue that are crawl-delayed and waiting
 * before delete from queue new crawl-delayed urls
 */
nsconddefine('WAITING_URL_FRACTION', 0.1);
/** Sitemap urls (ending .gz, .bz. xml) are added to host budgeting queue
 * with this penalty to their tier level. (So if tier would have been x
 * with penalty will be x + SITEMAP_TIER_PENALTY)
 */
nsconddefine('SITEMAP_TIER_PENALTY', 4);
/**  largest sized object allowed in a web archive (used to sanity check
 *  reading data out of a web archive)
 */
nsconddefine('MAX_ARCHIVE_OBJECT_SIZE', 100000000);
/** Largest posting string, in bytes, getPostingsString will read for a
 *  single term in a single index partition. A dictionary entry asking for
 *  more than this is treated as corrupt and skipped rather than read, so a
 *  garbage length cannot make the server allocate a huge block of memory.
 *  Real posting strings are far smaller than this; raise it only if the
 *  periodic memory log's max-posting-read figure approaches it.
 */
nsconddefine('MAX_POSTING_READ_LEN', 32 * 1024 * 1024);
/** Largest number of bytes a single blob may be when it is read out of a
 * BPlusTree or PartitionDocumentBundle partition file. A read asks PHP
 * for the whole length before it reads a byte, so a length that has been
 * corrupted asks for that much memory whatever the file holds; a length
 * past this is treated as corrupt, logged, and skipped.
 */
nsconddefine('MAX_BLOB_READ_LEN', 32 * 1024 * 1024);
/** File ending used for the moving thumbnail of a video, added to the
 * video's own name. Yioop's video library writes these as animated WebP,
 * so the ending has to end in .webp: WebSite::mimeType reads a file's
 * type from the last characters of its name rather than from its bytes,
 * and a name ending .gif is served as image/gif whatever it holds.
 */
nsconddefine('MOVING_THUMB_ENDING', '.animated.webp');
/** Value of the t request variable that asks for a video's moving
 * thumbnail rather than its still one.
 */
nsconddefine('MOVING_THUMB_ARG', 'athumb');
/** Treat earlier timestamps as being indexes of format version 0 */
nsconddefine('VERSION_0_TIMESTAMP', 1369754208);
/** Treat earlier timestamps as being indexes of format version 1 */
nsconddefine('VERSION_1_TIMESTAMP', 1528045371);
/** What version format to use for default indexing **/
nsconddefine('DEFAULT_CRAWL_FORMAT', 3);
/** 1 Gigibyte (GiB)*/
nsdefine('ONE_GIB', 1073741824);
/**
 * defineMemoryProfile works out how much memory this machine has and
 * sets the limits that depend on it, so a small machine and a large one
 * each get bounds they can meet.
 */
function defineMemoryProfile()
{
    /* assume have at least 4GiB */
    $memory = 4 * ONE_GIB;
    if (strstr(PHP_OS, "WIN")) {
        if (function_exists("exec")) {
            exec('wmic memorychip get capacity', $memory_array);
            if ($memory_array) {
                $memory = array_sum($memory_array);
            }
        }
    } else if (stristr(PHP_OS, "LINUX")) {
        set_error_handler(null);
        $mem_data = @file_get_contents("/proc/meminfo");
        set_error_handler(NS_CONFIGS . "yioop_error_handler");
        if (!empty($mem_data)) {
            $data = preg_split("/\s+/", $mem_data);
            $memory = 1024 * intval($data[1]);
        }
    } else if (stristr(PHP_OS, "DARWIN")) {
        exec('/usr/sbin/sysctl hw.memsize', $memory_array);
        if (!empty($memory_array)) {
            preg_match("/\d+/", $memory_array[0], $mem_matches);
            $memory = $mem_matches[0];
        }
    }
    $memory_factor = ceil($memory / (2 * ONE_GIB));
    nsdefine('MEMORY_PROFILE', min(4, $memory_factor));
    nsdefine('SYSTEM_RAM', $memory);
}
/* Check system memory then set up limits for processes based on this */
defineMemoryProfile();
/** Max memory index.php can use */
nsconddefine('INDEX_FILE_MEMORY_LIMIT', ceil(MEMORY_PROFILE/2) . "000M");
/** Max memory a QueueServer can use */
nsconddefine('QUEUE_SERVER_MEMORY_LIMIT', MEMORY_PROFILE . "000M");
/** Max memory a Fetcher can use */
nsconddefine('FETCHER_MEMORY_LIMIT', ceil(MEMORY_PROFILE/1.5) . "000M");
/** Max memory a MediaUpdater can use */
nsconddefine('MEDIA_UPDATER_MEMORY_LIMIT', ceil(MEMORY_PROFILE/2) . "000M");
/** Max memory a Mirror can use */
nsconddefine('MIRROR_MEMORY_LIMIT', ceil(MEMORY_PROFILE/2) ."000M");
/** Max memory a ClassifierTrainer can use */
nsconddefine('CLASSIFIER_TRAINER_LIMIT', ceil(MEMORY_PROFILE/4) ."000M");
/** Max memory a QueueServer can use */
nsconddefine('ARC_TOOL_MEMORY_LIMIT', (2 * MEMORY_PROFILE) . "000M");
/** TOKEN_TOOL_MEMORY_LIMIT is the most memory TokenTool may use.
 *  That tool builds the word lists and the other files a locale needs,
 *  which it does by reading whole dictionaries into memory, so it is
 *  given a share of what the machine has rather than a fixed figure.
 */
nsconddefine('TOKEN_TOOL_MEMORY_LIMIT', ceil(MEMORY_PROFILE/2) . "000M");
/** Used to control fraction of memory filled of current process
 *  (usually Fetcher or QueueServer) before action (such as switch shard)
 *  on current class (usually IndexArchiveBundle) is taken.
 */
nsconddefine('MEMORY_FILL_FACTOR', 0.7);
/**
 * Used to control fraction of memory filled before FetchUrl stops trying
 * to download pages in getPages. This is higher than MEMORY_FILL_FACTOR to
 * allow communication between processes even after the indexer and
 * scheduler are complaining memory exhansted. It is less than all memory
 * to try to prevent processes from just crashing due to lack of memory.
 */
nsconddefine('MEMORY_FILL_FACTOR_HIGH', 0.8);
/**
 * bloom filters are used to keep track of which urls are visited,
 * this parameter determines up to how many
 * urls will be stored in a single filter. Additional filters are
 * read to and from disk.
 */
nsconddefine('URL_FILTER_SIZE', MEMORY_PROFILE * 5000000);
/**
 * maximum number of urls that will be held in ram
 * (as opposed to in files) in the priority queue
 */
nsconddefine('NUM_URLS_QUEUE_RAM', MEMORY_PROFILE * 80000);
/** number of documents before next gen */
nsconddefine('NUM_DOCS_PER_PARTITION', 4 * MEMORY_PROFILE * 10000);
/** precision to round floating points document scores */
nsconddefine('PRECISION', 10);
/** maximum number of links to extract from a page on an initial pass*/
nsconddefine('MAX_LINKS_TO_EXTRACT', MEMORY_PROFILE * 80);
/** maximum number of top level links to extract for a host url web page*/
nsconddefine('MAX_TOP_LEVEL_LINKS', 6);
/** Estimate of the average number of links per page a document has*/
nsconddefine('AVG_LINKS_PER_PAGE', 24);
/**  minimum char length of link text before gets its own document */
nsconddefine('MIN_LINKS_TEXT_CHARS', 3);
/**  maximum number of chars for link text to use for any given url on
 *   page. As an example suppose the the url https://www.yahoo.com/
 *   appears twice of a page, first with with link text "Yahoo!" and
 *   the second time with text "Web Portal", the total useful link text
 *   for https://www.yahoo.com/ is "Yahoo! Web Portal" or 18 characters.
 *   MAX_LINKS_TEXT_CHARS serves as an upper bound on this useful link text
 *   after which further text on the same page to the same url will be trimmed.
 */
nsconddefine('MAX_LINKS_TEXT_CHARS', 100);
/**  maximum length of urls to try to queue, this is important for
 *  memory when creating schedule, since the amount of memory is
 *  going to be greater than the product MAX_URL_LEN*MAX_FETCH_SIZE
 *  text_processors need to promise to implement this check or rely
 *  on the base class which does implement it in extractHttpHttpsUrls
 */
nsconddefine('MAX_URL_LEN', 2048);
/** request this many bytes out of a page -- this is the default value to
 * use if the user doesn't set this value in the page options GUI
 */
nsdefine('PAGE_RANGE_REQUEST', 1000000);
/**
 * When getting information from an index dictionary in word iterator
 * for a  version < 3 index how many distinct generations to read in in one go
 */
nsconddefine('NUM_DISTINCT_GENERATIONS', 20);
/**
 * Used in computing the DOC_RANK for version < 3 indexes when a going
 * through index in descending
 * fashion.  It represents an upper bound on the maximum number of
 * generations an IndexArchiveBundle should have
 */
nsconddefine('MAX_GENERATIONS', 10000);
/**
 * Max number of chars to extract for description from a page to index.
 * Only words in the description are indexed. -- this is the default value
 * can be set in Page Options
 */
nsdefine('MAX_DESCRIPTION_LEN', 2000);
/**
 * Allow pages to be recrawled after this many days -- this is the
 * default value to use if the user doesn't set this value in the page options
 * GUI. What this controls is how often the page url filter is deleted.
 * A nonpositive value means the filter will never be deleted.
 */
nsdefine('PAGE_RECRAWL_FREQUENCY', -1);
/**
 *  Max download attempts for a given non crawl-delayed url before given
 *  up for given fetch batch before giving up
 */
nsdefine('MAX_DOWNLOAD_ATTEMPTS', 3);
/** number of multi curl page requests in one go */
nsconddefine('NUM_MULTI_CURL_PAGES', 100);
/** Fetch batch window size as divided by number of active fetchers*/
nsconddefine('FETCHER_NUM_WINDOW_SCALE', 2);
/** number of pages to extract from an archive in one go */
nsconddefine('ARCHIVE_BATCH_SIZE', 100);
/** time in seconds before we give up on multi page requests*/
nsconddefine('PAGE_TIMEOUT', 10);
/** time in seconds before we give up on a page request whose host is
    one of our own addresses but which was not answered in process. Such
    a request is a loopback to this single-process server and cannot
    complete while the server is busy with the current request, so it is
    kept short to fail fast rather than freezing every connection for the
    full page timeout. */
nsconddefine('LOOPBACK_PAGE_TIMEOUT', 2);
/** time in seconds before we give up on a single page request*/
nsconddefine('SINGLE_PAGE_TIMEOUT', (ONE_MINUTE/2));
/** max time in seconds in a process before write a log message if
 *  crawlTimeoutLog is called repeatedly from a loop
 */
nsconddefine('LOG_TIMEOUT', 30);
/** max time in seconds a message counted with crawlLogCounter waits
 *  before it is written out with how many times it happened. A message
 *  is counted rather than written when it can repeat thousands of times
 *  a second, as a reader of a broken index does, so this is much longer
 *  than LOG_TIMEOUT: five minutes of such a fault is one entry.
 */
nsconddefine('LOG_COUNTER_TIMEOUT', 300);
/** Number of lines of QueueServer log file to check to make sure both
 *  Indexer and Scheduler are running. 6000 should be roughly 20-30 minutes
 */
nsconddefine('LOG_LINES_TO_RESTART', 6000);
/** File name of file used to record last log lines when a Yioop process has
 * crashed.
 */
nsconddefine('CRASH_LOG_NAME', LOG_DIR . "/YioopCrashes.log");
/**
 * Maximum time a crawl daemon process can go before calling.
 * This is also the amount of time in log files of a Indexer or Scheduler
 * without a message before the process is deemed dead.
 * @see CrawlDaemon::processHandler
 */
nsconddefine('PROCESS_TIMEOUT', 15 * ONE_MINUTE);
/**
 * How recently a daemon lock file that records no process id must have
 * been written for the daemon to still be treated as running. The
 * normal case records the process id in the lock and is checked
 * directly, with no waiting; this short grace only applies to a lock
 * with no recorded id. Kept much shorter than PROCESS_TIMEOUT so a
 * stale lock left by a crashed or Ctrl-C-ended server does not block
 * the next start for long.
 * @see CrawlDaemon::lockHeldByLiveProcess
 */
nsconddefine('STALE_LOCK_TIMEOUT', ONE_MINUTE);
/**
 * Seconds without a heartbeat update before a per-folder podcast
 * download marker is treated as stale rather than active. The
 * PodcastDownloadJob refreshes a folder's marker as it downloads;
 * if the updater dies mid-download the marker stops advancing, and
 * after this long the Media List page stops showing a live
 * "downloading" status and offers the update button again.
 */
nsconddefine('PODCAST_DOWNLOAD_STALE_TIME', 3 * ONE_MINUTE);
/**
 * Milliseconds between polls the Media List page makes to refresh a
 * folder's podcast download status, switching its indicator between
 * downloading and idle without a full page reload.
 */
nsconddefine('PODCAST_STATUS_POLL_TIME', 10000);
/**
 * How long in milliseconds the browser waits before re-opening the
 * message-status event stream after the server renders the conversation
 * and closes it. The conversation refresh works by the browser holding an
 * EventSource open on the status endpoint; the server renders any new
 * messages, sends them, and closes, and the browser reconnects after this
 * interval to pick up the next ones. A shorter interval means a message
 * the other party sends shows sooner, at the cost of more reconnects; one
 * second keeps the wait short while the always-on server does no work
 * between reconnects when there is nothing new. A held-open push that
 * removes the reconnect entirely rides with the WebSocket work, where the
 * server can hold a socket without blocking its request loop.
 */
nsconddefine('USER_MESSAGE_STATUS_POLL_TIME', 5000);
/**
 * How often in seconds a held-open message WebSocket checks for messages
 * newer than the last it pushed and sends any it finds. Unlike the
 * event-stream reconnect interval above, which is a millisecond hint the
 * browser waits before re-opening a closed stream, this is a server-side
 * timer period on an open socket, so it is expressed in whole seconds and
 * fed straight to the server's timer. One second keeps a message's delay
 * short while the check stays cheap; the socket staying open is what makes
 * this feel immediate next to the reconnect path.
 */
nsconddefine('USER_MESSAGE_WS_PUSH_TIME', 1);
/**
 * Filename (under LOG_DIR) of the heartbeat written by the
 * launcher when Yioop runs under its own WebSite, i.e. when
 * invoked as `php index.php terminal ...`. Daemons that want
 * to die with their WebSite parent (rather than orphan
 * themselves and hold sockets open) poll this file.
 */
nsconddefine('WEBSITE_HEARTBEAT_FILE', 'web_beat.txt');
/**
 * Seconds between heartbeat writes from the launcher's
 * repeating timer. Short enough that the Ctrl-C-then-restart
 * developer workflow feels snappy, long enough to avoid
 * burning syscalls during normal operation.
 */
nsconddefine('WEBSITE_HEARTBEAT_WRITE_INTERVAL', 2);
/**
 * Seconds the own-web-server boot waits before relaunching the
 * background daemons (MediaUpdater, MailServer) that a Restart
 * Server action or profile change had stopped. The short delay
 * lets the replacement web server finish binding its ports first,
 * so the relaunched daemons come up against a server that is
 * already accepting connections.
 */
nsconddefine('OWN_SERVER_DAEMON_RELAUNCH_DELAY', 3);
/** Seconds to wait for a daemon to exit on its own after a stop
 *  signal during a graceful shutdown before escalating to a SIGTERM
 *  (when POSIX is available) and clearing its lock
 */
nsconddefine('OWN_SERVER_DAEMON_DRAIN_TIMEOUT', 15);
/** Milliseconds between polls the Restart Server page makes to the
 *  restart-status route while the server restarts and its daemons
 *  drain and relaunch
 */
nsconddefine('RESTART_STATUS_POLL_TIME', 5000);
/** Number of seconds of no fetcher contact before crawl is deemed dead
 *  The files C\SCHEDULES_DIR . "/{$this->channel}-" .
 *  L\CrawlConstants::crawl_status_file
 *  are used to determine if CRAWL_TIMEOUT reached.
 *  This is modified by QueueServer::writeAdminMessages only when
 *  the crawl state (waiting/start crawl/ shutdown, etc) changes.
 *  It is also updated when a fetcher sends an update command to
 *  FetchController when sends (schedule, robot, etag, or index info). Hence,
 *  if no data is sent by a fetcher to a queue server for a long time the
 *  crawl is likely stalled
 */
nsconddefine("CRAWL_TIMEOUT", 2 * PROCESS_TIMEOUT);
/**
 * Delay in microseconds between processing pages to try to avoid
 * CPU overheating. On some systems, you can set this to 0.
 */
nsconddefine('FETCHER_PROCESS_DELAY', 10000);
/**
 * Number of error page 400 or greater seen from a host before crawl-delay
 * host and dump remainder from current schedule
 */
nsconddefine('DOWNLOAD_ERROR_THRESHOLD', 50);
/** Crawl-delay to set in the event that DOWNLOAD_ERROR_THRESHOLD exceeded*/
nsconddefine('ERROR_CRAWL_DELAY', 20);
/**
 * The maximum time limit in seconds where if a file is not converted by the
 * time it will be picked up again by the client media updater
 * This value largely depends on the no of client media updaters that we have
 * and also the maximum video size that would be uploaded to yioop.
 * This value should be kept more than the sleeping time of media updater
 * loop to avoid conversion of same file multiple times.
 */
nsconddefine('MAX_FILE_TIMESTAMP_LIMIT', 600);
/**
 * Path to Whisper binary for audio transcription
 * Set this to enable automatic transcription of uploaded audio files.
 * If you install whisper with brew on macOS the path is likely
 * /opt/homebrew/bin/whisper
 */
nsconddefine('WHISPER', '');
/**
 * Path to FFMPEG binary for audio/video processing
 * Used for audio format conversion and video processing
 * If you install ffmpeg with brew on macOS the path is likely
 * /opt/homebrew/bin/ffmpeg
 */
nsconddefine('FFMPEG', '');
/**
 * Maximum size in bytes for audio files that can be transcribed
 */
nsconddefine('MAX_AUDIO_TRANSCRIBE_SIZE', 100000000);
/**
 * Timeout in seconds for audio transcription processing
 */
nsconddefine('TRANSCRIPTION_TIMEOUT', 1800);
/**
 * Maximum length of transcript content to display in browser
 */
nsconddefine('MAX_TRANSCRIPT_DISPLAY_LENGTH', 50000);
/**
 * Mail scheduled for delivery by yioop is aggregated in a text file until
 * MAIL_AGGREGATION_TIME seconds has passed. At this point all the mail
 * in thhe aggregation file is sent and a new aggregation file is started.
 */
nsconddefine('MAIL_AGGREGATION_TIME', 30);
/**
 * Seconds between sweeps of the MailSite outbound spool
 * (MAIL_DIR/outbound). Each authenticated submission to an
 * external recipient is queued there and delivered in the
 * background by direct MX so the single-threaded mail event loop
 * never blocks on a remote handshake. Defaults to the heartbeat
 * write interval so the drain rides at the same cadence as the
 * daemon's other periodic work.
 */
nsconddefine('MAIL_OUTBOUND_DRAIN_INTERVAL',
    WEBSITE_HEARTBEAT_WRITE_INTERVAL);
/**
 * PHP memory_limit applied by the long-running MailServer daemon
 * at start-up. The mail daemon is a persistent process that holds
 * per-folder message metadata for IMAP clients; a mailbox with
 * tens of thousands of messages needs far more headroom than the
 * 128M web-request default, so the daemon raises its own limit
 * here. A plain integer is bytes; a value like '1G' or '512M' uses
 * the usual PHP shorthand. Tune down on a memory-constrained host.
 */
nsconddefine('MAIL_SERVER_MEMORY_LIMIT', '1G');
/**
 * Default edge size of square image thumbnails in pixels
 */
nsconddefine('THUMB_DIM', 256);
/**
 * Maximum size of a user thumb file that can be uploaded
 */
nsconddefine('THUMB_SIZE', 1000000);
/** Characters we view as not part of words, not same as POSIX [:punct:]*/
nsconddefine('PUNCT', "\.|\,|\:|\;|\"|\'|\[|\/|\%|\?|-|\^" .
    "\]|\{|\}|\(|\)|\!|\||।|\&|\`|" .
    "\’|\‘|©|®|™|℠|…|\/|\>|，|\=|。|）|：|、|" .
    "”|“|《|》|（|「|」|★|【|】|·|\+|\*|；".
        "|！|—|―|？|！|،|؛|؞|؟|٪|٬|٭|\‚|\‘");
/** Maximum number of characters to use for a title from a string
 *  of text that begins with something like a title
 */
nsconddefine('AD_HOC_TITLE_LENGTH', 50);
/** Maximum number of simultaneous crawls (each concurrent crawl gets one
 * channel
 */
nsconddefine('MAX_CHANNELS', 10);
/** Used to say number of bytes in histogram bar (stats page) for file
 * download sizes
 */
nsconddefine('DOWNLOAD_SIZE_INTERVAL', 5000);
/** Used to say number of secs in histogram bar for file download times*/
nsconddefine('DOWNLOAD_TIME_INTERVAL', 0.5);
/** maximum number of urls to schedule to a given fetcher in one go
 *  Fetcher needs enough memory to hold all these files in memory.
 *  So took the FETCHER_MEMORY_LIMIT divided by 16000000 (roughly
 *  twice the average size of an image on the web) and worked this out
 *  as multiple of MEMORY_PROFILE
 */
nsconddefine('MAX_FETCH_SIZE', ceil(MEMORY_PROFILE * 416));
/**
 * A second way to ensure that not too much memory is consumed in the
 * fetcher by downloaded web pages is to try to estimate on the high side
 * the number bytes each url document is like to consume. This is done in
 * UrlParser::guessFileSizeFromUrl($url). One can sum the estimated bytes for
 * the urls in a fetch batch and check that it is less than MAX_FETCH_WEIGHT
 * below. (Set to 0.25 of the Fetcher's memory size)
 */
nsconddefine('MAX_FETCH_WEIGHT', MEMORY_PROFILE * 166666666);
/**
 * maximum number url queue files to process in trying to create a
 * fetch batch from a tier queue
 */
nsconddefine('MAX_FILES_PROCESS_BATCH', 5);
/** fetcher must wait at least this long between multi-curl requests */
nsconddefine('MINIMUM_FETCH_LOOP_TIME', 5);
/** an idling fetcher sleeps this long between queue_server pings*/
nsconddefine('FETCH_SLEEP_TIME', 10);
/** an a queue_server minimum loop idle time*/
nsconddefine('QUEUE_SLEEP_TIME', 5);
/** amount of time before resend a fetch batch that has not come back to the
 * QueueServer
 */
nsconddefine('SCHEDULE_TIMEOUT', 2 * MAX_DOWNLOAD_ATTEMPTS *
    ceil(MAX_FETCH_SIZE / NUM_MULTI_CURL_PAGES) *
    max(PAGE_TIMEOUT, MINIMUM_FETCH_LOOP_TIME));
/**
 * Maximum number of times to attempt to resave a schedule that timeouts.
 */
nsconddefine('MAX_RESCHEDULE_ATTEMPTS', 3);
/** How often mirror script tries to synchronize with machine it is mirroring*/
nsconddefine('MIRROR_SYNC_FREQUENCY', ONE_HOUR);
/**
 * How often mirror script tries to notify machine it is mirroring that it
 * is still alive
 */
nsconddefine('MIRROR_NOTIFY_FREQUENCY', ONE_MINUTE);
/** Max time before current index shard is rebuilt (queue_server) */
nsconddefine('FORCE_SAVE_TIME', 10 * ONE_MINUTE);
/** maximum length of a  search query */
nsconddefine('MAX_QUERY_TERMS', 10);
/** Largest offset into a result list a query may ask for with its limit
 * parameter. The number of results a query gathers, and the number of
 * summaries it then loads, both grow with this offset, so an unbounded
 * limit lets one request ask the server for as much memory as it likes.
 * Nobody pages by hand past a few hundred results; a crawler asking for
 * more is asking for work no reader wants.
 */
nsconddefine('MAX_QUERY_LIMIT', 1000);
/** maximum number of terms allowed in a conjunctive search query */
nsconddefine('MAX_QUERY_LEN', 4096);
/** whether to use question answering system */
nsconddefine('ENABLE_QUESTION_ANSWERING', true);
/** If true, when processing query see if subsets of terms in query form a
 *  known phrase and if so do lookup with that rather than do a conjunctive
 *  query over those terms
 */
/** Number of words until a string of words might be parsed as a sentence
 * for question answering
 */
nsconddefine('PHRASE_THRESHOLD', 3);
/**
 *  Default number of search results to advance/or go back through
 *  search or thread results.
 */
nsconddefine('NUM_RESULTS_PER_PAGE', 10);
/**
 * How the time a page resource was last changed is written in the
 * resource listing, as a format string for PHP's date(). The listing
 * puts this beside the resource's name, and the name is the column a
 * reader is usually reading or renaming, so the time says the day and
 * the minute and leaves the seconds and the offset from UTC out; those
 * cost more than half the column's width and say little about a file.
 */
nsconddefine('RESOURCE_MODIFIED_DATE_FORMAT', 'd M Y H:i');
/** Number of recently crawled urls to display on admin screen */
nsconddefine('NUM_RECENT_URLS_TO_DISPLAY', 10);
/** Maximum time a set of results can stay in query cache before it is
 * invalidated. If negative, then never use time to kick something out of
 * cache.
 */
nsconddefine('MAX_QUERY_CACHE_TIME', 2 * ONE_DAY); /* two days */
/** Minimum time a set of results can stay in query cache before it is
 * invalidated (used for active crawl or feed results)
 */
nsconddefine('MIN_QUERY_CACHE_TIME', ONE_HOUR); /* one hour */
/**
 * Default number of items to page through for users,roles, mixes, etc
 * on the admin screens
 */
nsconddefine('DEFAULT_ADMIN_PAGING_NUM', 50);
/** Maximum number of bytes that the file that the suggest-a-url form
 * send data to can be.
 */
nsconddefine('MAX_SUGGEST_URL_FILE_SIZE', 100000);
/** Maximum number of a user can suggest to the suggest-a-url form in one day
 */
nsconddefine('MAX_SUGGEST_URLS_ONE_DAY', 10);
/** Most names a screen offers at once while somebody is typing into a
 * field that asks for a person, a group or a role.
 */
/** The mark for fetching a file down to the reader's own machine: an
 * arrow pointing down onto a line, drawn rather than taken from the
 * letters a font provides, since the letter meaning an arrow down is
 * drawn differently by each font and reads as an ordinary arrow. It
 * takes the color of the words around it. Both the screen that edits a
 * file and the screen that reads one wear it.
 */
nsconddefine('DOWNLOAD_DRAWING', '<svg viewBox="0 0 24 24" ' .
    'width="1em" height="1em" fill="none" stroke="currentColor" ' .
    'stroke-width="2.5" stroke-linecap="round" ' .
    'stroke-linejoin="round" aria-hidden="true" focusable="false">' .
    '<path d="M12 3v11"/><path d="M7 10l5 5 5-5"/>' .
    '<path d="M4 20h16"/></svg>');
nsconddefine('MAX_SUGGEST_RESULTS', 5);
/** How many letters somebody types before a screen offers any names.
 * Fewer than this and nothing is offered, since two letters match too
 * much of a large site to be worth showing.
 */
nsconddefine('MIN_SUGGEST_LENGTH', 3);
/** How long a screen waits after a keystroke before it asks again for
 * names, in milliseconds, so that typing does not ask on every letter.
 */
nsconddefine('SUGGEST_WAIT', 2000);
/** Directly add suggested urls to crawl options and inject them into any
 *  active crawl. If false, these are stored in a file and the user has to
 *  click a button to add them.
 */
nsconddefine('DIRECT_ADD_SUGGEST', false);
/**
 * Length after which to truncate names for users/groups/roles when
 * they are displayed (not in DB)
 */
nsconddefine('NAME_TRUNCATE_LEN', 7);
/** USER STATUS value used for someone who is not in a group but can browse*/
nsdefine('NOT_MEMBER_STATUS', -1);
/** USER STATUS value used for a user who can log in and perform activities */
nsdefine('ACTIVE_STATUS', 1);
/**
 * USER STATUS value used for a user whose account is created, but which
 * still needs to undergo admin or email verification/activation
 */
nsdefine('INACTIVE_STATUS', 2);
/**
 * USER STATUS used to indicate an account which can no longer perform
 * activities but which might be retained to preserve old blog posts.
 */
nsdefine('SUSPENDED_STATUS', 3);
/** Group status used to indicate a user that has been invited to join
 * a group but who has not yet accepted
 */
nsdefine('INVITED_STATUS', 4);
/** USER STATUS value used for a user who can log in and perform activities
 *  and who has editor privileges in a group
 */
nsdefine('EDITOR_STATUS', 5);
/**
 * Options code used to indicate that a group should parse text using
 * markdown
 */
nsdefine('MARKDOWN_ENGINE', 1);
/**
* Options code used to indicate that a group should parse text using
* mediawiki
 */
nsdefine('MEDIAWIKI_ENGINE', 0);
/** SOCIAL_GROUPS.OPTIONS bit: group's wiki pages are encrypted */
nsdefine('GROUP_OPTION_ENCRYPTED', 1);
/** SOCIAL_GROUPS.OPTIONS bit: the public may view the source and
    history of this group's wiki pages (off keeps the public and
    crawlers out of page source and history) */
nsdefine('GROUP_OPTION_PAGE_SOURCE_ALLOWED', 2);
/** SOCIAL_GROUPS.OPTIONS bit: the public may view this group's list of
    wiki pages (off hides the page listing from the public, so pages
    cannot be enumerated by anyone who is not a member) */
nsdefine('GROUP_OPTION_PAGE_LIST_ALLOWED', 4);
/**
 * Bit of a group's options saying a page in it may set its own theme,
 * header and footer. With it clear, a page takes the group's and the
 * controls for its own are not drawn.
 */
nsdefine('GROUP_OPTION_PAGE_CUSTOMIZE_ALLOWED', 8);
/**
 * Bit of a group's options saying the list of its pages may be seen by a
 * stranger only when the address names a category, so the categories on a
 * front page can be followed without opening every page the group holds.
 * It is read only where the plain page-list bit is clear.
 */
nsdefine('GROUP_OPTION_PAGE_LIST_ARTICLES', 16);
/**
 * What a group's page-list setting holds when the list may be seen only
 * where the address names a category. Unlike the bit above, this is the
 * value the setting takes in a form and in the data a view is given.
 */
nsdefine('GROUP_OPTION_PAGE_LIST_ARTICLES_SETTING', 2);
/** Names the pages the site shows when it cannot answer a request in
    the ordinary way, and the status line sent with each. A page here is
    an ordinary wiki page of the public group, named for the status, so
    what the site says when it turns a request away can be written and
    translated like anything else it says. */
nsdefine('ERROR_PAGE_STATUSES', [
    "400" => "HTTP/1.0 400 Bad Request",
    "403" => "HTTP/1.0 403 Forbidden",
    "404" => "HTTP/1.0 404 Not Found",
    "409" => "HTTP/1.0 409 Conflict"
]);
/**
 * Yioop's messaging system is implemented using a Yioop group for each
 * user called the users personal group. Messages between users
 * correspond to a thread in this personal group's feed with the title
 * PERSONAL_GROUP_PREFIX and then the user the chat is with.
 */
nsdefine('PERSONAL_GROUP_PREFIX', 'Personal$');
/**
 * Character that joins a git repository wiki page's name to an issue number
 * to make the name of the hidden companion page holding that issue's record.
 * A dollar sign is not allowed in a page name a person makes, so a name with
 * one in it can only be one of these companion pages.
 */
nsdefine('GIT_ISSUE_SEPARATOR', '$$');
/**
 * Put between a git repository page's name and the rest of the name of the
 * page holding a report that is waiting for an editor to accept it. It is
 * a different mark from the one a numbered issue uses so that one
 * comparison on the name picks out one kind and leaves the other alone.
 */
nsdefine('GIT_ISSUE_HELD_SEPARATOR', '$?');
/**
 * Put between a git repository page's name and the token standing for a
 * reporter who has been shut out of reporting against it, naming the page
 * that says until when.
 */
nsdefine('GIT_ISSUE_BAN_SEPARATOR', '$~');
/**
 * What a git repository page's waiting reports were all kept in before
 * each got a page of its own. Nothing writes it any more; it is named here
 * only so the upgrade that clears the old ones away can find them.
 */
nsdefine('GIT_ISSUE_OLD_HELD_PAGE', '$anon');
nsdefine('GIT_ISSUE_ANON_NONE', 'none');
/**
 * Anyone may report, but an anonymous report waits for an editor to accept
 * it before it becomes a numbered issue. This is the setting a repository
 * starts with.
 */
nsdefine('GIT_ISSUE_ANON_MODERATED', 'moderated');
/**
 * Anyone may report and an anonymous report becomes a numbered issue at
 * once, without waiting for an editor.
 */
nsdefine('GIT_ISSUE_ANON_UNMODERATED', 'unmoderated');
/**
 * Most anonymous issue reports one address may make in a day. Beyond this
 * the address is turned away until the day is out, which bounds how much one
 * reporter can leave for an editor to read through.
 */
nsconddefine('MAX_ANON_GIT_ISSUES_ONE_DAY', 5);
/**
 * Most reports one repository holds waiting for an editor. A report arriving
 * at a full queue is turned away rather than dropping an older one, so a
 * flood cannot push out reports an editor has not seen yet.
 */
nsconddefine('MAX_HELD_GIT_ISSUES', 200);
/**
 * Longest handle or email address an anonymous reporter may give. It is
 * shown as-is where a username would otherwise be, so it is kept short
 * enough to sit in that column.
 */
nsconddefine('GIT_ISSUE_HANDLE_LEN', 64);
/**
 * How many characters of the hash of an anonymous reporter's address are
 * shown to an editor as that reporter's token. Enough that two reporters
 * are unlikely to share one, short enough to read at a glance, and far too
 * little of the hash to work an address back out of.
 */
nsconddefine('GIT_ISSUE_REPORTER_TOKEN_LEN', 8);
/**
 * The three spans an editor may shut a reporter out for, as the values the
 * form carries. What each means in seconds is worked out when the ban is
 * written, so a ban already given keeps the end it was given.
 */
nsdefine('GIT_ISSUE_BAN_WEEK', 'week');
/** GIT_ISSUE_BAN_MONTH is what a ban of one month is written down as
 *  where a repository's issues are being moderated. It is one of the
 *  lengths listed at GIT_ISSUE_BAN_WEEK, which says how a length is
 *  read back and why a ban already given keeps the end it was given.
 */
nsdefine('GIT_ISSUE_BAN_MONTH', 'month');
/** GIT_ISSUE_BAN_FOREVER is what a ban with no end is written down as
 *  where a repository's issues are being moderated. It is one of the
 *  lengths listed at GIT_ISSUE_BAN_WEEK, which says how a length is
 *  read back and why a ban already given keeps the end it was given.
 */
nsdefine('GIT_ISSUE_BAN_FOREVER', 'forever');
/**
 * Largest number of recent release tags offered as version choices when
 * reporting an issue against a git repository, on top of the current-code
 * choice that always heads the list.
 */
nsdefine('GIT_ISSUE_VERSION_CHOICES', 10);
/**
 * In Yioop each Wiki page has its own set of file/image/etc resources/assets.
 * To move resource between pages, one can cut/copy them to a clipboard,
 * then past to the new location from the clipboard. The clipboard is
 * implemented as a wiki page in the user's personal group. The constant
 * CLIPBOARD_PAGE_NAME is used to indicate how that page should be named.
 */
nsdefine('CLIPBOARD_PAGE_NAME', 'Clipboard');
/**
 * Maximum number of thread items that can be held in the clipboard for
 * discussion groups the user is an owner of of.
 */
nsdefine('MAX_THREAD_CLIPBOARD_ITEMS', 200);
/**
 * Group registration type that only allows people to join a group by
 * invitation
 */
nsdefine('INVITE_ONLY_JOIN', 1);
/**
 * Group registration type that only allows people to request a membership
 * in a group from the group's owner
 */
nsdefine('REQUEST_JOIN', 2);
/**
 * Group registration type that only allows people to request a membership
 * in a group from the group's owner, but allows people to browse the groups
 * content without join
 */
nsdefine('PUBLIC_BROWSE_REQUEST_JOIN', 3);
/**
 * Group registration type that allows anyone to obtain membership
 * in the group
 */
nsdefine('PUBLIC_JOIN', 4);
/**
 * If a group has a fee to join, the fee will e at least this much.
 */
nsdefine('LOW_JOIN_FEE', 20);
/**
 *  Group access code signifying only the group owner can
 *  read items posted to the group or post new items
 */
nsdefine('GROUP_PRIVATE', 1);
/**
 *  Group access code signifying members of the group can
 *  read items posted to the group but only the owner can post
 *  or comment on new items
 */
nsdefine('GROUP_READ', 2);
/**
 *  Group access code signifying members of the group can
 *  read items posted to the group but only the owner can post
 *   new items
 */
nsdefine('GROUP_READ_COMMENT', 3);
/**
 *  Group access code signifying members of the group can both
 *  read items posted to the group as well as post new items
 */
nsdefine('GROUP_READ_WRITE', 4);
/**
 *  Group access code signifying members of the group can both
 *  read items posted to the group as well as post new items
 *  and can edit the group's wiki
 */
nsdefine('GROUP_READ_WIKI', 5);
/**
 * Indicates a group where people can't up and down vote threads
 */
nsdefine("NON_VOTING_GROUP", 0);
/**
 * Indicates a group where people can vote up threads (but not down)
 */
nsdefine("UP_VOTING_GROUP", 1);
/**
 * Indicates a group where people can vote up and down threads
 */
nsdefine("UP_DOWN_VOTING_GROUP", 2);
/**
 *  Typical posts to a group feed are on user created threads and
 *  so are of this type
 */
nsdefine('STANDARD_GROUP_ITEM', 0);
/**
 *  Indicates the thread was created to go alongside the creation of a wiki
 *  page so that people can discuss the pages contents
 */
nsdefine('WIKI_GROUP_ITEM', 1);
/**
 *  Indicates the thread was created to filter items from search results
 */
nsdefine('SEARCH_FILTER_GROUP_ITEM', 2);
/**
 *  Indicates the thread was created to edit items in search results
 */
nsdefine('SEARCH_EDIT_GROUP_ITEM', 3);
/**
 *  Indicates the thread was created to edit a query => url_list item
 */
nsdefine('QUERY_MAP_GROUP_ITEM', 4);
/**
 *  Used to record that a page belongs to the standard category
 */
nsdefine('WIKI_STANDARD_LINK', -1);
/**
 *  Used to record that a page belongs to the template category
 */
nsdefine('WIKI_TEMPLATE_LINK', -2);
/**
 *  Controls whether the BulkEmailJob is used to send mails from the
 *  Yioop instance or if mails are sent from the web app.
 */
nsconddefine('SEND_MAIL_MEDIA_UPDATER', false);
/**
 * Whether the site's own outgoing mail (registration and bulk mail) is
 * handled by the built-in MailSite as the reserved bot account. When
 * true the outgoing-mail connection details are derived from the local
 * MailSite rather than read from the manually entered mail-server
 * fields, so no external relay needs to be configured.
 */
nsconddefine('MAIL_BOT_HANDLED', false);
/**
 * Impression type used to record one view of a thread
 */
nsdefine('THREAD_IMPRESSION', 1);
/**
 * Impression type used to record one view of a wiki page
 */
nsdefine('WIKI_IMPRESSION', 2);
/**
 * Impression type used to record one thread or wiki page view in a group
 */
nsdefine('GROUP_IMPRESSION', 3);
/**
 * Impression type used to record one search query view
 */
nsdefine('QUERY_IMPRESSION', 4);
/**
 * Impression type used to record one search cache request view
 */
nsdefine('CACHE_IMPRESSION', 5);
/**
 * Impression type used to record one view of a wiki page resource
 */
nsdefine('RESOURCE_IMPRESSION', 6);
/**
 * Impression type used to record that the user clicked the
 * a link on a query result page
 */
nsdefine('QUERY_IMPRESSION_EXIT', 7);
/**
 * Number of ITEM_RECOMMENDATIONs to suggest to a user
 */
nsdefine('MAX_RECOMMENDATIONS', 3);
/**
 * Type used to indicate ITEM_TYPE is about a trending thread
 */
nsdefine('TRENDING_RECOMMENDATION', 1);
/**
 * Type used to indicate ITEM_TYPE is about a thread
 */
nsdefine('THREAD_RECOMMENDATION', 2);
/**
 * Type used to indicate ITEM_TYPE is about a group
 */
nsdefine('GROUP_RECOMMENDATION', 3);
/**
 * Type used to indicate ITEM_TYPE is about a wiki resource
 */
nsdefine('RESOURCE_RECOMMENDATION', 4);
/**
 * Used to control update frequency of impression analytic data when
 * media updater in use
 */
nsconddefine("ANALYTICS_UPDATE_INTERVAL", ONE_HOUR / 6);
/**
 * Used to control update frequency of that raw (not summarized)
 * impression data is deleted if the media updater is running and
 * the CullOldRawImpressionsJob is running
 */
nsconddefine("CULL_OLD_IMPRESSIONS_INTERVAL", ONE_DAY);
/** Value of epsilon in differential privacy formula */
nsconddefine('PRIVACY_EPSILON', 0.01);
/** Flag to turn on/off search impression recording */
nsconddefine('SEARCH_ANALYTICS_MODE', true);
/** Flag to turn on/off group impression recording */
nsconddefine('GROUP_ANALYTICS_MODE', true);
/** Flag to turn on/off differential privacy */
nsconddefine('DIFFERENTIAL_PRIVACY', false);
/** Number of trending feed item results to compute */
nsconddefine('NUM_TRENDING', 50);
/*
 * Database Field Sizes
 */
/* Length for names of things like first name, last name, etc */
nsdefine('NAME_LEN', 32);
/** Length of a session id, written as hex characters, used to size
    the column that maps a session id to a signed-in user */
nsdefine('SESSION_ID_LEN', 64);
/** Used for lengths of media sources, passwords, and emails */
nsdefine('LONG_NAME_LEN', 64);
/** Length of the personal recovery question a member can type when the
    site uses question-based account recovery */
nsdefine('RECOVERY_QUESTION_LEN', 256);
/** Bullets shown in the recovery-answer field on the change form to mark
    that an answer is already on file; the real answer is kept only as a
    hash and is never shown back */
nsdefine('RECOVERY_ANSWER_PLACEHOLDER', str_repeat("\u{2022}", 8));
/** Length for names of things like group names, etc */
nsdefine('SHORT_TITLE_LEN', 128);
/** Length for names of things like titles of blog entries, etc */
nsdefine('TITLE_LEN', 512);
/** Length of a feed item or post, etc */
nsdefine('MAX_GROUP_POST_LEN', 8192);
/** Length for for the contents of a wiki_page */
nsdefine('MAX_GROUP_PAGE_LEN', 524288);
/** Largest longest-common-subsequence table, counted in cells, that a
    wiki page-history diff is allowed to build. The table needs one cell
    for each pair made from a differing line in the first version and a
    differing line in the second, so its memory grows with the product of
    the two line counts. At roughly sixty bytes per cell this ceiling keeps
    a single diff under about fifteen megabytes. When two versions differ
    so much that they would exceed this, the diff falls back to showing the
    whole differing region as a block of removed lines followed by a block
    of added lines, which is coarser but costs almost no memory. */
nsdefine('MAX_DIFF_LCS_CELLS', 250000);
/** Largest longest-common-subsequence table, counted in cells, that a
    browser-side wiki-revision diff builds exactly before it drops to the
    banded search. The browser computes one diff on demand in its own tab and
    holds the exact table's backtrack directions at one byte per cell in a
    typed array, carrying only two rows of lengths, so several million cells
    cost only a few megabytes and finish well under a second. This ceiling is
    therefore far above the server one and comfortably covers a long page, or
    one whose stored revision repeats its body. Only a region past it falls
    back to the diagonal band (MAX_DIFF_LCS_BAND), which cannot span a length
    gap wider than the band and so shows such a region coarsely. */
nsdefine('MAX_DIFF_LCS_CELLS_CLIENT', 4000000);
/** How far, in lines, a browser-side wiki-revision diff lets an alignment
    drift from the diagonal before it stops looking. Two revisions of a page
    are usually near-identical, so their matching lines sit close to the
    diagonal; bounding the search to a band this wide keeps the browser diff
    fast on very large revisions. When the two revisions differ in length by
    more than this the browser shows the whole region coarsely instead. */
nsdefine('MAX_DIFF_LCS_BAND', 20);
/** Largest file, in bytes, that the Git repository read view will show
    inline. A file over this is offered for download instead of being
    printed into the page, which keeps one oversized file in a repository
    from bloating the rendered page. */
nsdefine('MAX_GIT_BLOB_VIEW_LEN', 1048576);
/** How long, in seconds, the git push handler remembers that a set of
    HTTP basic-auth credentials was accepted. A push resends the same
    username and password with every object it uploads, and checking the
    password hash is deliberately slow, so without this a large push spends
    most of its time re-hashing the same password hundreds of times. Only a
    successful check is remembered, and only in the running server's memory,
    so a wrong password still costs a full hash and nothing is written to
    disk. Kept short so a changed or revoked password takes effect soon. */
nsconddefine('GIT_AUTH_CACHE_TIMEOUT', 5 * ONE_MINUTE);
/** Number of leading characters of a commit's full object name shown as its
    short name in the read view, enough to be unique in all but the largest
    histories while staying easy to read. */
nsconddefine('GIT_SHORT_HASH_LENGTH', 9);
/** How many of the busiest authors and commonest file endings a Git
    repository's statistics show, longest bars first */
nsconddefine('GIT_STATS_TOP', 8);
/** How many of the most recent months a Git repository's commits-over-time
    statistics show */
nsconddefine('GIT_STATS_MONTHS', 24);
/** Number of rows the Git read view's commit and tag lists load at a time,
    both for the first page and for each further page fetched as the reader
    scrolls. */
nsconddefine('GIT_LIST_PAGE_SIZE', 20);
/** Number of recent commits, and of recent tags, offered directly in the
    read view's ref chooser before the reader opens the full list. */
nsconddefine('GIT_RECENT_REF_COUNT', 5);
/** Maximum size, in bytes, of the body of a mail message composed
    via the user-mail Compose flow. 8 MB is generous for plain-text
    email (~4 million characters) while drawing a clear upper bound
    if a user pastes something pathological. */
nsdefine('MAX_MAIL_BODY_LEN', 8 * 1024 * 1024);
/** Most bytes of a wiki page resource served buffered in a single
    response. A range request asking for more is answered with this
    many bytes (the Content-Range header names the prefix actually
    served, and the player fetches the rest with follow-up range
    requests); a whole-file request for a larger resource is sent
    through the web server's streaming path instead of being
    buffered. Bounds the memory one resource request can hold. */
nsconddefine('MAX_RESOURCE_SERVE_LEN', 4 * 1024 * 1024);
/** Read-block size, in bytes, for streaming a resource (a range
    response or a large whole-file response) out through the server's
    generator emitter. Each iteration reads this much from the file and
    yields it as one body chunk; the value matches the server's per
    stream pump slice so a single read fills a pump cycle without the
    generator being woken more often than the flow-control window can
    absorb. */
nsconddefine('RESOURCE_STREAM_BLOCK_LEN', 256 * 1024);
/* Largest a page's resource folder may be, in bytes, and still be
   offered as a single zip download. This is a gigabyte counted in powers
   of ten, the same way the helper that puts a byte count into words
   counts, so the figure a reader is told is the figure being enforced.
   The archive is built on disk before it is sent, so the ceiling is on
   what the site is willing to spend a request building and a reader is
   willing to wait for, rather than on what fits in memory. A folder over
   this is refused outright rather than sent short, since an archive
   missing files without saying so is worse than none.
 */
nsconddefine('MAX_RESOURCES_ZIP_LEN', 1000 * 1000 * 1000);
/** Maximum number of characters that can be entered in a text area in a
    wiki cvs form */
nsconddefine('CVS_FORM_TEXTAREA_LEN', 4096);
/** Maximum time in seconds to a group call event */
/**
 * MAX_CALL_RING_TIME is how long, in seconds, a call that nobody has
 * picked up keeps ringing before it is treated as gone. A waiting call
 * sends nothing while it waits, so it is held live by its age over this
 * span rather than by any event, and the caller stops ringing after the
 * same time.
 */
nsdefine('MAX_CALL_RING_TIME', 60);
nsdefine('MAX_CALL_EVENT_AGE', 20);
/** array of ICE server for user chat audio or video calls */
nsconddefine('GROUP_CALL_ICE_SERVERS', null);
/** Address the call relay listens on. It stands between two browsers
    that cannot reach each other, so a relay meant to serve people
    outside this machine listens on an address they can reach. */
nsconddefine('TURN_BIND', '0.0.0.0');
/** Port the call relay listens on. 3478 is the port the standard for
    this, RFC 5766, gives for it. */
nsconddefine('TURN_PORT', 3478);
/** Name the call relay gives itself when it asks a browser to prove
    who it is. */
nsconddefine('TURN_REALM', 'yioop');
/** Name a browser gives the call relay. */
nsconddefine('TURN_USER', 'yioop');
/** Secret a browser gives the call relay with that name. A site that
    runs a relay reachable from outside sets this to something of its
    own. */
nsconddefine('TURN_SECRET', '');
/** Range of ports the call relay hands out, one per call it carries,
    written as the lowest and the highest with a dash between. */
nsconddefine('TURN_RELAY_PORTS', '60000-60100');
/** Length for base 64 encode timestamps */
nsdefine('TIMESTAMP_LEN', 11);
/**
 * Number of leading characters of a hashed folder name used as a
 * subdirectory prefix, so many folders spread across subdirectories
 * rather than piling into one
 */
nsdefine('FOLDER_PREFIX_LEN', 3);
/** Length for timestamps down to microseconds */
nsdefine('MICROSECOND_TIMESTAMP_LEN', 20);
/** Length for a CAPTCHA */
nsdefine('CAPTCHA_LEN', 6);
/** Length for a number field */
nsdefine('MAX_IP_ADDRESS_AS_STRING_LEN', 39);
/** Length for a number field */
nsdefine('NUM_FIELD_LEN', 4);
/** Length for writing mode in locales */
nsdefine('WRITING_MODE_LEN', 5);
/** Length for a mail-account display name (the user-facing label
 *  for a configured account in the sidebar) and for an IMAP
 *  default-folder name. 80 chars is comfortably larger than any
 *  realistic display label or folder path. */
nsdefine('MAIL_DISPLAY_NAME_LEN', 80);
/** Length for the PROVIDER tag on a mail account, e.g. "gmail",
 *  "yahoo", "custom". 32 chars matches NAME_LEN; the values come
 *  from the fixed preset list in MailAccountModel. */
nsdefine('MAIL_PROVIDER_LEN', 32);
/** Length for a mail server hostname (IMAP HOST, SMTP_HOST).
 *  255 is the RFC 1035 maximum DNS-name length. */
nsdefine('MAIL_HOST_LEN', 255);
/** Length for a mail-account login username (IMAP USERNAME,
 *  SMTP_USERNAME). Mail usernames are typically email addresses;
 *  255 accommodates the longest realistic local + @ + domain. */
nsdefine('MAIL_USERNAME_LEN', 255);
/** Length for the TLS-mode token on a mail account (TLS_MODE,
 *  SMTP_TLS_MODE), the STATUS token on a scheduled-send row,
 *  and the MODE / STATUS tokens on a clone-job row. Values are
 *  short identifiers like "imaps", "starttls", "none",
 *  "queued", "sent", "pending", "running", "done", "cancelled",
 *  "add", "wipe". */
nsdefine('MAIL_TLS_MODE_LEN', 16);
/** Length for the base64-encoded sodium nonce stored alongside
 *  an encrypted mail-account password (PASSWORD_NONCE,
 *  SMTP_PASSWORD_NONCE). sodium_crypto_secretbox uses a 24-byte
 *  nonce; base64 expands to 32 chars; 64 gives headroom for any
 *  future cipher-suite swap. */
nsdefine('MAIL_PASSWORD_NONCE_LEN', 64);
/** Length for the base64-encoded sodium ciphertext on a mail
 *  account (PASSWORD_CIPHERTEXT, SMTP_PASSWORD_CIPHERTEXT). Real
 *  passwords are short enough that the ciphertext fits in under
 *  200 chars; 1024 is a generous ceiling. */
nsdefine('MAIL_PASSWORD_CIPHERTEXT_LEN', 1024);
/** Length for an RFC-5322 Subject or Message-ID header value
 *  stored on a scheduled-send row (SUBJECT, IN_REPLY_TO). 255 is
 *  the recommended ceiling for human-typed subject lines and is
 *  enough for any reasonable Message-ID. */
nsdefine('MAIL_SUBJECT_LEN', 255);
/** Length for the LAST_ERROR string on a scheduled-send row.
 *  Long enough to capture the SMTP server's full rejection
 *  message including transient-vs-permanent code and reason. */
nsdefine('MAIL_LAST_ERROR_LEN', 500);
/** Length for the short-token ERROR_KIND / RESOLVED_VIA
 *  columns on MAIL_CLONE_ERROR. Stored as snake_case tokens
 *  (e.g. 'pipelined_fetch_eof', 'single_connection_retry');
 *  64 is comfortably above current usage and matches the
 *  width of other Yioop short-token columns. */
nsdefine('MAIL_CLONE_ERROR_KIND_LEN', 64);
/** Length for a folder-name column. IMAP folder names per RFC
 *  3501 are essentially length-unlimited (modified UTF-7 plus
 *  arbitrary hierarchy depth); 255 matches Postfix's typical
 *  cap and is enough for realistic Maildir++ trees such as
 *  Archive/2025/Q4/work. */
nsdefine('MAIL_FOLDER_NAME_LEN', 255);
/** Length for the CredentialCipher master key stored in
 *  MAIL_SECRET.KEY_VALUE in the private database. 128 chars
 *  comfortably holds the base64-encoded 32-byte key plus any
 *  versioning prefix added in the future. */
nsdefine('MAIL_SECRET_KEY_LEN', 128);
/** Max user session size */
nsdefine('MAX_USER_SESSION_SIZE', 16384);
/** Number of components in a term or item embedding */
nsdefine('EMBEDDING_VECTOR_SIZE', 200);
/** Max number of inserts to do in a single insert statement for
 */
nsconddefine('BATCH_SQL_INSERT_NUM', 500);
/*
 * Wiki forms related
 */
/** Max size of CSV associated with a  user created wiki form */
nsconddefine('MAX_WIKI_FORM_CSV_SIZE', 5000000);
/** Max number of fields that a user created wiki form can have */
nsconddefine('MAX_WIKI_FORM_FIELDS', 200);
/**
 * Name of the decoy (honeypot) field added to every wiki form. It is
 * hidden from people and from screen readers, so a real visitor never
 * fills it; an automated form-filler that blindly populates inputs does,
 * which lets the server reject that submission as a bot.
 */
nsconddefine('WIKI_FORM_HONEYPOT_FIELD', 'homepage_url');
/**
 * Shortest believable time, in seconds, between a wiki form being shown
 * and being submitted. A submission that arrives faster than this was
 * almost certainly made by a script, not a person reading the form, so it
 * is rejected.
 */
nsconddefine('MIN_WIKI_FORM_DELAY', 2);
/** Name to use for csv resource associated with a wiki form page */
nsconddefine('WIKI_FORM_CSV_FILE', "form_data.csv");
/** Name to use for meta data related to a secret ballot wiki form page */
nsconddefine('WIKI_FORM_SECRETS_FILE', "secrets.txt");
/**
 * How long, in seconds, a link inviting a witness to take part in a
 * secret ballot stays good for. A witness follows it in their own time,
 * so it has to outlast a working day, but not so long that an old mail
 * still opens the ballot. Sending a fresh set of invitations ends the
 * ones before it whatever this says.
 */
nsconddefine('BALLOT_INVITE_LIFETIME', 86400);
/**
 * How long, in seconds, after writing to a witness the site treats
 * another invitation to the same witness as a repeat rather than
 * something asked for. One press of the button that reaches the site
 * twice would otherwise send two invitations, the second ending the
 * first, so the witness would be holding a link that no longer works.
 */
nsconddefine('BALLOT_INVITE_REPEAT_WAIT', 60);
/**
 * Name of the file a ballot's witness round is kept in while the
 * witnesses are still answering. It is separate from the secrets file
 * because that file existing is what says a ballot has begun, and a round
 * is what happens before one begins.
 */
nsconddefine('WIKI_FORM_ROUND_FILE', "witness_round.txt");
/*
 * Adjustable CHAT BOT RELATED defines
 */
/** How many pages a made-up web holds when a site owner has not said.
 * Small enough that a crawl of it finishes while somebody watches.
 */
nsconddefine('SYNTHETIC_GRAPH_SIZE', 1000);
/** How many pages of a made-up web a crawl starts from when a site
 * owner has not said.
 */
nsconddefine('SYNTHETIC_SEED_COUNT', 1);
/** The number a made-up web is built from. The same number gives the
 * same web every run, so one crawl of it can be compared with another.
 */
nsconddefine('SYNTHETIC_SEED', 1);
/** Maximum number of patterns that can be used in a chat bot */
nsconddefine('MAX_BOT_PATTERNS', 50);
/** Maximum number chat bots that will be listened to in a group.
 * Beyond this number additional bots will be ignored. I.e., they
 * won't get a chance to answer posts
 */
nsconddefine('GROUP_BOT_FOLLOWERS', 10);
/*
 * Adjustable AD RELATED defines
 *
 /** Truncate length for ad description and keywords*/
nsdefine('ADVERTISEMENT_TRUNCATE_LEN', 8);
/** Initial bid amount for advertisement keyword */
nsconddefine('AD_KEYWORD_INIT_BID', 1);
/** Allows the root account to purchase free ad credits. Might
 *  mess up the value of credits if allow. This only makes a difference
 *  in the presence of an ad processing script
 */
nsconddefine('ALLOW_FREE_ROOT_CREDIT_PURCHASE', false);
/** advertisement date format for start date and end date*/
nsconddefine('AD_DATE_FORMAT','Y-m-d');
/** advertisement logo*/
nsconddefine('AD_LOGO','resources/adv-logo.png');
/** sentence compression enabled or not*/
nsconddefine('SENTENCE_COMPRESSION_ENABLED', false);
/** The number of rows to be used in bulk insert from Lexicon */
nsconddefine('NUM_LEX_BULK_INSERTS',100000);
/** Length of advertisement credits service account id string*/
nsconddefine('AD_CREDITS_SERVICE_ACCOUNT_LEN', 32);
