很多朋友搭建了原版或者魔改版的sspanel,界面以及功能各有千秋,那么,如何搭建自己的魔改sspanel呢?授人以鱼不如授人以渔,这个博文系列将会介绍如何修改出属于自己的sspanel站点。
今天所述实例基于orvice 的sspanel原版,另外向大家推荐使用glzjin大大的魔改版,Git链接:https://github.com/glzjin/ss-panel-v3-mod
sspanelV3目前是基于mvc结构,我所讲述的也是基于V3的修改。mvc大家应该都很了解,分为model( 模型)、view(界面)、controller(控制器)三个模块,model是数据模型,和数据库关联,view是界面,展示我们所看到的直观界面,controller是控制器,完成逻辑判断,并且可以传递动态数据到view中实现界面数据的变更。这里就不再对mvc做过多陈述,不清楚的可以查阅相关资料。
首先,我们来看一下sspanel的目录结构
其中有几个文件夹分别需要我们注意的,第一个是public,我们知道在配置过程中,项目的根目录指向的就是这个文件夹,其次分别是app(controller、model源码目录),resource(资源文件目录)
本期只讲述最基本的view修改方法,控制代码以及数据库等将在以后几期具体讨论
sspanel的view资源文件存放在resource/view目录中,
文件夹以及文件命名很规范,很容易就可以找到对应的页面,打开主页源码index.tpl,这是基于html的网页源码,在开头及结尾处引用了两个文件,header.tpl、footer.tpl,把其中源码加入连贯起来就是一个完整的html网页,当然其中还包含了部分PHP源码
{include file='header.tpl'} <div class="section no-pad-bot" id="index-banner"> <div class="container"> <br><br> <h1 class="header center orange-text">{$config["appName"]}</h1> <div class="row center"> <h5 class="header col s12 light">轻松科学上网 保护个人隐私</h5> {$homeIndexMsg} </div> {if $user->isLogin} <div class="row center"> <a href="/user" id="download-button" class="btn-large waves-effect waves-light orange">进入用户中心</a> </div> {else} <div class="row center"> <a href="/auth/register" id="download-button" class="btn-large waves-effect waves-light orange">立即注册</a> </div> {/if} <br><br> </div> </div> <div class="container"> <div class="section"> <!-- Icon Section --> <div class="row"> <div class="col s12 m4"> <div class="icon-block"> <!-- To be compatible with some old browsers(especially mobile browsers), use instead of flash_on. For more information visit the link below. http://google.github.io/material-design-icons/#using-the-icons-in-html --> <h2 class="center light-blue-text"><i class="material-icons"></i></h2> <h5 class="center">Super Fast</h5> <p class="light"> Bleeding edge techniques using Asynchronous I/O and Event-driven programming. </p> </div> </div> <div class="col s12 m4"> <div class="icon-block"> <!-- To be compatible with some old browsers(especially mobile browsers), use instead of group. For more information visit the link below. http://google.github.io/material-design-icons/#using-the-icons-in-html --> <h2 class="center light-blue-text"><i class="material-icons"></i></h2> <h5 class="center">Open Source</h5> <p class="light"> Totally free and open source. A worldwide community devoted to deliver bug-free code and long-term support. </p> </div> </div> <div class="col s12 m4"> <div class="icon-block"> <!-- To be compatible with some old browsers(especially mobile browsers), use instead of settings. For more information visit the link below. http://google.github.io/material-design-icons/#using-the-icons-in-html --> <h2 class="center light-blue-text"><i class="material-icons"></i></h2> <h5 class="center">Easy to work with</h5> <p class="light"> Avaliable on multiple platforms, including PC, MAC, Mobile (Android and iOS) and Routers (OpenWRT). </p> </div> </div> </div> </div> <br><br> <div class="section"> </div> </div> {include file='footer.tpl'}
header
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0"/> <title>{$config["appName"]}</title> <!-- CSS fonts.googleapis.com --> <link href="//fonts.lug.ustc.edu.cn/icon?family=Material+Icons" rel="stylesheet"> <link href="/assets/materialize/css/materialize.min.css" type="text/css" rel="stylesheet" media="screen,projection"/> <link href="/assets/materialize/css/style.css" type="text/css" rel="stylesheet" media="screen,projection"/> </head> <body> <nav class="light-blue lighten-1" role="navigation"> <div class="nav-wrapper container"><a id="logo-container" href="/" class="brand-logo">{$config["appName"]}</a> <ul class="right hide-on-med-and-down"> <li><a href="/">首页</a></li> <li><a href="https://shadowsocks.org/en/download/clients.html">客户端下载</a></li> <li><a href="/code">邀请码</a></li> {if $user->isLogin} <li><a href="/user">用户中心</a></li> <li><a href="/user/logout">退出</a></li> {else} <li><a href="/auth/login">登录</a></li> <li><a href="/auth/register">注册</a></li> {/if} </ul> <ul id="nav-mobile" class="side-nav"> <li><a href="/">首页</a></li> <li><a href="https://shadowsocks.org/en/download/clients.html">客户端下载</a></li> <li><a href="/code">邀请码</a></li> {if $user->isLogin} <li><a href="/user">用户中心</a></li> <li><a href="/user/logout">退出</a></li> {else} <li><a href="/auth/login">登录</a></li> <li><a href="/auth/register">注册</a></li> {/if} </ul> <!-- To be compatible with some old browsers(especially mobile browsers), use instead of menu. For more information visit the link below. http://google.github.io/material-design-icons/#using-the-icons-in-html --> <a href="#" data-activates="nav-mobile" class="button-collapse"><i class="material-icons"></i></a> </div> </nav>
在header.tpl中我们可以找到这段代码
<link href="/assets/materialize/css/materialize.min.css" type="text/css" rel="stylesheet" media="screen,projection"/> <link href="/assets/materialize/css/style.css" type="text/css" rel="stylesheet" media="screen,projection"/>
这里引用了两个css样式文件,需要注意的是,项目的根目录是是public,这里引用的相对路径全是基于public目录,我们可以在public下找到对应的目录以及文件,如果需要增加或者修改css样式那么就可以定位到改目录进行增改,如果需要增加css样式文件,也别忘记在header.tpl中加入新的link引用。在footer文件中,包含了js引用,修改方法同header文件。
这样一来,修改view对于有html基础的人来说很简单,只需要按照html规则修改,就可以修改出自己满意的界面。界面的事还是交给UI美工妹子吧(雾)
回到index. tpl,第五行
<h1 class="header center orange-text">{$config["appName"]}</h1>
{$config["appName"]},这里是php的语法,作用是获取config对象的appname元素的值,将它显示在html中,config对象是由控制器传递过来,暂时我们只需要明白它的意义即可。
我们知道ssapnelV3是基于mvc架构,页面的访问并不是直接访问到页面文件,而是由controller处理后再返回的html源码。我也提及到,网页的根目录是public,不过关于页面定向的实际处理文件是的app文件夹下的routes.PHP(不同魔改版本位置略有差异,例如glzjin的魔改版本位置是config文件夹下)具体位置可以在public的index.php中找到
<?php // PUBLIC_PATH define('PUBLIC_PATH', __DIR__); // Bootstrap require PUBLIC_PATH . '/../bootstrap/app.php'; // Build Slim App $app = require BASE_PATH . '/app/routes.php'; // Run ButterFly! $app->run();
这里的routes.php是在app目录下,打开这个文件
<?php use App\Controllers; use App\Middleware\Admin; use App\Middleware\Api; use App\Middleware\Auth; use App\Middleware\Guest; use App\Middleware\Mu; use Slim\App; use Zeuxisoo\Whoops\Provider\Slim\WhoopsMiddleware; /*** * The slim documents: http://www.slimframework.com/docs/objects/router.html */ // config $debug = false; if (defined("DEBUG")) { $debug = true; } // Make a Slim App // $app = new App($c) $app = new App([ 'settings' => [ 'debug' => $debug, 'whoops.editor' => 'sublime' ] ]); $app->add(new WhoopsMiddleware); // Home $app->get('/', 'App\Controllers\HomeController:index'); $app->get('/code', 'App\Controllers\HomeController:code'); $app->get('/tos', 'App\Controllers\HomeController:tos'); $app->get('/debug', 'App\Controllers\HomeController:debug'); $app->post('/debug', 'App\Controllers\HomeController:postDebug'); //$app->get('/about', 'App\Controllers\HomeController:about'); $app->get('/client', 'App\Controllers\HomeController:client'); $app->get('/nodeList', 'App\Controllers\UserController:nodeList'); // User Center $app->group('/user', function () { $this->get('', 'App\Controllers\UserController:index'); $this->get('/', 'App\Controllers\UserController:index'); $this->post('/checkin', 'App\Controllers\UserController:doCheckin'); $this->get('/node', 'App\Controllers\UserController:node'); $this->get('/node/{id}', 'App\Controllers\UserController:nodeInfo'); $this->get('/profile', 'App\Controllers\UserController:profile'); $this->get('/invite', 'App\Controllers\UserController:invite'); $this->post('/invite', 'App\Controllers\UserController:doInvite'); $this->get('/edit', 'App\Controllers\UserController:edit'); $this->post('/password', 'App\Controllers\UserController:updatePassword'); $this->post('/sspwd', 'App\Controllers\UserController:updateSsPwd'); $this->post('/method', 'App\Controllers\UserController:updateMethod'); $this->get('/sys', 'App\Controllers\UserController:sys'); $this->get('/trafficlog', 'App\Controllers\UserController:trafficLog'); $this->get('/kill', 'App\Controllers\UserController:kill'); $this->post('/kill', 'App\Controllers\UserController:handleKill'); $this->get('/logout', 'App\Controllers\UserController:logout'); })->add(new Auth());
最上层引用了controller的目录,如果自己有新增的目录,也要引用进来,我们可以看到注释 //home、//user Center,其下分别是主页这一块的页面、用户中心这一块的页面,$app->group('/user', function () {起到了一个分组的作用,在这个组里是以/user为根路径,例如www.baidu.com,指向的是home,www.baidu.com/user,指向的就是user下的处理。
这个是我修改后的代码,所以能够看到新增的两行
$app->get('/client', 'App\Controllers\HomeController:client');
$app->get('/nodeList', 'App\Controllers\UserController:nodeList');
如果在浏览器中访问www.baidu.com/client,那么这个路径就会由App\Controllers\HomeController:client这个方法去处理,HomeController就是页面的控制器,按图索骥找到app\controller下的源码文件
/** * HomeController */ class HomeController extends BaseController { public function index() { $homeIndexMsg = DbConfig::get('home-index'); return $this->view()->assign('homeIndexMsg', $homeIndexMsg)->display('index.tpl'); } public function code() { $msg = DbConfig::get('home-code'); $codes = InviteCode::where('user_id', '=', '0')->take(10)->get(); return $this->view()->assign('codes', $codes)->assign('msg', $msg)->display('code.tpl'); } public function debug($request, $response, $args) { $server = [ "headers" => $request->getHeaders(), "content_type" => $request->getContentType() ]; $res = [ "server_info" => $server, "ip" => Http::getClientIP(), "version" => Config::get('version'), "reg_count" => Check::getIpRegCount(Http::getClientIP()), ]; Logger::debug(json_encode($res)); return $this->echoJson($response, $res); } public function tos() { return $this->view()->display('tos.tpl'); } public function client() { return $this->view()->display('client.tpl'); }
最后一行可以找到我加入代码引用的client方法,返回了client.tpl这个页面,这里并没有代码的处理,只是简单的返回了一个静态页面,如果按照我的写法,那么我们就新增了一个client页面,通过www.baidu.com/client.tpl可以访问到这个页面(别忘了把百度换成你自己的域名)。可能有人会疑惑,$this->view()->display('client.tpl');我的client.tpl界面在哪里,这里的view对象是在HomeController继承的BaseController中所定义的,上一章我说了view资源文件的位置,具体的view引用和app\Services\View.php有关,作者在其中有相应注释,有兴趣的道友可以去看一下,这里不再讲解。这里也可以新增一个api方法,不返回页面,代码雷同
public function debug($request, $response, $args) { $server = [ "headers" => $request->getHeaders(), "content_type" => $request->getContentType() ]; $res = [ "server_info" => $server, "ip" => Http::getClientIP(), "version" => Config::get('version'), "reg_count" => Check::getIpRegCount(Http::getClientIP()), ]; Logger::debug(json_encode($res)); return $this->echoJson($response, $res); }
可以获取请求的参数,处理后通过response返回json或其他格式数据。
上面我们新增了一个静态页面,如果要加一个动态页面那该如何?上段代码中,有个主页的index方法
public function index() { $homeIndexMsg = DbConfig::get('home-index'); return $this->view()->assign('homeIndexMsg', $homeIndexMsg)->display('index.tpl'); }
->assign('homeIndexMsg', $homeIndexMsg)这个方法就是向view传递数据对象,类似于map键值对,键是homeIndexMsg,值是$homeIndexMsg对象,在对应的index.tpl中就可以通过键引用到对象的值
<div class="section no-pad-bot" id="index-banner"> <div class="container"> <br><br> <h1 class="header center orange-text">{$config["appName"]}</h1> <div class="row center"> <h5 class="header col s12 light">轻松科学上网 保护个人隐私</h5> {<span style="color:#ff0000;">$homeIndexMsg</span>} </div> {if $user->isLogin} <div class="row center"> <a href="/user" id="download-button" class="btn-large waves-effect waves-light orange">进入用户中心</a> </div> {else} <div class="row center"> <a href="/auth/register" id="download-button" class="btn-large waves-effect waves-light orange">立即注册</a> </div> {/if} <br><br> </div> </div>
想必有人会疑惑,我们看到了传入的对象被引用,还有另外两个对象config、user,并没有看见传入,这两个对象是在view中被默认传入的,在所有页面中都可以直接调用,打开app\Services\View.php就可以看到对应的代码,在//add config注释下面几行,将这些配置信息以及个人信息传入
<?php namespace App\Services; use Smarty; class View { public static function getSmarty(){ $smarty=new smarty(); //实例化smarty $smarty->settemplatedir(BASE_PATH.'/resources/views/'.Config::get('theme').'/'); //设置模板文件存放目录 $smarty->setcompiledir(BASE_PATH.'/storage/framework/smarty/compile/'); //设置生成文件存放目录 $smarty->setcachedir(BASE_PATH.'/storage/framework/smarty/cache/'); //设置缓存文件存放目录 // add config $smarty->assign('config',Config::getPublicConfig()); $smarty->assign('user',Auth::getUser()); $smarty->assign('analyticsCode',DbConfig::get('analytics-code')); return $smarty; } }
本章讲到这里,下一章会阐述模型的修改以及数据库相关。诸位快行动起来,拆了重建,搞搞大新闻,想必是极好的。
本文出自:
文章评论