2015年1月15日 星期四

分年

今天請假回鄉下幫母親做分年, 早上 6:50 出發順路載二姨丈, 回到鄉下家時快八點了, 一看曬穀場有一台車, 原來是表哥跟阿興剛到.
8:20 出發前往慈恩塔, 順路去買金香與粄, 用的紅龜與發粄各一包, 給親友的 12 小包 (這放桌下不拿來拜). 今年可能因為有人指揮交通, 所以車子還可以開進去, 但是廣場人山人海, 到處香煙瀰漫. 我先點香去塔裡向母親稟告牲禮擺放桌次, 然後回供桌與諸親友焚香祭拜.
來給媽分年的親友 : 阿信叔, 阿見叔, 阿將伯, 小舅媽, 小阿姨, 左營阿姨, 二姨丈, 表哥與阿興. 感謝. 分年者, 意思是為今年往生者辦過年也, 每年農曆 12 月 25 日辦理, 但今年閏九月, 故提前為 11 月 25 日.
中午在美綠訂一桌 2500 請阿姨舅媽他們吃午飯, 因習俗分年完即各自回家, 不可在家請客. 美綠之前來過一次, 覺得這裡環境佳, 離家又近, 請客聚餐甚好.

2015年1月14日 星期三

用 jQuery EasyUI 打造輕量級 CMS (八)

這幾天主要在寫檔案上傳下載功能, 本以為很簡單, 只要套用應用程式上傳的程式碼即可快速解決, 沒想到弄了兩三天還沒搞定, 而且還發現之前寫的檔案函式庫 file.php 中的上傳函式有 bug.

寫檔案上傳下載參考了下列文章, 特別是下載部分, 一般的 PHP 書籍探討下載的較少, 我主要是參考 "php 檔案下載" 這篇, 採用 http 標頭來傳送檔案.

# Download a file by jQuery.Ajax
# 透過Javascript觸發檔案下載
# php 檔案下載
# PHP 如何取得 現在檔案的目錄 與 上層目錄
# Download File Using jQuery
# jQuery File Download Plugin for Ajax
# ASP.Net : Download file using jQuery
# jquery.fileDownload.js & jQuery UI Dialog

這陣子系統規劃已做了大幅度改變, 來不及紀錄, 因此只好就現況說明, 首先必須在系統安裝檔 install.php 增加一個 sys_files 來記錄已上傳的檔案資料 :

    //建立 sys_files 資料表
    $data_array["id"]="smallint(6) NOT NULL AUTO_INCREMENT PRIMARY KEY";
    $data_array["file_name"]="varchar(255)";          //檔案名稱
    $data_array["file_note"]="varchar(255)";          //檔案說明
    $data_array["file_type"]="varchar(255)";          //檔案類型
    $data_array["file_size"]="varchar(255)";          //檔案大小
    $data_array["uploader"]="varchar(255)";           //上傳者
    $data_array["upload_time"]="datetime";            //上傳時間
    $data_array["download_count"]="int(10) unsigned"; //下載次數
    $data_array["public"]="char(1)";                  //公開 "Y"/"N"
    $result=create_table("sys_files",$data_array);
    if ($result) {$msg .= "建立資料表 sys_files ... 完成!<br>";}

其中 file_name, file_type, file_size 用來儲存上傳函式的傳回值, public 用來讓上傳者選擇是否讓所上傳的檔案可被其他人下載. download_count 用來統計下載次數, 每次被下載時, 此欄之值就加 1, 因此它須賦予初值 0.

然後在系統功能檔 sys.php 裡增添一個 files 的 case :

  case "files" : {
?>

<?php
      break;
      }

我們的檔案列表用 datagrid 來顯示, 就放在上面的 ?> 與 <?php 中間 :

