Yetixx
Yetixx
Server: nginx/1.28.0
System: Linux instance-rr9enuui 6.1.0-15-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.66-1 (2023-12-09) x86_64
User: www (1000)
PHP: 8.0.26
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: /www/wwwroot/www.byte123.top/wp-content/plugins/BoomStart/admin/class-bs-admin.php
<?php
/**
 * 后台管理页面
 *
 * @package BoomStart
 */

// 防止直接访问
if (!defined('ABSPATH')) {
	exit;
}

class BS_Admin
{

	/**
	 * 初始化
	 */
	public static function init()
	{
		add_action('admin_menu', array(__CLASS__, 'register_menu'));
		add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueue_assets'));
	}

	/**
	 * 注册后台菜单
	 */
	public static function register_menu()
	{
		add_menu_page(
			__('一键起站', 'boomstart'),
			__('一键起站', 'boomstart'),
			'manage_options',
			'boomstart',
			array(__CLASS__, 'render_page'),
			'dashicons-star-filled',
			30
		);
	}

	/**
	 * 加载资源文件
	 */
	public static function enqueue_assets($hook)
	{
		// 仅在插件页面加载
		if ('toplevel_page_boomstart' !== $hook) {
			return;
		}

		wp_enqueue_style(
			'bs-admin-css',
			BS_URL . 'assets/css/bs-admin.css',
			array(),
			BS_VERSION
		);

		wp_enqueue_script(
			'bs-admin-js',
			BS_URL . 'assets/js/bs-admin.js',
			array('jquery'),
			BS_VERSION,
			true
		);

		// 传递 AJAX 参数
		wp_localize_script(
			'bs-admin-js',
			'bsAdmin',
			array(
			'ajaxUrl' => admin_url('admin-ajax.php'),
			'nonce' => wp_create_nonce('bs_action_nonce'),
			'hasApiKey' => !empty(get_option('bs_api_key')) ? '1' : '0',
			'i18n' => [
				'emptyDescription' => __('请输入站点主题描述', 'boomstart'),
				'selectCategory' => __('请选择分类', 'boomstart'),
				'generating' => __('正在生成中...', 'boomstart'),
				'generateBtn' => __('开始魔法生成', 'boomstart'),
				'generateFailed' => __('生成失败,请重试', 'boomstart'),
				'appending' => __('正在追加生成...', 'boomstart'),
				'appendBtn' => __('开始追加生成', 'boomstart'),
				'appendFailed' => __('追加生成失败,请重试', 'boomstart'),
				'confirmPublish' => __('确定要发布所有草稿吗?发布后无法通过本插件批量撤回。', 'boomstart'),
				'publishing' => __('发布中...', 'boomstart'),
				'publishFailed' => __('发布失败,请重试', 'boomstart'),
				'networkError' => __('网络错误,请检查连接后重试', 'boomstart'),
				'confirmDelete' => __('确定要删除所有生成的分类和文章吗?\n它们将被移至回收站,可在 WordPress 回收站中恢复。', 'boomstart'),
				'deleting' => __('删除中...', 'boomstart'),
				'deleteFailed' => __('删除失败,请重试', 'boomstart'),
				'deleteSuccess' => __('已删除,页面即将刷新', 'boomstart'),
				'saving' => __('保存中...', 'boomstart'),
				'saved' => __('✅ 已保存', 'boomstart'),
				'saveFailed' => __('保存失败,请重试', 'boomstart'),
				'testing' => __('测试中...', 'boomstart'),
				'testSuccess' => __('✅ 连接成功,模型响应正常', 'boomstart'),
				'testFailed' => __('❌ 连接失败:', 'boomstart'),
				'aiThinking' => __('AI 正在思考中,请稍候...', 'boomstart'),
				'taskExpired' => __('任务已过期,请重新生成', 'boomstart'),
				'generatingElapsed' => __('已等待 %s 秒...', 'boomstart'),
				'generateTimeout' => __('生成超时,请重试', 'boomstart'),
				'useMockNotice' => __('你尚未配置 API Key,无法使用 AI 生成。\n你可以先配置 API Key,或使用测试数据体验功能(内容为固定示例,非 AI 生成)。', 'boomstart'),
				'useMockData' => __('使用测试数据', 'boomstart'),
				'goSettings' => __('去配置 API Key', 'boomstart'),
				'stageThinking' => __('⏳ 正在思考站点主题...', 'boomstart'),
				'stageCategories' => __('📂 正在创建分类...', 'boomstart'),
				'stageWriting' => __('✍️ 正在撰写文章内容...', 'boomstart'),
				'stageImages' => __('🖼️ 正在下载本地配图...', 'boomstart'),
				'stageDone' => __('✅ 全部处理完成!', 'boomstart'),
				'optionsSaved' => __('✅ 选项已保存', 'boomstart'),
				'optionsSaving' => __('保存中...', 'boomstart'),
			],
		)
		);
	}

