Files
FeedLite/lib/bootstrap.php
T
laoyang 27614d8ca8 统一全部表单控件尺寸与外观;删除下载地址字段
用户:「输入框、按钮、下拉框特别丑,一点设计感都没有,高度太随意,
      全面检查一下。还有那个下载链接要它干什么?」

一、表单控件(这是主要工作量)
  原来样式散在 6 处(.field / .filter-row / .filters / .mail-test / .admin 各一套),
  padding 有 8/9/12px 三种,select 与 input 高度不等,并排放参差不齐。

  改成一套体系:
  - CSS 变量统一尺度:--ctl-h 38px / --ctl-h-sm 32px / --ctl-h-lg 42px,
    统一圆角 9px、统一左右内边距 12px、统一边框与阴影
  - 高度用「固定 height + line-height 居中」而不是 padding 撑
  - 🔴 选择器必须**宽松**(按 type 通吃,不用 .field 之类限定)。
     第一版限制了容器,导致后台零散布局里的裸 select/input 只有 19/21px,
     和旁边 38px 按钮并排,正是用户说的高度随意。
     放开后实测:19px → 38px,全部一致
  - 下拉框自绘箭头(内联 SVG),不再用各平台不一的原生箭头,右侧留 34px
  - 搜索框加左侧放大镜图标,并去掉 Chrome 自带清除按钮
  - 聚焦统一 brand 光晕,悬停边框加深
  - 按钮也改固定 height(原来 padding 撑出 38.3px,与输入框差 0.3px)
  - .btn.sm 必须显式改 height,否则 padding 被 height 吃掉、小按钮还是 38px

  文件选择按钮:原生控件重做成真按钮
    <label class=file-btn> 包住 input[type=file]
    input 用 clip 视觉隐藏(不能用 display:none,那样点不到也聚焦不了)
    按钮内显示「选择图片」+ 图标 + 选中后的文件名

二、删除「下载地址」字段
  它唯一用途:状态改为「已解决」时把邮件按钮换成「下载最新版本」。
  收益小、维护成本高(每个软件都要填一条 URL)。
  - 后台表单项删除
  - mailer 里对应的判断分支也删掉,按钮统一「查看详情」
  - ⚠️ 数据库 download_url 列**保留不删**:删列不可逆,万一要恢复还得迁一次

验证(用控件总览测试页 + 真实页面实测):
  10 类控件高度全部 38px(文本/数字/下拉/搜索/文件按钮/主按钮/小按钮…),
  文本域 110px,同一行垂直中心完全一致(935.3)
  裸 select/input 从 19/21px 修正到 38px
  下拉箭头 SVG 生效、右内边距 34px;搜索框放大镜生效、左内边距 36px
  真实三个页面(提交页/列表页/详情页)逐一检查:
    没有 checkbox/radio/file 被误改,所有可见控件高度收敛为 38(文本域 110)
  下载地址字段:admin.php 输出与源码中均无 download_url 表单项
  5 个路由 200
2026-11-04 19:17:20 +08:00

