首先在系統安裝程式 install.php 裡添加 sys_apps 資料表, 用來記錄應用程式的安裝資訊 :
//建立 sys_apps 資料表
$data_array["id"]="smallint(6) NOT NULL AUTO_INCREMENT PRIMARY KEY";
$data_array["app_name"]="varchar(255)"; //應用程式名稱
$data_array["installed"]="char(1)"; //已安裝 "Y"/"N"
$data_array["show_tabs"]="char(1)"; //顯示頁籤 "Y"/"N"
$data_array["tab_names"]="varchar(255)"; //頁籤名稱
$data_array["table_names"]="varchar(255)"; //資料表名稱
$data_array["remark"]="varchar(255)"; //安裝結果
$result=create_table("sys_apps",$data_array);
if ($result) {$msg .= "建立資料表 sys_apps ... 完成!<br>";}
$data_array=NULL;
這裡欄位 tab_names 用來記錄應用程式的頁籤名稱 (以逗號隔開), 以便於應用程式 app.php 中讀取後產生頁籤的 div 元素. 欄位 table_names 則是記錄應安裝程式 app_install.php 本身所建立的資料表名稱 (也是以逗號隔開),, 以便要刪除該應用程式時可以讀取此欄位值, 刪除資料庫中所有此應用程式之資料表. 安裝成功後, 欄位 installed 會設定為 "Y" 表示已安裝, 避免二次安裝. 而欄位 show_tabs 是從 jQueryUI 架站機來的, 此處似乎用不到, 可刪除.
系統頁籤資料表 sys_tabs 中也要加入一筆紀錄 :
//插入 sys_tabs 之應用程式標籤
$data_array["tab_name"]="apps";
$data_array["tab_label"]="應用程式";
$data_array["tab_link"]="sys.php?op=apps";
$data_array["tab_level"]=9;
$data_array["tab_tip"]="應用程式";
$data_array["tab_order"]=96;
$result=insert("sys_tabs", $data_array);
$data_array=NULL;
在頁籤 apps 中, 其資料來源 (href) 是取自系統程式 sys.php 中的 apps 模組, 因此要在 sys.php 裡添加一個 case, 裡面主要是一個放頁籤內容的 div 元素 :
case "apps" : {
?>
<div class="tab" title="應用程式">
</div>
<?php
break;
}
然後我們要在這個空的頁籤放一個 table 元素轉化的 datagrid, 用來表列已上傳登錄的程式 :
case "apps" : {
?>
<div class="tab" title="應用程式">
<!--應用程式 sys_apps 列表-->
<table id="sys_apps" title="應用程式列表" style="width:auto" data-options="tools:'#apps_tools',toolbar:'#apps_toolbar'"></table>
<div id="apps_tools">
<a href="#" id="reload_apps" class="icon-reload" title="重新載入"></a>
</div>
<div id="apps_toolbar" style="text-align:right;padding:2px;">
<a href="#" id="upload_app" class="easyui-linkbutton" data-options="iconCls:'icon-add'">上傳</a>
<a href="#" id="install_app" class="easyui-linkbutton" data-options="iconCls:'icon-ok'">安裝</a>
<a href="#" id="remove_app" class="easyui-linkbutton" data-options="iconCls:'icon-remove'">移除</a>
</div>
<!--上傳應用程式表單對話框-->
<div id="upload_app_dialog" class="easyui-dialog" title="上傳應用程式" style="width:420px;height:200px;" data-options="closed:'true',buttons:'#upload_app_buttons'">
<form id="upload_app_form" style="padding:15px" method="post" enctype="multipart/form-data" target="upload_target">
<div style="margin:10px">
<label style="width:60px;display:inline-block;">主檔案 : </label>
<input name="uploader[]" id="main" class="easyui-filebox" data-options="missingMessage:'此欄位為必填',required:true,buttonText:'選擇主檔案',prompt:'app.php'" style="width:270px">
</div>
<div style="margin:10px">
<label style="width:60px;display:inline-block;">安裝檔 : </label>
<input name="uploader[]" id="install" class="easyui-filebox" data-options="missingMessage:'此欄位為必填',required:true,buttonText:'選擇安裝檔',prompt:'app_install.php'" style="width:270px">
</div>
<div>
<iframe id="upload_target" name="upload_target" src="#" style="width:0;height:0;border:0px solid #ffffff;">
</iframe>
</div>
</form>
</div>
<div id="upload_app_buttons" style="padding-right:15px;">
<a href="#" id="clear_app" class="easyui-linkbutton" iconCls="icon-clear" style="width:90px">重設</a>
<a href="#" id="upload_app_go" class="easyui-linkbutton" iconCls="icon-ok" style="width:90px">上傳</a>
</div>
</div>
這裡除了作為 datagrid 的 table 外, 還有一個上傳應用程式檔案的 form (預設是關閉的 closed:true), 其編碼類型 enctype 必須為 multipart/form-data, 而其 target 屬性則指定一個 iframe 元素, 亦即後端程式的回應要投射到 iframe 裡, 這樣做的原因是因為我們無從得知後端何時可完成上傳作業, 乾脆讓後端完成後自己把回應訊息用一個函式 丟給 iframe 去顯示, 這函式原先放在 sys.php 中, 在 Chrome/Firefox 均無問題, 但在 IE 就會有語法錯誤, 我判斷是原因可能是此函式用 Ajax 傳回之故, 為了解決此問題, 便將此函式移到上一層的 main.php 中了 :
<!-- 應用程式上傳回呼函式 -->
//不可放在 sys.php 內 (IE 會語法錯誤)
function upload_app_callback(status,msg) { //必須在最上層, 不可放在 $ 內
$("#upload_app_dialog").dialog("close"); //關閉上傳對話框
$("#sys_apps").datagrid("reload",{op:"list_apps"}); //重載 datagrid
if (status=="success") { //顯示上傳結果
var msg=msg + '檔案上傳全部成功! <br>';
var icon="info";
}
else {
var msg=msg + '檔案上傳失敗, 請重新上傳! <br>';
var icon="error";
}
$.messager.alert("上傳結果",msg,icon);
}
應用程式由主程式 app.php 與其安裝檔 app_install.php 組成, 我們用兩個 filebox 元件來選取要上傳的檔案, 其 name 均為 uploader[], 上傳後會放在根目錄的 apps 子目錄下. 上面的 apps 應用程式模組中的 datagrid 與上傳表單的控制程式如下 :
$(function(){
//應用程式 sys_apps
$('#sys_apps').datagrid({
columns:[[
{field:'id',title:'id',sortable:true},
{field:'app_name',title:'名稱',sortable:true},
{field:'installed',title:'已安裝',sortable:true},
{field:'show_tabs',title:'顯示頁籤',sortable:true},
{field:'tab_names',title:'頁籤名稱',align:'center',sortable:true},
{field:'table_names',title:'資料表名稱',sortable:true},
{field:'remark',title:'備註',sortable:true}
]],
url:"sys.php",
queryParams:{op:"list_apps"},
fitColumns:true,
singleSelect:true,
pagination:true,
pageSize:10,
rownumbers:true
});
$("#clear_app").bind("click",function(){
$("#upload_app_form")[0].reset();
});
$("#upload_app_go").bind("click",function(){
var main=$("#main").filebox("getText");
main=main.substr(main.lastIndexOf("\\") + 1, main.length);
var app_main=main.split(".")[0]; //主檔名
var install=$("#install").filebox("getText");
install=install.substr(install.lastIndexOf("\\") + 1, install.length);
var install_main=install.split("_")[0]; //安裝主檔名
var reg=/.php$/i; //副檔名均為 .php
if (!reg.test(main) || !reg.test(install)) {
var msg="主程式與其安裝檔必須均為 .php 檔!<br> 請重新選取.";
$.messager.alert("訊息",msg,"error");
return;
}
reg=/_install.php$/i; //過濾出安裝檔
if (!reg.test(install)) {
var msg="安裝檔檔名格式不正確!<br>格式 : APP_install.php<br>";
$.messager.alert("訊息",msg,"error");
return;
}
if (app_main != install_main) { //主檔名不同
msg="此兩檔案並非同一組應用程式! <br>" +
"主程式與其安裝檔之檔名格式 : " +
"APP.php 與 APP_install.php<br>請重新選取.<br>";
$.messager.alert("訊息",msg,"error");
return;
} //end of if
reg=/\W/g; //非英數字與底線
if (reg.test(app_main) || reg.test(install_main)) {
var msg="應用程式及其安裝檔檔名必須均為英數字或底線!<br>" +
"請重新選取.<br>"; ;
$.messager.alert("訊息",msg,"error");
return;
}
$("#upload_app_form").form("submit",{
url:"sys.php?op=upload_app&app_main=" + app_main
});
});
$("#upload_app").bind("click",function(){ //顯示上傳表單
$("#upload_app_dialog").dialog("open").dialog("setTitle","上傳應用程式");
$("#upload_app_form").form("clear");
});
$("#reload_apps").bind("click",function(){
$("#sys_apps").datagrid("load",{op:"list_apps"});
});
$("#install_app").bind("click",function(){
var row=$("#sys_apps").datagrid("getSelected");
if (row) {
if (row.installed=="N") {
var url="apps/" + row.app_name + "_install.php";
$.ajax({
type:"POST",
url:url,
cache:false,
dataType:"json",
success:function(data) {
$("#sys_apps").datagrid("reload",{op:"list_apps"}); //更新列表
if (data.result=="success") {
$.messager.alert("訊息","應用程式安裝成功!","info");
}
else {$.messager.alert("訊息",data.error,"error");}
},
error:function(xhr, thrownError) {
var msg='<p>應用程式安裝失敗!<br>狀態 : ' +
xhr.status + " - " + thrownError + '</p>';
$.messager.alert("訊息",msg,"error");
}
});
}
else {$.messager.alert("訊息","此應用程式已安裝過了!","warning");}
}
else {$.messager.alert("訊息","請選擇要安裝的應用程式!","info");}
});
$("#remove_app").bind("click",function(){
var row=$("#sys_apps").datagrid("getSelected");
if (row) {
$.messager.confirm("確認","確定要刪除這個應用程式嗎?",function(btn){
if (btn){
var params={op:"remove_app",id:row.id};
var callback=function(data){
if (data.status==="success"){
$("#sys_apps").datagrid("reload",{op:"list_apps"});
}
else {$.messager.alert("訊息",data.msg,"error");}
};
$.post("sys.php",params,callback,"json");
}
})
}
else {$.messager.alert("訊息","請選擇要刪除的應用程式!","info");}
});
}); //end of jQuery
首先是 datagrid 的設定, 資料來源為 sys.php 中的 list_apps 模組, 其程式如下 :
case "list_apps" : {
$page=isset($_REQUEST['page']) ? intval($_REQUEST['page']) : 1;
$rows=isset($_REQUEST['rows']) ? intval($_REQUEST['rows']) : 10;
$sort=isset($_REQUEST['sort']) ? $_REQUEST['sort'] : 'id';
$order=isset($_REQUEST['order']) ? $_REQUEST['order'] : 'asc';
if (isset($_REQUEST['search_field'])) { //有 search
$where="WHERE ".$_REQUEST['search_field']." LIKE '%".
$_REQUEST['search_what']."%'";
}
else {$where="";} //無 search
$start=($page-1) * $rows; //本頁第一個列索引 (0 起始)
$SQL="SELECT COUNT(*) FROM `sys_apps`";
$RS=run_sql($SQL);
$total=$RS[0][0]; //紀錄總筆數
$SQL="SELECT * FROM sys_apps ".$where." ORDER BY ".
$sort." ".$order." LIMIT ".$start.",".$rows;
$RS=run_sql($SQL);
$apps=Array();
if (is_array($RS)) {
for ($i=0; $i<count($RS); $i++) {
$apps[$i]=Array("id" => $RS[$i]["id"],
"app_name" => $RS[$i]["app_name"],
"installed" => $RS[$i]["installed"],
"show_tabs" => $RS[$i]["show_tabs"],
"tab_names" => $RS[$i]["tab_names"],
"table_names" => $RS[$i]["table_names"],
"remark" => $RS[$i]["remark"]
);
}
}
$arr=array("total" => $total, "rows" => $apps);
echo json_encode($arr);
break;
}
這邊其實用不到 search 功能 (因為應用程式不會很多, 不需要, 甚至根本不需要分頁, 但我是整組複製過來改, 懶得刪除了).
當按下上傳鈕 upload_app 時, 會顯示上傳表單 :
$("#upload_app").bind("click",function(){ //顯示上傳表單
$("#upload_app_dialog").dialog("open").dialog("setTitle","上傳應用程式");
$("#upload_app_form").form("clear");
});
按檔案盒右側的按鈕會彈出檔案選取視窗, 分別選好主程式與安裝程式後, 按上傳即可, 下圖是上傳工作日誌應用程式 :
注意, 選好檔案後, filebox 的文字欄位顯示的不是真實的位址, 而是一律以 fakepath 代替. 按鈕 upload_app_go 的 click 事件處理會先檢查上傳檔名是否符合既定的格式 (APP.php 與 APP_install.php), 首先必須從檔案盒的文字欄位取出路徑與檔名, 這必須使用 EasyUI 的 filebox 繼承自 textbox 的 getText 方法, 不能用 jQuery 的 val() 方法, 因為 EasyUI 又對 jQuery 物件進行了包裝. 然後用 lastIndexOf 取出最後一個倒斜線後面的檔名, 再辨別是否副檔名均為 php, 安裝檔是否以 "_install.php" 結尾等等, 通過後就將表單附帶應用程式主檔名提交給後端 sys.php 的 upload_app 模組處理 :
case "upload_app" : { //ajax:處理 app 檔案上傳用
$status="success"; //預設上傳成功
$msg=""; //執行結果字串
$upload_dir="./apps/"; //設定上傳目錄
$result=upload_files("uploader", $upload_dir);
if (is_array($result)) { //成功傳回陣列
for ($i=0; $i<count($result); $i++) {
$msg .= $result[$i]["name"]." 上傳成功(".$result[$i]["size"].
" bytes)<br>";
}
//登錄於 sys_apps 資料表
$data_array["app_name"]=$_REQUEST["app_main"];
$data_array["installed"]="N";
$data_array["show_tabs"]="N"; //安裝時更新
$data_array["tab_names"]=""; //安裝時填入
$data_array["table_names"]=""; //安裝時填入
$data_array["remark"]=""; //安裝時更新
$result=insert("sys_apps", $data_array);
}
else { //上傳失敗
$msg .= "檔案上傳失敗 : <br>".$result;
$status="failure";
} //end of else
$para='"'.$status.'","'.$msg.'"'; //回傳回呼函式之參數
?>
<!-- 輸出 script 到 $op=apps 中的 iframe 並執行其中的回呼函式 -->
<script language="javascript" type="text/javascript">
window.top.window.upload_app_callback(<?php echo $para; ?>);
</script>
<?php
break;
}
此模組主要是呼叫 upload_files() 這個上傳函式來處理檔案上傳, 成功的話就在 sys_apps 資料表中紀錄此應用程式檔案名稱與未安裝狀態, 最後把上傳結果放在呼叫最上層方法 upload_app_callback() 的參數字串 $para 中, 傳回給要求者 (sys.php 的 apps 模組). 這樣就會執行此最上層回呼函式來顯示上傳結果. 這個 upload_app_callback() 函式是放在版面布局程式 main.php 中, 已如上述.
上面這個上傳程式的主角是 upload_files() 函式, 收錄在 lib/files.php 函式庫中 :
/*-----------------------------------------------------------------------------
upload_files($uploader,$path="./")
功能 :
此函數將上傳之多個檔案, 由暫存區移至指定路徑下.
參數 :
$uploader : 上傳元件名稱, 乃陣列形式, 例如下列 input 中之 name 值
檔案1 : <input type="file" name="uploader[]">
檔案2 : <input type="file" name="uploader[]">
上傳表單之 enctype 須設為 multipart/form-data :
<form action="upload.php" method="post"
enctype="multipart/form-data">
$path : 檔案儲存路徑, 例如 "./images" (預設值為目前程式所在目錄 ./)
傳回值 :
成功傳回一個二維陣列, 失敗傳回錯誤字串. 陣列第一維為數字索引, 第二維為關聯式,
有三個元素 : 例如第一個上傳檔案 :
$result[0]["name"]=檔案名稱
$result[0]["type"]=檔案類型
$result[0]["size"]=檔案大小
範例 :
<input type="file" name="uploader[]">
$result=upload_files("uploader", "./images");
for ($i=0; $i<count($result); $i++) {
echo $result[$i]["name"]."上傳成功(".$result[$i]["size"]." bytes)<br>";
}
-----------------------------------------------------------------------------*/
function upload_files($uploader,$path="./") {
$counts=count($_FILES[$uploader]["name"]); //上傳檔案數
for ($i=0; $i<$counts; $i++) {
if ($_FILES[$uploader]["error"][$i]==0) { //上傳成功
$file_name=$_FILES[$uploader]["name"][$i];
$tmp_name=$_FILES[$uploader]["tmp_name"][$i];
$new_name=$path.mb_convert_encoding($file_name,"big5","utf-8");
if (move_uploaded_file($tmp_name, $new_name)) { //移動成功
$result[$i]["name"]=$_FILES[$uploader]["name"][$i];
$result[$i]["type"]=$_FILES[$uploader]["type"][$i];
$result[$i]["size"]=$_FILES[$uploader]["size"][$i];
} //end of if
else {$result.=$file_name." 移動失敗<br>";} //移動失敗
} //end of if
else {$result.=$file_name." 上傳失敗<br>";} //上傳失敗
} //end of for
return $result;
}
這個函式與之前在檔案上傳功能時用的 upload_file() 不同之處在於 upload_files() 可傳多檔, 而 upload_file() 只能傳單檔. 所以這裡必須有個迴圈來逐一處理上傳的每個檔案. PHP 函式 mb_convert_encoding() 用來處理中文檔名問題, 但這裡用不到 (檔名僅允許英數字與底線).
上傳成功時結果如下 :
這時點選該列應用程式, 再按安裝鈕, 就會執行上面 Script 中的事件處理程序 :
$("#install_app").bind("click",function(){...參考上面}
此程式會將上傳對話框中的表單以 Ajax 方式提交給已上傳到 ./apps/ 下的 APP_install.php 安裝程式執行, 該安裝程式必須將本身所建立的資料表名稱填入系統資料表 sys_apps 的 table_names 欄位 (以逗號隔開), 以及將所有自己的頁籤填入 tab_names 欄位 (這是繼承舊系統規劃而來, 用不到, 以後可能刪除), 然後傳回執行結果. 由於安裝程式可能出錯, 因此這裡採用 jQuery 最低層的 ajax() 方法來執行非同步請求, 以便能取得較多的錯誤訊息 :
var url="apps/" + row.app_name + "_install.php";
$.ajax({... 參考上面});
應用程式安裝成功後, 除了顯示結果訊息外, 同時也重新載入 datagrid 內容, 這時就會顯示已安裝 :
這個安裝程式 APP_install.php 範本如下 :
<?php
/*-----------------------------------------------------------------------------
Title : 應用程式安裝檔
Author : Tony
Version : v1.0.0
Prototype : 2015-01-07
Last Updated : 2015-01-09
Usage : 安裝應用程式所使用之資料表, 填入初始值
Note :
-----------------------------------------------------------------------------*/
/*=== 系統固定的部分 (勿修改) ===*/
session_start(); //啟動 session 功能
header('Content-type: text/html; charset=utf-8');
//檢查是否已登入, 否則回登入畫面
if (!isset($_SESSION["user_account"])) {header("Location: index.php");}
//設定台北時間
date_default_timezone_set("Asia/Taipei");
//匯入資料庫設定與函式庫
require_once("../db.php"); //匯入資料庫設定檔 (必須)
require_once("../lib/mysql.php"); //匯入資料庫模組 (必須)
//變數設定
$success=FALSE; //整體成功或失敗旗標
$error=Array(); //儲存錯誤訊息用
$tabs=Array(); //儲存頁籤用
$tables=Array(); //儲存表單用
/*=== 應用程式頁籤範本 (只改 APP 名稱 & 複製修改) ===*/
//建立 APP_tabs 資料表 (必須)
$tables[]="APP_tabs"; //"APP" 要改
$data_array["id"]="smallint(6) NOT NULL AUTO_INCREMENT PRIMARY KEY";
$data_array["tab_name"]="varchar(255)";
$data_array["tab_label"]="varchar(255)";
$data_array["tab_link"]="varchar(255)";
$data_array["tab_level"]="tinyint(4)"; //1 (使用者) ~9 (管理者)
$data_array["tab_tip"]="varchar(255)";
$data_array["tab_order"]="tinyint(4)";
$result=create_table("APP_tabs",$data_array); //"APP" 要改
if ($result===TRUE) {$success=TRUE;}
else {$error[]="建立資料表 APP_tabs ... 失敗!";} //"APP" 要改
$data_array=NULL;
//插入 APP_tabs (範本)
$tabs[]="TAB1"; //顯示頁籤標題用
$data_array["tab_name"]="TAB1";
$data_array["tab_label"]="TAB1";
$data_array["tab_link"]="apps/APP.php?op=TAB1"; //"APP" 要改
$data_array["tab_level"]=1;
$data_array["tab_tip"]="TAB1";
$data_array["tab_order"]=1;
$result=insert("APP_tabs", $data_array); //"APP" 要改
if ($result===TRUE) {$success=TRUE;}
else {$error[]="插入資料表 APP_tabs ... 失敗!";} //"APP" 要改
$data_array=NULL;
//插入 APP_tabs (範本)
$tabs[]="TAB2"; //顯示頁籤標題用
$data_array["tab_name"]="TAB2";
$data_array["tab_label"]="TAB2";
$data_array["tab_link"]="apps/APP.php?op=TAB2"; //"APP" 要改
$data_array["tab_level"]=1;
$data_array["tab_tip"]="TAB2";
$data_array["tab_order"]=1;
$result=insert("APP_tabs", $data_array); //"APP" 要改
if ($result===TRUE) {$success=TRUE;}
else {$error[]="插入資料表 APP_tabs ... 失敗!";} //"APP" 要改
$data_array=NULL;
/*--- 新增其他頁籤請複製上面的 TAB1/TAB2 範本於此改寫 ---*/
/*=== 安裝其他資料表範本 (請複製修改, 完成後刪除範本) ===*/
//建立 APP_table1 資料表 (範本)
$tables[]="APP_table1"; //刪除 APP 時用到 (必須)
$data_array["id"]="int(10) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY";
$data_array["field1"]="varchar(255)";
$data_array["field2"]="char(1)";
$data_array["field3"]="text";
$data_array["field4"]="date";
$data_array["field5"]="datetime";
$data_array["field6"]="smallint(6) unsigned NOT NULL DEFAULT '0'";
$data_array["field7"]="tinyint(3) unsigned NOT NULL DEFAULT '10'";
$data_array["field8"]="int(10) unsigned";
$data_array["field9"]="bigint(20) unsigned";
$data_array["field10"]="float unsigned";
$result=create_table("APP_table1",$data_array);
if ($result===TRUE) {$success=TRUE;}
else {$error[]="建立資料表 APP_table1 ... 失敗!";} //"APP_table1" 要改
$data_array=NULL;
//插入 APP_table1 (AUTO_INCREMENT 不用填) (範本)
$SQL="INSERT INTO `app_table1` (".
"`field1`,".
"`field2`,".
"`field3`,".
"`field4`,".
"`field5`,".
"`field6`,".
"`field7`,".
"`field8`,".
"`field9`,".
"`field10`".
"`) VALUES (".
"'',".
"'',".
"'',".
"'',".
"'',".
",".
",".
",".
",".
"".
")";
$result=run_sql($SQL);
if ($result===TRUE) {$success=TRUE;}
else {$error[]="插入資料表 APP_table1 ... 失敗!";} //"APP_table1" 要改
$data_array=NULL;
/*--- 新增其他資料表請複製上面的 APP_table1 範本於此改寫 ---*/
/*=== 更新系統 sys_apps 資料表 (只改 APP 名稱)===*/
//更新系統 sys_apps 資料表的 app_name='程式名稱' 欄位
$data_array["installed"]="Y"; //已安裝 "Y"/"N"
$data_array["show_tabs"]="Y"; //顯示頁籤 "Y"/"N"
$data_array["tab_names"]=join(",",$tabs); //儲存 tabs 以便產生頁籤
$data_array["table_names"]=join(",",$tables); //儲存 tables 以便刪除時清理
$data_array["remark"]="OK"; //安裝結果
$result=update("sys_apps", $data_array, "app_name", "APP"); //"APP" 要改
if ($result===TRUE) {$success=TRUE;}
else {$error[]="更新資料表 sys_apps ... 失敗!";}
$data_array=NULL;
//在 sys_header_links 插入應用程式入口超連結
$data_array["title"]="APP"; //"APP" 要改
$data_array["url"]="javascript:APP()"; //"APP" 要改
$data_array["target"]="_self";
$data_array["sequence"]=1;
$data_array["hint"]="APP"; //"APP" 要改
$result=insert("sys_header_links", $data_array);
if ($result===TRUE) {$success=TRUE;}
else {$error[]="插入資料表 sys_header_links ... 失敗!";}
$data_array=NULL;
/*====== 輸出 JSON 格式之安裝結果 (不用改) ======*/
$arr["result"]=$success ? "success" : "failure";
$arr["error"]=join("<br>",$error);
echo json_encode($arr);
?>
注意,這裡我們用 join() 函式把所記錄的資料表名稱以逗號串接後寫入 table_names 欄位裡, 以備將來要刪除應用程式時斬草除根之用.
而主程式 APP.php 範本如下 :
<?php
/*-----------------------------------------------------------------------------
Title : 應用程式
Translator : Tony
Version : v1.0.0
Prototype : 2015-01-05
Last Updated : 2015-01-05
Usage : 應用程式的主要功能寫在此檔中
Note :
請根據安裝檔內寫入 tabs 的各頁籤 $tab 與各作業 $op 撰寫功能
Ajax/DataTable 的 url 格式 :
$url="apps/app.php?op=get_xxxx";
$url="apps/app.php?op=list_xxxx";
$url="apps/app.php?op=add_xxxx";
$url="apps/app.php?op=remove_xxxx";
$url="apps/app.php?op=update_xxxx";
-----------------------------------------------------------------------------*/
session_start(); //必須啟動才能用 session
header('Content-Type: text/html;charset=UTF-8');
require_once("../db.php"); //匯入資料庫設定檔 (必須)
require_once("../lib/mysql.php"); //匯入資料庫模組 (必須)
require_once("../lib/tools.php"); //匯入工具模組 (選項)
require_once("../lib/parse.php"); //匯入剖析模組 (選項)
require_once("../lib/file.php"); //匯入file模組 (選項)
require_once("../lib/http.php"); //匯入http模組 (選項)
$op=$_REQUEST["op"]; //功能
switch ($op) {
case "TAB1" : { //for ajax
echo "Hello TAB1! The time is ".date("Y-m-d H:i:s");
break;
}
case "TAB2" : { //for ajax
echo "Hello TAB2! The time is ".date("Y-m-d H:i:s");
break;
}
default : { //for rendering
?>
<div id="APP-tab" class="easyui-tabs" data-options="fit:'true'">
<?php
$RS=search("APP_tabs"); //"APP" 要改
if (is_array($RS)) {
for ($i=0; $i<count($RS); $i++) {
?>
<div class="tab" title="<?php echo $RS[$i]["tab_name"] ?>" data-options="href:'<?php echo $RS[$i]["tab_link"] ?>'">
</div>
<?php
} //for
} //if
} //default
} //switch
?>
</div>
以後要寫任何掛在此系統上的應用程式就用此兩範本去擴展即可.
最後, 點選要刪除的 app 列, 再按右上角的刪除鈕就可以刪除應用程式, 它會執行下列事件處理程序 :
$("#remove_app").bind("click",function(){ ... 參考上面});
經過確認後會呼叫 sys.php 的 remove_app 模組來處理刪除工作 :
case "remove_app" : {
$id=$_REQUEST["id"];
$RS=search("sys_apps","id",$id);
$status="success";
$msg="";
if (is_array($RS)) { //有登錄
//刪除此 app 所有資料表
$app_name=$RS[0]["app_name"];
$installed=$RS[0]["installed"];
if ($installed=="Y") { //已安裝
$tables=explode(",",$RS[0]["table_names"]);
for ($i=0; $i<count($tables); $i++) {
$result=drop_table($tables[$i]);
if ($result===FALSE) {
$status="failure";
$msg .= "刪除資料表 ".$tables[$i]." 失敗<br>";
}
}
//從 sys_header_links 刪除此 app 超連結
$result=delete_record("sys_header_links","title",$app_name);
if ($result===FALSE) {
$status="failure";
$msg .= "刪除應用程式在 sys_header_links 之超連結失敗<br>";
}
} //installed
//從 sys_apps 刪除此 app 登錄
$result=delete_record("sys_apps","id",$id);
if ($result===FALSE) {
$status="failure";
$msg .= "刪除應用程式在 sys_apps 之登錄失敗<br>";
}
//刪除應用程式檔案
$result=delete_file("./apps/".$app_name.".php");
if ($result==FALSE) {
$status="failure";
$msg .= $app_name.".php 刪除失敗<br>";
}
$result=delete_file("./apps/".$app_name."_install.php");
if ($result==FALSE) {
$status="failure";
$msg .= $app_name."_install.php 刪除失敗<br>";
}
} //registered
else { //未上傳登錄
$status="failure";
$msg="刪除失敗 : 應用程式不存在!";
}
$arr["status"]=$status;
$arr["msg"]=$msg;
echo json_encode($arr);
break;
}
這裡我們先去讀取 sys_apps 中該應用程式的 table_names 欄位, 用 explode() 方法拆解出此應用所屬之資料表後, 在迴圈中用 drop_table 來刪除資料表. 另外就是刪除 sys_header_links 上的超連結紀錄. 最後去 apps/ 目錄下刪除此應用程式及其安裝檔案即可, 其中使用了收錄在 lib/file.php 函式庫中的 delete_file(), 此函式其實很簡單, 就是 PHP 的 unlink :
/*-----------------------------------------------------------------------------
delete_file($filename)
功能 :
此函數指定路徑下之檔案與目錄列表.
參數 :
$filename : 檔案名稱 (相對路徑, 例如 log/visitors.txt)
傳回值 :
成功傳回 TRUE, 失敗傳回 FALSE.
範例 :
$result=delete_file("a.txt");
$result=delete_file("./a.txt");
$result=delete_file("files/a.txt");
-----------------------------------------------------------------------------*/
function delete_file($filename) {
return unlink($filename);
}
因今天 (1/27) 下午才會進辦公室, 所以趁著早上閒閒把應用程式部分記錄完了, 希望以後要回頭參考時自己還看得懂, 雖然花了很多時間來紀錄, 但是值得, 因為這是此簡易架站系統最重要的部分, 而且隨著年華老去, 記性已不若以往, 上個月寫的東西這個月可能就印象模糊了, 唉.
$(function(){
//應用程式 sys_apps
$('#sys_apps').datagrid({
columns:[[
{field:'id',title:'id',sortable:true},
{field:'app_name',title:'名稱',sortable:true},
{field:'installed',title:'已安裝',sortable:true},
{field:'show_tabs',title:'顯示頁籤',sortable:true},
{field:'tab_names',title:'頁籤名稱',align:'center',sortable:true},
{field:'table_names',title:'資料表名稱',sortable:true},
{field:'remark',title:'備註',sortable:true}
]],
url:"sys.php",
queryParams:{op:"list_apps"},
fitColumns:true,
singleSelect:true,
pagination:true,
pageSize:10,
rownumbers:true
});
$("#clear_app").bind("click",function(){
$("#upload_app_form")[0].reset();
});
$("#upload_app_go").bind("click",function(){
var main=$("#main").filebox("getText");
main=main.substr(main.lastIndexOf("\\") + 1, main.length);
var app_main=main.split(".")[0]; //主檔名
var install=$("#install").filebox("getText");
install=install.substr(install.lastIndexOf("\\") + 1, install.length);
var install_main=install.split("_")[0]; //安裝主檔名
var reg=/.php$/i; //副檔名均為 .php
if (!reg.test(main) || !reg.test(install)) {
var msg="主程式與其安裝檔必須均為 .php 檔!<br> 請重新選取.";
$.messager.alert("訊息",msg,"error");
return;
}
reg=/_install.php$/i; //過濾出安裝檔
if (!reg.test(install)) {
var msg="安裝檔檔名格式不正確!<br>格式 : APP_install.php<br>";
$.messager.alert("訊息",msg,"error");
return;
}
if (app_main != install_main) { //主檔名不同
msg="此兩檔案並非同一組應用程式! <br>" +
"主程式與其安裝檔之檔名格式 : " +
"APP.php 與 APP_install.php<br>請重新選取.<br>";
$.messager.alert("訊息",msg,"error");
return;
} //end of if
reg=/\W/g; //非英數字與底線
if (reg.test(app_main) || reg.test(install_main)) {
var msg="應用程式及其安裝檔檔名必須均為英數字或底線!<br>" +
"請重新選取.<br>"; ;
$.messager.alert("訊息",msg,"error");
return;
}
$("#upload_app_form").form("submit",{
url:"sys.php?op=upload_app&app_main=" + app_main
});
});
$("#upload_app").bind("click",function(){ //顯示上傳表單
$("#upload_app_dialog").dialog("open").dialog("setTitle","上傳應用程式");
$("#upload_app_form").form("clear");
});
$("#reload_apps").bind("click",function(){
$("#sys_apps").datagrid("load",{op:"list_apps"});
});
$("#install_app").bind("click",function(){
var row=$("#sys_apps").datagrid("getSelected");
if (row) {
if (row.installed=="N") {
var url="apps/" + row.app_name + "_install.php";
$.ajax({
type:"POST",
url:url,
cache:false,
dataType:"json",
success:function(data) {
$("#sys_apps").datagrid("reload",{op:"list_apps"}); //更新列表
if (data.result=="success") {
$.messager.alert("訊息","應用程式安裝成功!","info");
}
else {$.messager.alert("訊息",data.error,"error");}
},
error:function(xhr, thrownError) {
var msg='<p>應用程式安裝失敗!<br>狀態 : ' +
xhr.status + " - " + thrownError + '</p>';
$.messager.alert("訊息",msg,"error");
}
});
}
else {$.messager.alert("訊息","此應用程式已安裝過了!","warning");}
}
else {$.messager.alert("訊息","請選擇要安裝的應用程式!","info");}
});
$("#remove_app").bind("click",function(){
var row=$("#sys_apps").datagrid("getSelected");
if (row) {
$.messager.confirm("確認","確定要刪除這個應用程式嗎?",function(btn){
if (btn){
var params={op:"remove_app",id:row.id};
var callback=function(data){
if (data.status==="success"){
$("#sys_apps").datagrid("reload",{op:"list_apps"});
}
else {$.messager.alert("訊息",data.msg,"error");}
};
$.post("sys.php",params,callback,"json");
}
})
}
else {$.messager.alert("訊息","請選擇要刪除的應用程式!","info");}
});
}); //end of jQuery
首先是 datagrid 的設定, 資料來源為 sys.php 中的 list_apps 模組, 其程式如下 :
case "list_apps" : {
$page=isset($_REQUEST['page']) ? intval($_REQUEST['page']) : 1;
$rows=isset($_REQUEST['rows']) ? intval($_REQUEST['rows']) : 10;
$sort=isset($_REQUEST['sort']) ? $_REQUEST['sort'] : 'id';
$order=isset($_REQUEST['order']) ? $_REQUEST['order'] : 'asc';
if (isset($_REQUEST['search_field'])) { //有 search
$where="WHERE ".$_REQUEST['search_field']." LIKE '%".
$_REQUEST['search_what']."%'";
}
else {$where="";} //無 search
$start=($page-1) * $rows; //本頁第一個列索引 (0 起始)
$SQL="SELECT COUNT(*) FROM `sys_apps`";
$RS=run_sql($SQL);
$total=$RS[0][0]; //紀錄總筆數
$SQL="SELECT * FROM sys_apps ".$where." ORDER BY ".
$sort." ".$order." LIMIT ".$start.",".$rows;
$RS=run_sql($SQL);
$apps=Array();
if (is_array($RS)) {
for ($i=0; $i<count($RS); $i++) {
$apps[$i]=Array("id" => $RS[$i]["id"],
"app_name" => $RS[$i]["app_name"],
"installed" => $RS[$i]["installed"],
"show_tabs" => $RS[$i]["show_tabs"],
"tab_names" => $RS[$i]["tab_names"],
"table_names" => $RS[$i]["table_names"],
"remark" => $RS[$i]["remark"]
);
}
}
$arr=array("total" => $total, "rows" => $apps);
echo json_encode($arr);
break;
}
這邊其實用不到 search 功能 (因為應用程式不會很多, 不需要, 甚至根本不需要分頁, 但我是整組複製過來改, 懶得刪除了).
當按下上傳鈕 upload_app 時, 會顯示上傳表單 :
$("#upload_app").bind("click",function(){ //顯示上傳表單
$("#upload_app_dialog").dialog("open").dialog("setTitle","上傳應用程式");
$("#upload_app_form").form("clear");
});
按檔案盒右側的按鈕會彈出檔案選取視窗, 分別選好主程式與安裝程式後, 按上傳即可, 下圖是上傳工作日誌應用程式 :
注意, 選好檔案後, filebox 的文字欄位顯示的不是真實的位址, 而是一律以 fakepath 代替. 按鈕 upload_app_go 的 click 事件處理會先檢查上傳檔名是否符合既定的格式 (APP.php 與 APP_install.php), 首先必須從檔案盒的文字欄位取出路徑與檔名, 這必須使用 EasyUI 的 filebox 繼承自 textbox 的 getText 方法, 不能用 jQuery 的 val() 方法, 因為 EasyUI 又對 jQuery 物件進行了包裝. 然後用 lastIndexOf 取出最後一個倒斜線後面的檔名, 再辨別是否副檔名均為 php, 安裝檔是否以 "_install.php" 結尾等等, 通過後就將表單附帶應用程式主檔名提交給後端 sys.php 的 upload_app 模組處理 :
case "upload_app" : { //ajax:處理 app 檔案上傳用
$status="success"; //預設上傳成功
$msg=""; //執行結果字串
$upload_dir="./apps/"; //設定上傳目錄
$result=upload_files("uploader", $upload_dir);
if (is_array($result)) { //成功傳回陣列
for ($i=0; $i<count($result); $i++) {
$msg .= $result[$i]["name"]." 上傳成功(".$result[$i]["size"].
" bytes)<br>";
}
//登錄於 sys_apps 資料表
$data_array["app_name"]=$_REQUEST["app_main"];
$data_array["installed"]="N";
$data_array["show_tabs"]="N"; //安裝時更新
$data_array["tab_names"]=""; //安裝時填入
$data_array["table_names"]=""; //安裝時填入
$data_array["remark"]=""; //安裝時更新
$result=insert("sys_apps", $data_array);
}
else { //上傳失敗
$msg .= "檔案上傳失敗 : <br>".$result;
$status="failure";
} //end of else
$para='"'.$status.'","'.$msg.'"'; //回傳回呼函式之參數
?>
<!-- 輸出 script 到 $op=apps 中的 iframe 並執行其中的回呼函式 -->
<script language="javascript" type="text/javascript">
window.top.window.upload_app_callback(<?php echo $para; ?>);
</script>
<?php
break;
}
此模組主要是呼叫 upload_files() 這個上傳函式來處理檔案上傳, 成功的話就在 sys_apps 資料表中紀錄此應用程式檔案名稱與未安裝狀態, 最後把上傳結果放在呼叫最上層方法 upload_app_callback() 的參數字串 $para 中, 傳回給要求者 (sys.php 的 apps 模組). 這樣就會執行此最上層回呼函式來顯示上傳結果. 這個 upload_app_callback() 函式是放在版面布局程式 main.php 中, 已如上述.
上面這個上傳程式的主角是 upload_files() 函式, 收錄在 lib/files.php 函式庫中 :
/*-----------------------------------------------------------------------------
upload_files($uploader,$path="./")
功能 :
此函數將上傳之多個檔案, 由暫存區移至指定路徑下.
參數 :
$uploader : 上傳元件名稱, 乃陣列形式, 例如下列 input 中之 name 值
檔案1 : <input type="file" name="uploader[]">
檔案2 : <input type="file" name="uploader[]">
上傳表單之 enctype 須設為 multipart/form-data :
<form action="upload.php" method="post"
enctype="multipart/form-data">
$path : 檔案儲存路徑, 例如 "./images" (預設值為目前程式所在目錄 ./)
傳回值 :
成功傳回一個二維陣列, 失敗傳回錯誤字串. 陣列第一維為數字索引, 第二維為關聯式,
有三個元素 : 例如第一個上傳檔案 :
$result[0]["name"]=檔案名稱
$result[0]["type"]=檔案類型
$result[0]["size"]=檔案大小
範例 :
<input type="file" name="uploader[]">
$result=upload_files("uploader", "./images");
for ($i=0; $i<count($result); $i++) {
echo $result[$i]["name"]."上傳成功(".$result[$i]["size"]." bytes)<br>";
}
-----------------------------------------------------------------------------*/
function upload_files($uploader,$path="./") {
$counts=count($_FILES[$uploader]["name"]); //上傳檔案數
for ($i=0; $i<$counts; $i++) {
if ($_FILES[$uploader]["error"][$i]==0) { //上傳成功
$file_name=$_FILES[$uploader]["name"][$i];
$tmp_name=$_FILES[$uploader]["tmp_name"][$i];
$new_name=$path.mb_convert_encoding($file_name,"big5","utf-8");
if (move_uploaded_file($tmp_name, $new_name)) { //移動成功
$result[$i]["name"]=$_FILES[$uploader]["name"][$i];
$result[$i]["type"]=$_FILES[$uploader]["type"][$i];
$result[$i]["size"]=$_FILES[$uploader]["size"][$i];
} //end of if
else {$result.=$file_name." 移動失敗<br>";} //移動失敗
} //end of if
else {$result.=$file_name." 上傳失敗<br>";} //上傳失敗
} //end of for
return $result;
}
這個函式與之前在檔案上傳功能時用的 upload_file() 不同之處在於 upload_files() 可傳多檔, 而 upload_file() 只能傳單檔. 所以這裡必須有個迴圈來逐一處理上傳的每個檔案. PHP 函式 mb_convert_encoding() 用來處理中文檔名問題, 但這裡用不到 (檔名僅允許英數字與底線).
上傳成功時結果如下 :
這時點選該列應用程式, 再按安裝鈕, 就會執行上面 Script 中的事件處理程序 :
$("#install_app").bind("click",function(){...參考上面}
此程式會將上傳對話框中的表單以 Ajax 方式提交給已上傳到 ./apps/ 下的 APP_install.php 安裝程式執行, 該安裝程式必須將本身所建立的資料表名稱填入系統資料表 sys_apps 的 table_names 欄位 (以逗號隔開), 以及將所有自己的頁籤填入 tab_names 欄位 (這是繼承舊系統規劃而來, 用不到, 以後可能刪除), 然後傳回執行結果. 由於安裝程式可能出錯, 因此這裡採用 jQuery 最低層的 ajax() 方法來執行非同步請求, 以便能取得較多的錯誤訊息 :
var url="apps/" + row.app_name + "_install.php";
$.ajax({... 參考上面});
應用程式安裝成功後, 除了顯示結果訊息外, 同時也重新載入 datagrid 內容, 這時就會顯示已安裝 :
這個安裝程式 APP_install.php 範本如下 :
<?php
/*-----------------------------------------------------------------------------
Title : 應用程式安裝檔
Author : Tony
Version : v1.0.0
Prototype : 2015-01-07
Last Updated : 2015-01-09
Usage : 安裝應用程式所使用之資料表, 填入初始值
Note :
-----------------------------------------------------------------------------*/
/*=== 系統固定的部分 (勿修改) ===*/
session_start(); //啟動 session 功能
header('Content-type: text/html; charset=utf-8');
//檢查是否已登入, 否則回登入畫面
if (!isset($_SESSION["user_account"])) {header("Location: index.php");}
//設定台北時間
date_default_timezone_set("Asia/Taipei");
//匯入資料庫設定與函式庫
require_once("../db.php"); //匯入資料庫設定檔 (必須)
require_once("../lib/mysql.php"); //匯入資料庫模組 (必須)
//變數設定
$success=FALSE; //整體成功或失敗旗標
$error=Array(); //儲存錯誤訊息用
$tabs=Array(); //儲存頁籤用
$tables=Array(); //儲存表單用
/*=== 應用程式頁籤範本 (只改 APP 名稱 & 複製修改) ===*/
//建立 APP_tabs 資料表 (必須)
$tables[]="APP_tabs"; //"APP" 要改
$data_array["id"]="smallint(6) NOT NULL AUTO_INCREMENT PRIMARY KEY";
$data_array["tab_name"]="varchar(255)";
$data_array["tab_label"]="varchar(255)";
$data_array["tab_link"]="varchar(255)";
$data_array["tab_level"]="tinyint(4)"; //1 (使用者) ~9 (管理者)
$data_array["tab_tip"]="varchar(255)";
$data_array["tab_order"]="tinyint(4)";
$result=create_table("APP_tabs",$data_array); //"APP" 要改
if ($result===TRUE) {$success=TRUE;}
else {$error[]="建立資料表 APP_tabs ... 失敗!";} //"APP" 要改
$data_array=NULL;
//插入 APP_tabs (範本)
$tabs[]="TAB1"; //顯示頁籤標題用
$data_array["tab_name"]="TAB1";
$data_array["tab_label"]="TAB1";
$data_array["tab_link"]="apps/APP.php?op=TAB1"; //"APP" 要改
$data_array["tab_level"]=1;
$data_array["tab_tip"]="TAB1";
$data_array["tab_order"]=1;
$result=insert("APP_tabs", $data_array); //"APP" 要改
if ($result===TRUE) {$success=TRUE;}
else {$error[]="插入資料表 APP_tabs ... 失敗!";} //"APP" 要改
$data_array=NULL;
//插入 APP_tabs (範本)
$tabs[]="TAB2"; //顯示頁籤標題用
$data_array["tab_name"]="TAB2";
$data_array["tab_label"]="TAB2";
$data_array["tab_link"]="apps/APP.php?op=TAB2"; //"APP" 要改
$data_array["tab_level"]=1;
$data_array["tab_tip"]="TAB2";
$data_array["tab_order"]=1;
$result=insert("APP_tabs", $data_array); //"APP" 要改
if ($result===TRUE) {$success=TRUE;}
else {$error[]="插入資料表 APP_tabs ... 失敗!";} //"APP" 要改
$data_array=NULL;
/*--- 新增其他頁籤請複製上面的 TAB1/TAB2 範本於此改寫 ---*/
/*=== 安裝其他資料表範本 (請複製修改, 完成後刪除範本) ===*/
//建立 APP_table1 資料表 (範本)
$tables[]="APP_table1"; //刪除 APP 時用到 (必須)
$data_array["id"]="int(10) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY";
$data_array["field1"]="varchar(255)";
$data_array["field2"]="char(1)";
$data_array["field3"]="text";
$data_array["field4"]="date";
$data_array["field5"]="datetime";
$data_array["field6"]="smallint(6) unsigned NOT NULL DEFAULT '0'";
$data_array["field7"]="tinyint(3) unsigned NOT NULL DEFAULT '10'";
$data_array["field8"]="int(10) unsigned";
$data_array["field9"]="bigint(20) unsigned";
$data_array["field10"]="float unsigned";
$result=create_table("APP_table1",$data_array);
if ($result===TRUE) {$success=TRUE;}
else {$error[]="建立資料表 APP_table1 ... 失敗!";} //"APP_table1" 要改
$data_array=NULL;
//插入 APP_table1 (AUTO_INCREMENT 不用填) (範本)
$SQL="INSERT INTO `app_table1` (".
"`field1`,".
"`field2`,".
"`field3`,".
"`field4`,".
"`field5`,".
"`field6`,".
"`field7`,".
"`field8`,".
"`field9`,".
"`field10`".
"`) VALUES (".
"'',".
"'',".
"'',".
"'',".
"'',".
",".
",".
",".
",".
"".
")";
$result=run_sql($SQL);
if ($result===TRUE) {$success=TRUE;}
else {$error[]="插入資料表 APP_table1 ... 失敗!";} //"APP_table1" 要改
$data_array=NULL;
/*--- 新增其他資料表請複製上面的 APP_table1 範本於此改寫 ---*/
/*=== 更新系統 sys_apps 資料表 (只改 APP 名稱)===*/
//更新系統 sys_apps 資料表的 app_name='程式名稱' 欄位
$data_array["installed"]="Y"; //已安裝 "Y"/"N"
$data_array["show_tabs"]="Y"; //顯示頁籤 "Y"/"N"
$data_array["tab_names"]=join(",",$tabs); //儲存 tabs 以便產生頁籤
$data_array["table_names"]=join(",",$tables); //儲存 tables 以便刪除時清理
$data_array["remark"]="OK"; //安裝結果
$result=update("sys_apps", $data_array, "app_name", "APP"); //"APP" 要改
if ($result===TRUE) {$success=TRUE;}
else {$error[]="更新資料表 sys_apps ... 失敗!";}
$data_array=NULL;
//在 sys_header_links 插入應用程式入口超連結
$data_array["title"]="APP"; //"APP" 要改
$data_array["url"]="javascript:APP()"; //"APP" 要改
$data_array["target"]="_self";
$data_array["sequence"]=1;
$data_array["hint"]="APP"; //"APP" 要改
$result=insert("sys_header_links", $data_array);
if ($result===TRUE) {$success=TRUE;}
else {$error[]="插入資料表 sys_header_links ... 失敗!";}
$data_array=NULL;
/*====== 輸出 JSON 格式之安裝結果 (不用改) ======*/
$arr["result"]=$success ? "success" : "failure";
$arr["error"]=join("<br>",$error);
echo json_encode($arr);
?>
注意,這裡我們用 join() 函式把所記錄的資料表名稱以逗號串接後寫入 table_names 欄位裡, 以備將來要刪除應用程式時斬草除根之用.
而主程式 APP.php 範本如下 :
<?php
/*-----------------------------------------------------------------------------
Title : 應用程式
Translator : Tony
Version : v1.0.0
Prototype : 2015-01-05
Last Updated : 2015-01-05
Usage : 應用程式的主要功能寫在此檔中
Note :
請根據安裝檔內寫入 tabs 的各頁籤 $tab 與各作業 $op 撰寫功能
Ajax/DataTable 的 url 格式 :
$url="apps/app.php?op=get_xxxx";
$url="apps/app.php?op=list_xxxx";
$url="apps/app.php?op=add_xxxx";
$url="apps/app.php?op=remove_xxxx";
$url="apps/app.php?op=update_xxxx";
-----------------------------------------------------------------------------*/
session_start(); //必須啟動才能用 session
header('Content-Type: text/html;charset=UTF-8');
require_once("../db.php"); //匯入資料庫設定檔 (必須)
require_once("../lib/mysql.php"); //匯入資料庫模組 (必須)
require_once("../lib/tools.php"); //匯入工具模組 (選項)
require_once("../lib/parse.php"); //匯入剖析模組 (選項)
require_once("../lib/file.php"); //匯入file模組 (選項)
require_once("../lib/http.php"); //匯入http模組 (選項)
$op=$_REQUEST["op"]; //功能
switch ($op) {
case "TAB1" : { //for ajax
echo "Hello TAB1! The time is ".date("Y-m-d H:i:s");
break;
}
case "TAB2" : { //for ajax
echo "Hello TAB2! The time is ".date("Y-m-d H:i:s");
break;
}
default : { //for rendering
?>
<div id="APP-tab" class="easyui-tabs" data-options="fit:'true'">
<?php
$RS=search("APP_tabs"); //"APP" 要改
if (is_array($RS)) {
for ($i=0; $i<count($RS); $i++) {
?>
<div class="tab" title="<?php echo $RS[$i]["tab_name"] ?>" data-options="href:'<?php echo $RS[$i]["tab_link"] ?>'">
</div>
<?php
} //for
} //if
} //default
} //switch
?>
</div>
以後要寫任何掛在此系統上的應用程式就用此兩範本去擴展即可.
最後, 點選要刪除的 app 列, 再按右上角的刪除鈕就可以刪除應用程式, 它會執行下列事件處理程序 :
$("#remove_app").bind("click",function(){ ... 參考上面});
經過確認後會呼叫 sys.php 的 remove_app 模組來處理刪除工作 :
case "remove_app" : {
$id=$_REQUEST["id"];
$RS=search("sys_apps","id",$id);
$status="success";
$msg="";
if (is_array($RS)) { //有登錄
//刪除此 app 所有資料表
$app_name=$RS[0]["app_name"];
$installed=$RS[0]["installed"];
if ($installed=="Y") { //已安裝
$tables=explode(",",$RS[0]["table_names"]);
for ($i=0; $i<count($tables); $i++) {
$result=drop_table($tables[$i]);
if ($result===FALSE) {
$status="failure";
$msg .= "刪除資料表 ".$tables[$i]." 失敗<br>";
}
}
//從 sys_header_links 刪除此 app 超連結
$result=delete_record("sys_header_links","title",$app_name);
if ($result===FALSE) {
$status="failure";
$msg .= "刪除應用程式在 sys_header_links 之超連結失敗<br>";
}
} //installed
//從 sys_apps 刪除此 app 登錄
$result=delete_record("sys_apps","id",$id);
if ($result===FALSE) {
$status="failure";
$msg .= "刪除應用程式在 sys_apps 之登錄失敗<br>";
}
//刪除應用程式檔案
$result=delete_file("./apps/".$app_name.".php");
if ($result==FALSE) {
$status="failure";
$msg .= $app_name.".php 刪除失敗<br>";
}
$result=delete_file("./apps/".$app_name."_install.php");
if ($result==FALSE) {
$status="failure";
$msg .= $app_name."_install.php 刪除失敗<br>";
}
} //registered
else { //未上傳登錄
$status="failure";
$msg="刪除失敗 : 應用程式不存在!";
}
$arr["status"]=$status;
$arr["msg"]=$msg;
echo json_encode($arr);
break;
}
這裡我們先去讀取 sys_apps 中該應用程式的 table_names 欄位, 用 explode() 方法拆解出此應用所屬之資料表後, 在迴圈中用 drop_table 來刪除資料表. 另外就是刪除 sys_header_links 上的超連結紀錄. 最後去 apps/ 目錄下刪除此應用程式及其安裝檔案即可, 其中使用了收錄在 lib/file.php 函式庫中的 delete_file(), 此函式其實很簡單, 就是 PHP 的 unlink :
/*-----------------------------------------------------------------------------
delete_file($filename)
功能 :
此函數指定路徑下之檔案與目錄列表.
參數 :
$filename : 檔案名稱 (相對路徑, 例如 log/visitors.txt)
傳回值 :
成功傳回 TRUE, 失敗傳回 FALSE.
範例 :
$result=delete_file("a.txt");
$result=delete_file("./a.txt");
$result=delete_file("files/a.txt");
-----------------------------------------------------------------------------*/
function delete_file($filename) {
return unlink($filename);
}
因今天 (1/27) 下午才會進辦公室, 所以趁著早上閒閒把應用程式部分記錄完了, 希望以後要回頭參考時自己還看得懂, 雖然花了很多時間來紀錄, 但是值得, 因為這是此簡易架站系統最重要的部分, 而且隨著年華老去, 記性已不若以往, 上個月寫的東西這個月可能就印象模糊了, 唉.
3 則留言 :
Dear Tony, 近日研究 EasyUI 發現您的 blog有非常多有用的資源,也看到有其他朋友向您索取 EsyUICMS 源碼,是否方便也向您索取一份研究學習呢?謝謝。
jack3389@gmail.com
Dear Jack,
源碼沒問題, 隨信附上目前的 zip 檔, 獻醜了. 因最近分心研究 Arduino, EasyUICMS 還有一些功能未寫完, 且 PHP & MySQL 資安部分尚未周全, 等我 Arduino 搞定後回師再戰. 有發現 BUG 記得告訴我.
Tony 敬上
Hi Tony,
好像沒有收到您的 zip 檔,再麻煩您寄一次囉。
謝謝
張貼留言