<div class="tab" title="檔案">
  <!--檔案 sys_files 列表-->
  <table id="sys_files" title="檔案列表" style="width:auto" data-options="tools:'#files_tools',toolbar:'#files_toolbar'"></table>
  <div id="files_tools">
    <a href="#" id="upload_file" class="icon-add" title="上傳檔案"></a>
    <a href="#" id="remove_file" class="icon-remove" title="刪除檔案"></a>
    <a href="#" id="reload_files" class="icon-reload" title="重新載入"></a>
    <a href="#" id="download_file" class="icon-save" title="下載另存"></a>
  </div>
  <div id="files_toolbar" style="text-align:right;padding:2px;">
    <select id="files_search_field" class="easyui-combobox" data-options="panelHeight:120">
      <option value="file_name">檔案名稱</option>
      <option value="file_note">檔案說明</option>
      <option value="file_type">檔案類型</option>
      <option value="uploader">上傳者</option>
      <option value="upload_time">上傳時間</option>
    </select>
    <input id="files_search_what" class="easyui-textbox">
  </div>

上面的 HTML 主要就是在頁籤裡頭放一個 id="sys_files" 的 datagrid, 它具有一個工具 (tools) 以及一個工具按鈕列 (toolbars), 前者用來放置控制檔案操作的小圖示 (上傳, 刪除, 下載, 更新); 後者用來放置搜尋框.

當然這個 datagrid 需要 JS 來初始化 :

    //檔案 sys_files
    $('#sys_files').datagrid({
      columns:[[
        {field:'id',title:'id',sortable:true},
        {field:'file_name',title:'檔案名稱',sortable:true},
        {field:'file_note',title:'檔案說明',sortable:true},
        {field:'file_type',title:'檔案類型',sortable:true},
        {field:'file_size',title:'檔案大小',align:'right',sortable:true},
        {field:'uploader',title:'上傳者',sortable:true},
        {field:'upload_time',title:'上傳時間',sortable:true},
        {field:'download_count',title:'下載次數',align:'right',sortable:true}
        ]],
      url:"sys.php",
      queryParams:{op:"list_files"},
      fitColumns:true,
      singleSelect:true,
      pagination:true,
      pageSize:10,
      rownumbers:true,
      onDblClickRow:function(index,row){
        var url="sys.php?op=download_file&id=" + row.id +
                "&file_name=" + row.file_name;
        window.open(url);
        }
      });
    $("#files_search_what").textbox({
      icons:[{
        iconCls:"icon-search",
        handler:function(e){
          $('#sys_files').datagrid("load",{
            op:"list_files",
            search_field:$("#files_search_field").combobox("getValue"),
            search_what:$("#files_search_what").textbox("getValue")
            });        
          }
        }]
      });

這裡很重要的一點是, url 與 queryParams 這兩個參數指定了 datagrid 的資料來源, 所有架站系統的功能我都整合在 sys.php 這個檔案裡 (雖然專家不建議這麼做, 好像難維護, 但我討厭一大堆零零散散的小檔案), 然後用 op 參數與 switch-case 指令來選擇要執行的功能. 所以要在 sys.php 裡增添一個 case=list_files 的模組來輸出 datagrid 所需的 json :

  case "list_files" : {
    $page=isset($_REQUEST['page']) ? intval($_REQUEST['page']) : 1;
    $rows=isset($_REQUEST['rows']) ? intval($_REQUEST['rows']) : 10;
    $sort=isset($_REQUEST['sort']) ? $_REQUEST['sort'] : 'upload_time';
    $order=isset($_REQUEST['order']) ? $_REQUEST['order'] : 'desc';
    if (isset($_REQUEST['search_field'])) { //有 search
      if ($_SESSION["user_level"]==9) { //管理員 (搜尋全部)
        $where="WHERE ".$_REQUEST['search_field']." LIKE '%".
               $_REQUEST['search_what']."%'";
        }
      else { //非管理員 (搜尋公開的與自己上傳的)
        $where="WHERE (public='Y' OR uploader='".$_SESSION["user_name"].
               "') AND ".$_REQUEST['search_field']." LIKE '%".
               $_REQUEST['search_what']."%'";
        }
      }
    else { //無 search (顯示全部)
      if ($_SESSION["user_level"]==9) {$where="";} //管理員
      else { //非管理員 (只能看得到公開的與自己上傳的)
        $where="WHERE public='Y' OR uploader='".$_SESSION["user_name"]."'";
        }          
      }
    $start=($page-1) * $rows;  //本頁第一個列索引 (0 起始)
    $SQL="SELECT COUNT(*) FROM `sys_files`";
    $RS=run_sql($SQL);
    $total=$RS[0][0]; //紀錄總筆數
    $SQL="SELECT * FROM sys_files ".$where." ORDER BY ".
         $sort." ".$order." LIMIT ".$start.",".$rows;
    $RS=run_sql($SQL);
    $files=Array();
    if (is_array($RS)) {
      for ($i=0; $i<count($RS); $i++) {
        $files[$i]=Array("id" => $RS[$i]["id"],
                         "file_name" => $RS[$i]["file_name"],
                         "file_note" => $RS[$i]["file_note"],
                         "file_type" => $RS[$i]["file_type"],
                         "file_size" => $RS[$i]["file_size"],
                         "uploader" => $RS[$i]["uploader"],
                         "upload_time" => $RS[$i]["upload_time"],
                         "download_count" => $RS[$i]["download_count"]
                         );
        }
      }
    $arr=array("total" => $total, "rows" => $files);
    echo json_encode($arr);
    break;
    }