	/**
	 * 渲染管理页面
	 */
	public static function render_page()
	{
		// 权限校验
		if (!current_user_can('manage_options')) {
			wp_die(esc_html__('您没有权限访问此页面。', 'boomstart'));
		}

		$stats = BS_Cleaner::get_generated_stats();
		$has_content = ($stats['drafts'] > 0 || $stats['published'] > 0 || $stats['terms'] > 0);
?>
		<div class="wrap bs-wrap">
			<h1><?php esc_html_e('一键起站', 'boomstart'); ?></h1>
			<?php self::render_settings_card(); ?>
			<?php self::render_generation_options_card(); ?>
			<?php if ($has_content): ?>
				<?php self::render_action_card($stats); ?>
			<?php
		else: ?>
				<?php self::render_input_view(); ?>
			<?php
		endif; ?>
		</div>
		<?php
	}

	/**
	 * 渲染 API 设置卡片
	 */
	private static function render_settings_card()
	{
		$api_key = get_option('bs_api_key', '');
		$has_api_key = !empty($api_key);
		$collapsed_class = $has_api_key ? 'collapsed' : '';
		$badge = $has_api_key
			? '<span class="bs-badge-ok">✅ ' . esc_html__('已配置', 'boomstart') . '</span>'
			: '<span class="bs-badge-warn">⚠️ ' . esc_html__('未配置', 'boomstart') . '</span>';
?>
		<div class="bs-card bs-settings-card <?php echo esc_attr($collapsed_class); ?>">
			<div class="bs-settings-header">
				<span>
					<?php esc_html_e('⚙️ API 设置', 'boomstart'); ?>
					<?php echo $badge; ?>
				</span>
				<span class="bs-settings-arrow">▾</span>
			</div>
			<div class="bs-settings-body">
				<div class="bs-form-group">
					<label for="bs-api-base-url"><?php esc_html_e('Base URL', 'boomstart'); ?></label>
					<input type="text" id="bs-api-base-url" class="bs-textarea"
						value="<?php echo esc_attr(get_option('bs_api_base_url', '')); ?>"
						placeholder="https://api.deepseek.com/v1">
				</div>
				<div class="bs-form-group">
					<label for="bs-api-key"><?php esc_html_e('API Key', 'boomstart'); ?></label>
					<input type="password" id="bs-api-key" class="bs-textarea"
						value="<?php echo esc_attr($api_key); ?>"
						placeholder="sk-...">
				</div>
				<div class="bs-form-group">
					<label for="bs-api-model"><?php esc_html_e('Model', 'boomstart'); ?></label>
					<input type="text" id="bs-api-model" class="bs-textarea"
						value="<?php echo esc_attr(get_option('bs_api_model', '')); ?>"
						placeholder="deepseek-chat">
				</div>
				<div class="bs-form-group">
					<label>
						<input type="checkbox" id="bs-enable-images"
							<?php checked(get_option('bs_enable_images', 0), 1); ?>>
						<?php esc_html_e('自动配图(为文章下载特色图片)', 'boomstart'); ?>
					</label>
					<p class="bs-settings-note">
						<?php esc_html_e('开启后会根据文章标题自动下载免费图片到媒体库,可能增加生成时间', 'boomstart'); ?>
					</p>
				</div>
				<div class="bs-form-actions">
					<button id="bs-btn-save-settings" class="button button-secondary">
						<?php esc_html_e('保存设置', 'boomstart'); ?>
					</button>
					<button id="bs-btn-test-api" class="button button-secondary">
						<?php esc_html_e('🔗 测试连接', 'boomstart'); ?>
					</button>
					<span id="bs-test-result" style="margin-left: 8px;"></span>
				</div>
				<p class="bs-settings-note">
					<?php esc_html_e('支持 Deepseek、OpenAI 等兼容 OpenAI API 格式的 AI 服务', 'boomstart'); ?>
				</p>
			</div>
		</div>
		<?php
	}

	/**
	 * 渲染生成选项卡片
	 */
	private static function render_generation_options_card()
	{
		$word_count = get_option('bs_word_count', 'standard');
		$tone = get_option('bs_tone', 'professional');
?>
		<div class="bs-card bs-options-card">
			<div class="bs-options-header">
				<span><?php esc_html_e('📝 生成选项', 'boomstart'); ?></span>
			</div>
			<div class="bs-options-body">
				<div class="bs-options-row">
					<div class="bs-form-group bs-form-inline">
						<label for="bs-word-count"><?php esc_html_e('文章篇幅', 'boomstart'); ?></label>
						<select id="bs-word-count" class="bs-select">
							<option value="brief" <?php selected($word_count, 'brief'); ?>><?php esc_html_e('简洁(约200字)', 'boomstart'); ?></option>
							<option value="standard" <?php selected($word_count, 'standard'); ?>><?php esc_html_e('标准(约300字)', 'boomstart'); ?></option>
							<option value="rich" <?php selected($word_count, 'rich'); ?>><?php esc_html_e('丰富(约500字)', 'boomstart'); ?></option>
						</select>
					</div>
					<div class="bs-form-group bs-form-inline">
						<label for="bs-tone"><?php esc_html_e('写作风格', 'boomstart'); ?></label>
						<select id="bs-tone" class="bs-select">
							<option value="professional" <?php selected($tone, 'professional'); ?>><?php esc_html_e('专业正式', 'boomstart'); ?></option>
							<option value="friendly" <?php selected($tone, 'friendly'); ?>><?php esc_html_e('亲切随和', 'boomstart'); ?></option>
							<option value="seo" <?php selected($tone, 'seo'); ?>><?php esc_html_e('SEO 优化', 'boomstart'); ?></option>
						</select>
					</div>
					<button id="bs-btn-save-options" class="button button-small">
						<?php esc_html_e('保存选项', 'boomstart'); ?>
					</button>
					<span id="bs-options-result" style="margin-left: 8px;"></span>
				</div>
			</div>
		</div>
	<?php
	}