146 lines
6.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 启动引导:加载配置、注册自动加载、开启会话、初始化数据库。
* 所有页面(index/thread/new/api)都以此为入口。
*/
if (!defined('FB_ROOT')) {
define('FB_ROOT', dirname(__DIR__));
// 静态资源版本号:改 assets/ 下的 css/js 后把这个数字 +1,
// 让浏览器与 CDN 缓存失效。全站只此一处,别再往页面里写死数字。
if (!defined('FB_ASSET_V')) { define('FB_ASSET_V', 47); }
}
/* ------------------------------------------------------------------
* 错误显示策略必须最先设置:
* 任何被打印到页面的 Warning/Deprecated 都会让响应头提前发出,
* 从而令随后的 session_start() 失败(表现为「会话已过期」、CSRF 全部失效)。
* 所以这里在 require 任何库文件之前就把 display_errors 关掉。
* ------------------------------------------------------------------ */
@ini_set('display_errors', '0');
@ini_set('log_errors', '1');
error_reporting(E_ALL);
// 兜底:捕获致命错误时也不要向页面输出堆栈(生产环境)
if (!defined('FB_BOOTSTRAPPED')) {
define('FB_BOOTSTRAPPED', 1);
}
// 后台改设置后自增,用来让 fb_cfg 的进程内缓存失效
if (!isset($GLOBALS['__fb_cfg_stamp'])) {
$GLOBALS['__fb_cfg_stamp'] = 0;
}
if (!function_exists('fb_cfg')) {
/**
* 只读【配置文件】的值(config.php → config.example.php),
* **不叠加 settings 表、绝不触达数据库**。
*
* 为什么必须单独有这么一条通道:正常的 fb_cfg() 会去读 settings 表,
* 而读表要先 fb_db() —— 那一步会就地建出一个空数据库文件。
* 「是否已安装」的判定恰恰以「数据库文件在不在」为依据,
* 所以判定必须走这条不碰库的路径,否则第一秒就把安装态自己造出来了。
*/
function fb_cfg_file() {
static $file = null;
static $stamp = -1;
$now = isset($GLOBALS['__fb_cfg_stamp']) ? (int)$GLOBALS['__fb_cfg_stamp'] : 0;
if ($file === null || $stamp !== $now) {
$local = FB_ROOT . '/config.php';
$example = FB_ROOT . '/config.example.php';
if (is_file($local)) { $file = (array)(require $local); }
elseif (is_file($example)) { $file = (array)(require $example); }
else { $file = []; }
$stamp = $now;
}
return $file;
}
/**
* 读取配置(点分路径,如 fb_cfg('smtp.host'))
* 取值顺序:settings 表(后台改过的)> config.php(本地配置)> config.example.php(出厂默认)> $default
*
* config.php 不参与版本管理(见 .gitignore),新部署时可从 config.example.php 复制一份;
* 若不存在则直接以 config.example.php 为默认值运行,保证 clone 下来即可跑。
*/
function fb_cfg($path = null, $default = null) {
static $merged = null;
static $stamp = -1;
$now = isset($GLOBALS['__fb_cfg_stamp']) ? (int)$GLOBALS['__fb_cfg_stamp'] : 0;
if ($merged === null || $stamp !== $now) {
$cfg = fb_cfg_file();
// 后台设置以嵌套数组形式叠加在文件配置之上(只覆盖同名键)
$tree = function_exists('fb_setting_tree') ? fb_setting_tree() : [];
$merged = is_array($tree) && $tree ? array_replace_recursive($cfg, $tree) : $cfg;
$stamp = $now;
}
if ($path === null || $path === '') { return $merged; }
$cur = $merged;
foreach (explode('.', (string)$path) as $k) {
if (is_array($cur) && array_key_exists($k, $cur)) {
$cur = $cur[$k];
} else {
return $default;
}
}
return $cur;
}
}
/** 让 fb_cfg() 的进程内缓存失效(改了磁盘上的 config.php 后必须调用) */
function fb_cfg_flush() {
$GLOBALS['__fb_cfg_stamp'] = ((int)$GLOBALS['__fb_cfg_stamp']) + 1;
}
require_once FB_ROOT . '/lib/util.php';
require_once FB_ROOT . '/lib/db.php';
require_once FB_ROOT . '/lib/oss.php';
require_once FB_ROOT . '/lib/attach.php';
require_once FB_ROOT . '/lib/captcha.php';
require_once FB_ROOT . '/lib/mailer.php';
require_once FB_ROOT . '/lib/layout.php';
/* ------------------------------------------------------------------
* 安装引导:数据库还没生成(data/ 下没数据)就说明是全新部署,
* 一律先送去 install.php 填配置、建库、设口令,装完才进系统。
* 判据只看「数据库文件在不在」——绝不能在这里调 fb_db(),
* 因为那会就地建出一个空库、把安装态自己"装"出来,之后就再也进不了向导。
* ------------------------------------------------------------------ */
if (!defined('FB_SKIP_INSTALL_REDIRECT') && !fb_installed()) {
if (PHP_SAPI === 'cli') {
fwrite(STDERR, "FeedLite 尚未安装:请先在浏览器中打开本目录下的 install.php 完成安装。\n");
exit(1);
}
fb_redirect('install.php');
}
// 会话(CLI 下不启用)
if (PHP_SAPI !== 'cli' && session_status() !== PHP_SESSION_ACTIVE) {
$p = session_get_cookie_params();
$https = (!empty($_SERVER['HTTPS']) && strtolower((string)$_SERVER['HTTPS']) !== 'off');
@session_set_cookie_params([
'lifetime' => 0,
'path' => !empty($p['path']) ? $p['path'] : '/',
'domain' => '',
'secure' => $https,
'httponly' => true,
'samesite' => 'Lax',
]);
@session_start();
}
// 初始化数据库(建表)。
// ★ 只在【已安装】时做:未安装就建库,等于第一秒把这个状态自己抹掉,
// 安装向导(install.php)随后就会被判定成「已经装好了」而拒绝工作。
// install.php 在真正提交安装时会自己显式调用 fb_db()。
if (fb_installed()) {
fb_db();
// 顺手确保 uploads/ 目录与「禁止直读」规则在位且为最新版(local / oss 都需要)。
// ⚠️ 为什么放在这里:老站点上早已存在的旧版 .htaccess(曾允许直读)不会自己更新,
// 而部署账号通常对 uploads/ 无写权限 —— 只有 PHP(目录属主)能改。
// 函数内部只在「文件缺失或版本标记过期」时才写盘,命中后每次请求几乎零开销。
if (function_exists('fb_local_ensure_dir')) {
fb_local_ensure_dir();
}
}