上面預設排序是依上傳時間 upload_time 倒序排列, 亦即最新上傳的會列在最上面. 管理員可以看到所有上傳的檔案列表, 而一般使用者只能看到自己上傳的檔案以及別人公開的檔案, 公開與否由 public 這欄位來決定, 上傳時若未指明, 預設為公開.

其次, 我們替 datagrid 指定了 Double Click 事件, 當用戶雙擊任何一列時就會觸發此事件, 並傳回其索引 index 與列物件 row, 透過 row.id 與 row.file_name 即可取得要下載的檔案的 id 與名稱, 組成 url 後開啟一個新視窗來進行下載.


接著是處理檔案上傳按鈕圖示 (id="upload_file") 被按下時的情況, 這時要顯示一個 dialog 交談框, 讓使用者上傳檔案, 所以在上面的 HTML 後面添加 :

  <!--上傳檔案表單對話框-->
  <div id="upload_file_dialog" class="easyui-dialog" title="上傳應用程式" style="width:420px;height:225px;"  data-options="closed:'true',buttons:'#upload_file_buttons'">
    <form id="upload_file_form" style="padding:15px" action="sys.php?op=upload_file" method="post" enctype="multipart/form-data" target="upload_target">
      <div style="margin:10px">
        <label style="width:60px;display:inline-block;">檔案 : </label>
        <input name="upload" class="easyui-filebox" data-options="buttonText:'選擇檔案',prompt:'',required:true" style="width:280px">
      </div>
      <div style="margin:10px">
        <label style="width:60px;display:inline-block;">說明 : </label>
        <input name="file_note" class="easyui-textbox" style="width:280px" required="true">
      </div>
      <div style="margin:10px">
        <label style="width:60px;display:inline-block;">公開 : </label>
        <select name="public" class="easyui-combobox" panelHeight="50"  style="width:80px">
          <option value="Y">Y</option>
          <option value="N">N</option>
        </select>
        <img src="images/uploading.gif" id="uploading" style="visibility:hidden;margin-left:20px;">
      </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_file_buttons" style="padding-right:15px;">
    <a href="#" id="clear_file" class="easyui-linkbutton" iconCls="icon-clear" style="width:90px">重設</a>
    <a href="#" id="upload_file_go" class="easyui-linkbutton" iconCls="icon-ok" style="width:90px">上傳</a>
  </div>
</div>

其事件處理的 JS 如下 :

    $("#clear_file").bind("click",function(){
      $("#upload_file_form")[0].reset();
      });
    $("#upload_file_go").bind("click",function(){
      $("#uploading").css("visibility","visible");
      $("#upload_file_form").form("submit");
      });
    $("#upload_file").bind("click",function(){
      $("#upload_file_dialog").dialog("open").dialog("setTitle","上傳檔案");
      $("#upload_file_form").form("clear");
      });