	/**
	 * 渲染输入表单视图
	 */
	private static function render_input_view()
	{
?>
		<div class="bs-card">
			<p class="bs-intro">
				<?php esc_html_e('只需简单描述你的站点主题,我们就能为你生成精心设计的分类和文章草稿。', 'boomstart'); ?>
			</p>
			<form id="bs-generate-form" class="bs-form">
				<div class="bs-form-group">
					<label for="bs-site-description"><?php esc_html_e('站点主题描述', 'boomstart'); ?></label>
					<textarea id="bs-site-description" name="site_description" rows="5" class="bs-textarea" required
						placeholder="<?php esc_attr_e('例如:一个关于健康饮食和营养知识的博客...', 'boomstart'); ?>"></textarea>
				</div>
				<div class="bs-form-actions">
					<button type="submit" class="button button-primary button-hero bs-btn-generate">
						<?php esc_html_e('开始魔法生成', 'boomstart'); ?>
					</button>
				</div>
			</form>
			<div id="bs-result" style="display:none;"><div class="bs-result-content"></div></div>
		</div>
		<?php
	}

	/**
	 * 渲染行动卡片视图
	 *
	 * @param array $stats 统计信息
	 */
	private static function render_action_card($stats)
	{
?>
		<div class="bs-card">
			<p class="bs-stats-summary">
				<?php printf(
			esc_html__('✅ 已为你生成 %1$d 个分类、%2$d 篇草稿、%3$d 篇已发布', 'boomstart'),
			(int)$stats['terms'],
			(int)$stats['drafts'],
			(int)$stats['published']
		); ?>
			</p>

			<div class="bs-action-row">
				<?php if ($stats['drafts'] > 0): ?>
					<button id="bs-btn-publish-all" class="button button-primary">
						<?php printf(esc_html__('一键发布全部草稿(%d篇)', 'boomstart'), (int)$stats['drafts']); ?>
					</button>
				<?php
		else: ?>
					<button class="button" disabled><?php esc_html_e('✓ 已全部发布', 'boomstart'); ?></button>
				<?php
		endif; ?>

				<button id="bs-btn-add-more" class="button button-secondary">
					<?php esc_html_e('✨ 继续追加生成', 'boomstart'); ?>
				</button>

				<a href="<?php echo esc_url(admin_url('edit.php')); ?>" class="bs-link">
					<?php esc_html_e('查看文章 ↗', 'boomstart'); ?>
				</a>
				<a href="<?php echo esc_url(admin_url('edit-tags.php?taxonomy=category')); ?>" class="bs-link">
					<?php esc_html_e('查看分类 ↗', 'boomstart'); ?>
				</a>

				<button id="bs-btn-delete-all" class="button bs-btn-danger">
					<?php esc_html_e('🗑️ 删除所有生成内容', 'boomstart'); ?>
				</button>
			</div>

			<div id="bs-append-form-wrap" style="display:none;" class="bs-append-form-wrap">
				<div class="bs-form-group">
					<label for="bs-category-id"><?php esc_html_e('选择分类', 'boomstart'); ?></label>
					<?php wp_dropdown_categories([
			'show_option_none' => __('请选择分类', 'boomstart'),
			'option_none_value' => '0',
			'name' => 'bs_category_id',
			'id' => 'bs-category-id',
			'hide_empty' => false,
			'class' => 'bs-select',
		]); ?>
				</div>
				<div class="bs-form-group">
					<label for="bs-append-description"><?php esc_html_e('站点主题描述', 'boomstart'); ?></label>
					<textarea id="bs-append-description" rows="3" class="bs-textarea"
						placeholder="<?php esc_attr_e('可修改描述以生成不同风格的内容...', 'boomstart'); ?>"></textarea>
				</div>
				<div class="bs-form-actions">
					<button id="bs-btn-append-submit" class="button button-primary">
						<?php esc_html_e('开始追加生成', 'boomstart'); ?>
					</button>
					<button id="bs-btn-cancel-append" class="button">
						<?php esc_html_e('取消', 'boomstart'); ?>
					</button>
				</div>
				<div id="bs-append-result" style="display:none;"><div class="bs-append-result-content"></div></div>
			</div>
		</div>
		<?php
	}
}