這個 dialog 對話框預設不可以顯示, 因此在 data-options 中要將 closed 屬性設為 true, 必須等按下右上角的 upload_file 按鈕時才呼叫 open 方法將其開啟. 這 dialog 內放了一個表單, 注意, 其編碼型態 enctype 須設為 multipart/form-data 以便傳送檔案. 而後端程式執行結果的輸出目的 target 設為 upload_target, 這是 dialog 對話框中一個看不到 (大小為 0) 的 iframe 的 id, 這 iframe 是作為一個前後端的中介站, 用來執行上傳完畢時要顯示的訊息框程式. 因為檔案上傳後, 前端網頁程式並不知道後端何時會完成收檔, 因此也無法適時跳出一個訊息框來顯示上傳完成之訊息, 因此我們偷偷在前端網頁設置一個看不到的 iframe 當窗口, 當後端完成檔案接收後, 就輸出一個顯示傳檔已完成的 JS 程式丟回這個 iframe 中執行來執行, 讓使用者誤以為我們前端網頁能預測傳檔何時能完成呢, 真是巧妙啊.

顯示上傳結果的 JS 如下之 upload_callback() 所示, 它首先會將顯示上傳中的 gif 動畫圖檔還原為隱藏狀態, 然後關閉上傳對話框, 重新載入 datagrid, 最後顯示後端傳回來的上傳結果 (參數 status 與 msg) :

    <!--Top Level Script -->
    function upload_callback(status,msg) { //必須在最上層, 不可放在 $ 內
      $("#uploading").css("visibility","hidden");
      $("#upload_file_dialog").dialog("close");
      $("#sys_files").datagrid("reload",{op:"list_files"});
      if (status=="success") {var msg=msg + '檔案上傳成功! <br>';}
      else {var msg=msg + '檔案上傳失敗, 請重新上傳! <br>';}
      $.messager.alert("上傳結果", msg);
      }

在此 dialog 中用來指定要上傳之檔案的元件是 id=upload 的 easyui-filebox 元件, 按下其選擇檔案按鈕會跳出作業系統的檔案選擇對話框, 當選定檔案後, 其文字欄位會顯示檔案名稱, 但路徑一律顯示 C:\fakepath. 按下上傳鈕會向後端提交表單, 這時對話框中會冒出一個 uploading 的動畫圖形, 顯示還在上傳中, 這是因為我們在對話框中偷偷放了一個預設看不見 (visibility:none) 的 uploading.gif 圖檔, 這是我在網路上蒐來的, 因為 EasyUI 本身沒有提供, 我把它放在根目錄的 images 下. 當檔案接收完成, 必須再度將此圖檔隱藏起來.

所以在 sys.php 中須增添一個 case=upload_file 來處理上傳檔案之接收 :

  case "upload_file" : {
    $status="success";       //預設上傳成功
    $msg="";                     //執行結果字串
    $upload_dir="./files/";  //設定上傳目錄
    $file_note=$_REQUEST["file_note"];     //取得檔案說明
    $public=empty($_REQUEST["public"])?"Y":$_REQUEST["public"]; //取得公開與否 (預設 Y)
    $result=upload_file("upload", $upload_dir);
    if ($result !== FALSE) {
      $msg .= $result["name"]." (".$result["size"]." bytes).<br>";
      $data_array["file_name"]=$result["name"];
      $data_array["file_note"]=$file_note;
      $data_array["file_type"]=$result["type"];
      $data_array["file_size"]=$result["size"];
      $data_array["uploader"]=$_SESSION["user_name"];
      $data_array["upload_time"]=date("Y-m-d H:i:s");
      $data_array["download_count"]=0;
      $data_array["public"]=$public;
      insert("sys_files", $data_array);
      } //end of if
    else { //上傳失敗
      $msg .= $file_name." 上傳失敗 (".$result["file_error"].")!<br>";
      $status="failure";
      } //end of else
    $para='"'.$status.'","'.$msg.'"'; //回傳回呼函式之參數
?>
<!-- 輸出 script 到 $op=files 中的 iframe 並執行其中的回呼函式 -->
<script language="javascript" type="text/javascript">
  window.top.window.upload_callback(<?php echo $para; ?>);
</script>
<?php
    break;
    }

上面這個 JS 是就是檔案接收程式最後的輸出, 目的地就是前端網頁表單的 target 所指的 iframe 元素, 它會呼叫我們放在前端網頁視窗最上層的函式 upload_callback(), 其實就是把參數傳進去顯示上傳結果而已.

這個檔案接收程式使用了一個自建的函式 upload_file(), 我把它寫在 /lib/ 下的 file.php 中, 因此必須載入此函式庫才能使用 :

/*-----------------------------------------------------------------------------
upload_file($uploader,$path="./")
功能 :
  此函數將上傳之單一檔案, 由暫存區移至指定路徑下.
參數 :                                
  $uploader : 上傳元件名稱, 即 <input type="file" name="uploader"> 中之 name 值
  $path     : 檔案儲存路徑, 例如 "./images"
傳回值 :
  成功傳回陣列, 失敗傳回 FALSE. 陣列有三個元素 :
  $result["name"]=檔案名稱
  $result["type"]=檔案類型
  $result["size"]=檔案大小
範例 :
  <input type="file" name="uploader">
  $result=upload_file("uploader", "./images");
  echo "檔案 ".$result["name"]." 上傳成功 (".$result["size"]." bytes)";
-----------------------------------------------------------------------------*/
function upload_file($uploader,$path="./") {
  if ($_FILES[$uploader]["error"]==0) { //上傳成功
      $tmp_name=$_FILES[$uploader]["tmp_name"];
      $new_name=$path.$_FILES[$uploader]["name"];
      if (move_uploaded_file($tmp_name, $new_name)) { //移動成功
          $result["name"]=$_FILES[$uploader]["name"];
          $result["type"]=$_FILES[$uploader]["type"];
          $result["size"]=$_FILES[$uploader]["size"];
          } //end of if
      else {$result=FALSE;} //移動失敗
      } //end of if
  else {$result=FALSE;} //上傳失敗
  return $result;
  }

此函式只要傳入兩個參數, 第一參數 uploader 就是前端上傳元件 input 的 name, 這裡就是 "upload", 第二參數是上傳目錄, 這裡要傳入 "./files/". 如果上傳成功, 它會傳回一個關聯式陣列, 其元素分別是 name (檔案名稱), type (檔案類型), 與 size (檔案大小). 若上傳失敗就傳回 false.

2015-01-26 補充 :

今天回頭測試下載功能, 發現中文檔名的檔案無法下載, Google 了一下, 發現是 PHP 對於中文檔名支援問題, 試了 iconv 與 mb_convert_encoding 兩種函式將 big5 轉成 utf-8 來處理都不得法, 上傳 OK 但下載 NG (因為 PHP 的 basename 不支援中文) :

$file_name=$_FILES[$uploader]["name"];
$new_name=$path.mb_convert_encoding($file_name,"big5","utf-8");

我參考葉建榮的 "PHP 與 MySQL 網站規劃管理應用" 這本書的作法, 乾脆限制只能上傳英數檔名之檔案, 然後用檔案說明欄描述之. 當然這樣做有些不完美, 或許可以參考下面這個網站的建議處理中文檔名上傳問題 :

# php 中文檔名上傳問題

他的做法是上傳到伺服器後改以英數字檔名儲存 (例如日期時間+tmp_name), 但是把中文檔名記錄在資料庫裡, 檔案列表顯示的是資料庫所記錄的原始中文檔名, 但下載時卻連結到相對應的英數字檔名. 這個做法不錯, 但是必須於資料表中增加一個欄位來記錄真實檔名, 以便與原始中文檔名對應.


2015年1月11日 星期日

監護與分年事宜

這周忙著為新版工作日誌趕工, 所以沒空記事, 補記如下 :  

週三 (1/7) 下午請假半天回鄉下, 因法官要去幫阿蘭做監護宣告. 後續就等法官裁定. 由於案件單純, 所以應該很快就有結果. 這真的非常感謝林法官的協助, 以及社工組長的提點, 多年以來我都不知道有這個法律問題存在.

今天我跟菁菁去慈恩塔給媽上香, 沒看到師姐, 所以乾脆騎摩托車上去觀音壇找她, 詢問下周四分年要幾點為宜, 她說五點半就開門, 越晚人越多. 我說沒辦法這麼早, 就算九點人多也沒關係.

屋旁的小圳這兩天水非常豐沛, 也很清澈, 但要提到菜園去就累了. 我看做水車的計畫要盡快進行, 然後再設計自動灑水器就更完美了. 等菁菁月考完準備去找南瓜苗以及芭樂來種.




滑世代

今天在 Tsung's blog 看到 Youtube 的這個影片 :

# 智慧型手機世代的沉痛反思

我以前也有同樣的想法, 那時是在餐廳吃飯時, 看到隔壁桌的小男孩不斷地跟媽媽講幼稚園老師今天教怎麼做母親節的康乃馨, 但那個媽媽卻只顧著玩 Line 或 Facebook. 也看過一對情侶坐下後忙著滑手機, 沒有半句對話, 或許他們的溝通都是在 Line 上面完成的吧.

臉書上 po 的一大堆孩子生活照, 鐵定有親朋好友的 "讚", "好可愛喲", "真羨慕" 之類的. 呈現的永遠都是那麼美好. 但除了回覆這些外, 你還有其他選項嗎? 這就是問題了 (假).

我對這些都沒啥興趣, 臉書我是那種要用時才發現密碼忘記了那型的, Line 也是短則一天, 長則三天, 更長是一周想到時才看, 已讀不回更是常態. 人生有很多事要幹, 沒那種閒工夫去關心雜事, 坦白說, 我是那種自戀又自私的人啊!

話說回來, 按了讚, 人生改變了嗎? 那就多按幾次.



2015年1月6日 星期二

薛丁格的女朋友

關於量子力學的奠基者薛丁格, 我以前只聽過 "薛丁格的貓", 對於他的情史則聞所未聞. 今天在曹天元寫的 "量子物理史話" 這本書裡讀到 (p150), 這位繼愛因斯坦後不世出的偉大物理學家, 他的卓越學術貢獻可能得一部分感謝他的女朋友們, 特別是在 1925 年底, 當他的婚姻出現裂痕時, 來到瑞士蘇黎世陪伴他的 "前女友", 因為接下來的一年中, 薛丁格接連發表了六篇關於量子力學的主要論文 (愛因斯坦也是在 1905 年暴衝似地發表一連串的劃時代論文). 除此之外, 薛丁格還有許多情人, 風流史相當豐富, 看來情慾與賀爾蒙或許是文明前進的動力也說不定, 對於革命家是如此, 物理學家也不例外啊.

薛丁格, 奧地利理論物理學家, 1887 年生於維也納, 1961 年死於肺結核, 得年 75 歲. 有別於海森堡與坡恩等人以龐雜的矩陣運算來描述量子力學, 他從德布羅意的波粒二元性得到啟發, 於 1926 年提出了後來被稱為薛丁格方程式的波動方程, 以一個簡潔的偏微分方程式來描述量子狀態. 他曾前往柏林任教, 但1933 年納粹上台後即離開德國前往英國牛津, 同年與英國物理學家狄拉克因為對量子力學的貢獻共同獲得諾貝爾物理學獎.

雖然薛丁格本人推證自己的波動力學與海森堡的矩陣力學是等價的, 兩者可以互相推導, 在數學形式上雖然統一了, 但兩人對彼此看法卻從不苟同, 互相論戰不懈, 分別站在波動說與粒子說的兩端毫不妥協.

量子論令我著迷處並非其高深的數學方程式或物理意涵 (事實上我根本看不懂), 而是在其哲學面向. 我對薛丁格的貓悖論中的疊加態感到迷惑, 其所衍伸的多世界詮釋, 更讓我想到所謂的平行宇宙, 難不成, 其他的世界還有許多個我同時存在?

# 薛丁格方程式
# 奧地利文化17.物理學家埃爾溫•薛丁格
# 誰來對我高談闊論「薛丁格方程式」



 
 

2015年1月4日 星期日

收假囉

好快, 四天假期飛也似地過去了, 完成之事如下 :
  1. 1/1 看電影 (哈比人五軍之戰)
    EasyuiCMS 完成使用者管理.
  2. 1/2 參訪慈惠教養院
    我從內門經南化到左鎮, 回程則走新化上國8轉國3, 繞一圈約 115 公里. 雖然徒勞無功, 但了解也更深.
    EasyuiCMS 完成到訪者管理. 
  3. 1/3 參訪信望愛育幼院.
    十多年沒去了, 已轉型為中途之家. 也是徒勞無功, 但至少證實阿蘭的養兄已離世.
    EasyuiCMS 完成導覽區, 標題連結管理.
  4. 1/4 去塔裡給媽上香.
    EasyuiCMS 完成應用程式管理.

2015年1月3日 星期六

人多的地方不要去

這次上海跨年發生踩死人事件令人遺憾, 印證 "人多的地方不要去" 這句老掉牙的警語是多麼有智慧. 每年看一堆嚷著要去哪裡跨年的年輕人, 心裡就會想起這句話. 跨年的意義是甚麼? 是慶賀? 是期許? 大家恐怕只是當成一個活動罷了. 為什麼不跨日, 跨周, 跨月? 佛家說, 是日已過, 命亦隨減, 如少水魚, 斯有何樂? 一年過了, 壽命亦減一歲, 到底是在慶祝甚麼, 高興甚麼咧?

上海外灘我在 4 年前出差時逛過, 夜晚華燈初上照映整個黃浦江非常美麗. 只是中國人口太多了, 光是大上海就超過全台灣, 跨年人擠人不難理解. 只是悲劇帶給我們甚麼教訓? 很多活動的背後其實只是商業操作與包裝, 目的只是要藉人潮創造錢潮而已 (我的基督徒同事說, 他們不了解為何要過聖誕節, 耶穌的生日對他們而言不重要).

除了跨年, 還有哪些是人多的地方?
  1. 大型演唱會
  2. 球場賽事 (尤其是容易暴動的足球賽) 
  3. 選舉造勢活動
  4. 煙火秀
萬不得已必須參加, 到現場首先要注意逃難動線, 避免陷入人包人的險境, 不出事則已, 一旦發生推擠或衝突, 就算是會飛簷走壁的武林高手, 恐怕也插翅難飛.

"人多的地方不要去", 這道理至簡至易, 三歲小孩皆懂得, 但是八十老翁行不得.


2015年1月1日 星期四

輪子的可貴

這幾天寫 EasyuiCMS 進展神速, 但週五時長官交付任務而暫停, 因為帳務出問題須比對與校正, 人工方式費時費力, 希望能寫個程式來自動處理, 我說正在忙新版工作日誌, 但上面要求中午就能得到結果, 只得立刻處理.

幸好以前工具都 "穿便便", 花了三個小時組裝測試, 吃完中飯就交差了. 此事讓我深感以前花時間打造自己的輪子是對的, 任務來時就能快速派上用場. 用心寫日誌也是如此, 與其說是與同好分享研究成果那麼好聽, 還不如說是為了自己迅速恢復功力較實在吧? 畢竟自己寫的東西自己最了解.


哈比人五軍之戰

今天是 2015 的第一天, 早上記錯時間 8:30 跑去美奇萊買哈比人的電影票, 結果櫃台還沒開, 清潔阿桑說要 9:30, 白跑了一趟.

去年開始元旦全家到美奇萊看電影, 也是哈比人電影 (荒谷惡龍), 用台新信用卡支付票價平日 66 折, 假日固定 180, 用會員卡則為 170, 所以花 100 元更新會員卡, 再用會員卡訂四張 (我是本月壽星免費), 信用卡訂一張, 總共 170*3  + 180=690, 加上會員卡 100 共 790, 平均一張票 160, 這樣應該是最便宜的買法.

過去的一年中, 上半年在忙工作, 下半年則因母親住院, 往生與辦理後事而奔波, 2014 對我而言是不祥與悲傷的一年. 母親的離世讓我真正體悟無常之迅速, 修行孝親要及早.

進入 2015, 我也邁入知天命之年, 人生後半段正式上場. 功名利祿早已是定數, 並非單單努力可得. 若道千帆過盡皆不是, 如何能識得本地風光?