2007-02-18

讓側邊列 (Sidebar) 的元件能夠展開和摺疊 (Element Toggling)

前一篇提到的是隱藏和顯示列的元件,而本文介紹的是這些顯示的元件透過一些技巧可以動態展開和摺疊起來。Sidebar 的元件有的時候可能很長(尤其是網站列表或是 Archive 之類的),想要顯示該元件又不希望過長的元件會影響版面的美觀,這時候我們可以保留它,透過 Javascript 的技巧去動態切換顯示與否。而我看到人家的作法,要做一些麻煩的修改,參考對方的原始碼我寫了因應這個動作的 Javascript,一方面更精簡 Hack 的步驟,也減少原始碼的修改。

根據原文,還有上一篇對 Widget 程式碼的瞭解,要做的工作就是:第一,把想展開摺疊的區塊找出來,第二,做一個按鈕或連結,當按下去的時候,去切換該區塊的狀態(display inline or none)。打開完整範本原始碼,首先貼入處理這個動作的 Javascript (貼在 <head> </head> 標籤內、</style> 後面):

<script type='text/javascript'>
<!-- Function used for Sidebar Element Toggling: ElementToggle()-->
function ElementToggle(id) {
  var element = document.getElementById(id).getElementsByTagName(&#39;div&#39;);
  for(i = 0; i &lt; element.length; i++) {
    attribute = element[i].getAttribute(&#39;id&#39;);
    if(attribute == &#39;toggle&#39;)
    {
      if (element[i].style.display == &#39;none&#39;)
        element[i].style.display = &#39;inline&#39;;
      else
        element[i].style.display = &#39;none&#39;;
    }
  }
}   
</script>

接下來,要去修改元件 (Widget) 的程式碼。同樣以個人資料 (Profile) 元件為例,找 <b:widget ...> 開頭 </b:widget> 結尾的區段,應該是類似下面這樣:

<b:widget id='Profile1' locked='false' title='AUTHOR PROFILE' type='Profile'>
  <b:includable id='main'>
     <b:if cond='data:title != ""'>
      <h2><data:title/></h2>
    </b:if>
    <div class='widget-content'>    
     ............. (程式內文略過不表)
  </b:includable>
</b:widget>

這個元件的 ID = Profile1,是範本裡用來識別該元件的方法,接下來要插入動態切換的連結,為求簡單我先不顧美觀,直接插入像下面的原始碼:

<b:widget id='Profile1' locked='false' title='AUTHOR PROFILE' type='Profile'>
<b:includable id='main'>
     <b:if cond='data:title != ""'>
      <h2><a href='javascript:void(0);' onclick='javascript:ElementToggle("Profile1");'>[+/-]</a>
      <data:title/></h2>
    </b:if>
    <div class='widget-content' id='toggle' style='display:none;'>
     ............. (程式內文略過不表)
  </b:includable>
</b:widget>

有兩個部分要解釋一下。第一,塞入元件標題 data:title 前面的那行,就是呼叫 Javascript 的連結,其中有一個變數:Profile1,就是告訴 Javascript 你要動態切換哪一個元件,所以如果你有多個不同元件要摺疊,記得除了呼叫切換的這一行要塞入該元件原始碼中,還要更改 ID 名稱。第二個部分,<div class='widget-content'> 這行標籤開始就是元件的內容,也是要被折疊或展開的區塊,如果你希望該文件「預設」就被折疊起來,那就要在後面加入 CSS Style 去起始隱藏它,如果預設是被展開的,就把後面紅色的 style='display:none;' 部分拿掉就行了。最後,如果對以上樣式不滿意,或是想貼用圖片當切換按鈕,記得調整 <a> 連結那行的內容和位置就可以了!

回應: 94

 

2007-02-16

讓側邊列 (Sidebar) 的元件只在首頁顯示/不顯示

Blogger 頁面旁邊的 Sidebar 可以加掛很多模組,但是只要塞入一個元件 (Element),在任何頁面就都一定會出現。如果在某些條件下,特定元件可以選擇顯示或不顯示,一方面畫面也可以更清爽,少讀了某些元件網頁顯示也會更快更有效率。方法很簡單,只要在元件原始碼的外面加入 if 的條件判斷句,「當在什麼頁面的時候」,才會顯示元件的內容,就可以達到這個目的了。下面整理修改的範例,還有幾個常見的判斷條件。

首先,打開範本 (Template) 的完整 HTML 編修模式 (要展開 Widget),找 <b:widget ...> 開頭 </b:widget> 結尾的區段,這樣一個區段就代表一個元件,這個元件的 id, title 和 type 有助於瞭解到底是那個元件。舉個例子,像下面這段:

<b:widget id='Profile1' locked='false' title='AUTHOR PROFILE' type='Profile'>
  <b:includable id='main'>
     <b:if cond='data:title != ""'>
     ............. (程式內文略過不表)
  </b:includable>
</b:widget>

就是一個顯示作者個人資料 (Profile) 的元件,如果我希望到了內頁,也就是單篇文章完整顯示的頁面時,不要顯示這個個人資料的元件,我只要在元件的區段內塞入一個 if 的條件判斷句,就可以輕易地顯示或隱藏元件了。例如:

<b:widget id='Profile1' locked='false' title='AUTHOR PROFILE' type='Profile'>
  <b:includable id='main'>
  <b:if cond='data:blog.pageType != "item"'>
     <b:if cond='data:title != ""'>
     ............. (程式內文略過不表)
  </b:if>
  </b:includable>
</b:widget>

只要塞入兩行,像是上面範例的紅色那兩行判斷句就行了。(缺點:如果和我一樣元件樣式有外框處理的話,即使試圖把元件藏起來,外框的上緣和下緣的部分還是不能完全消失)。而那行判斷 (if cond='data:blog.pageType != "item") 的意思是:如果現在所在的頁面不是"內頁"的話,才會顯示該元件 (Profile)。只要把 != 改成 ==,判斷句就變成"只有在內頁的時候才會顯示該元件"了。這類判斷所在頁面的判斷句有幾種,整理如下:

<b:if cond='data:blog.pageType != "item"'>           (如果現在不在內頁的話)
<b:if cond='data:blog.pageType != "archive"'>        (如果現在不在 Archive 存檔頁面的話)
<b:if cond='data:blog.pageType != "index"'>          (如果現在不在 Label 標籤分類頁面的話)
<b:if cond='data:blog.url != data:blog.homepageUrl'> (如果現在不在首頁的話)

有了以上的幾種判斷句,我想不只可以控制 Sidebar 上的元件在哪種頁面顯示,任何範本裡的功能或操作也可以搭配上述條件使用,越玩真是越覺得,Blogger 你實在是太有彈性了啊!

參考文章:
如何讓某些元件只在首頁/內頁顯示
Blogger Help: Layouts Data Tags

回應: 21

 

首頁即時可展開的留言回應 (Comment Toggling)

Blogger 中唯一不能自訂樣式的介面,就只有留言回應的系統,主要的原因應該是留言者的身份認證所致,那個部分有太多的檢查、而且又不容易模組化。即使用了自訂網域,使用者一旦留言馬上就露了餡,看得出來這個 Blog 是 host 在 Blogger 上面的,不過這不是本文重點。在 Blogger 裡面如果只是要看每篇文章的留言內容,以現在的瀏覽流程來看,除了點入該篇文章連結去瀏覽單篇文章外,其他方式都會被指向到 Blogger 的留言介面去,並沒有一個單純只是瀏覽留言、卻又不用指向單篇文章的方法(講那麼複雜,就是可以在首頁、Archived 和搜尋結果的頁面、不用跳轉連結就能看特定文章的留言回應)。

後來不經意地看到了一個範例,操作的感覺和效率正是我要的 Hack,就動手準備拿來用,深入看了程式碼後發現了幾個問題:第一,範例裡面用的是 included 放在別人站上的  Javascript 檔案,裡面更扯的還要綁作者的一個 PHP 去做資料處理服務!第二,這個範例與樣版範本有很大的關連性,也就是說修改的地方照範例改了,出來的效果會因為你樣版和對方不同而有很大的差異!為了符合我自己的版面,所以就花了一些功夫去改原作者的程式碼,也因此改完的結果不是剪貼程式碼進去就能用。所以這次不貼出我自己 Hack 的程式碼,此部分請參考原作者原文,搭配我 Blog 的原始碼服用~(這次的修改牽扯的範圍很大,除了記得要備份之外,如果不是很瞭解範本的人,也不要輕易嘗試~)

根據原作者的範例,我有做一些修改:首先針對上面提到的第一點,我修改作者的 Javascript 檔案,除了修改函式和變數名稱(至少我要看得懂)、精簡程式碼外,也拿掉會連回原作者服務的部分功能函式(有犧牲到一點功能,但至少不會再要求連回作者網站)。接下來,因為這是關於「留言回應」的操作,當沒有留言的時候當然功能就要關閉(可以節省原始碼數量),有改到範本的部分也加入了註解。然後,能夠展開當然也要可以收合回去,我也補入了收合的連結,並且要兼顧整篇文章瀏覽和展開留言兩邊版面的一致和美觀。最後花最多時間的,還是在調整版面,以及修改 CSS。以上步驟講得很簡單,真的改起來會要人命~(花了我幾天 Debug)

細節不講了,美中不足的有兩點:第一,損失留言作者的連結(這就是上面提到的犧牲到的一點功能),第二,之前做的作者回應標示也沒辦法實現。不過能有目前的效果和效率(速度比原作者更快),美中不足的地方也瑕不掩瑜吧!最後再補充我參考的文章以及原作者連結,希望有興趣的人都可以改得順利~

參考文章:
[Blog] 讓評論具有展開摺疊的效果!
Singpolyma - Technical Blog: Asynchronous Peek-a-boo Comments

回應: 10

 

2007-02-10

流量統計工具 (RRDTool)

既然 Asus WL-500g 主要是一台網路設備,網路流量的統計數據就是一項很重要的資訊。在一般的伺服器上通常會跑像 MRTG (Multi Router Traffic Grapher) 的工具,而我們用的是 RRDtool (Round-Robin Database tool)。由於這個統計工具是透過 web + php 來展示的,而且要搭配 crontab 來定期收集資料,所以裝 RRDtool 之前記得要先安裝 PHP-enable 的 web server,還有 crontab 服務。確認以上套件都正確無誤後,安裝 RRDtool 的套件:

ipkg install rrdtool

這個套件 (1.2.14) 安裝完畢,還會連帶安裝像是 libart (2.3.17) 、freetype (2.1.10) 和 libpng (1.2.12),因為這些流量統計都是透過呼叫這些 library 即時產生出來的。安裝完套件一樣不需要改什麼設定,只要有一個 shell script 定期執行收集流量資料,然後產生統計圖表就行了。這個 shell 不用自己寫,可以下載或參考這個檔案,下載後上傳或是剪貼內容到 WL-500g 裡面就行了。不過記得,要修改一下統計網頁的產生目錄,編輯 rrdtool.sh 這個檔案並且修改圖片網頁的產生路徑 (RRDIMG),在你原來的網頁首頁路徑下再建一個目錄 rrd 就可以。假設你原來 web 的目錄在 /opt/public/www,你就可以修改 rrdtool.sh 指定成:

RRDIMG=/opt/public/www/rrd/

目錄不用手動建立,第一次產生資料時會自己產生(同樣的,對產生的網頁 layout 不滿意也可以自己手動調整 rrdtool.sh)。我們把 rrdtool.sh 這個檔案複製到 /opt/sbin 下面,另外再寫一個 shell 檔案用來呼叫它,檔名是 /opt/sbin/rrdrun.sh 檔案內容只有兩行(編輯完記得更改屬性成可執行檔):

#!/bin/sh
/opt/sbin/rrdtool.sh >> /opt/var/log/rrdtool.log

然後,我們再編輯我們的 crontab:

nano /opt/etc/crontab

每五分鐘收集一次資料,加入以下的紀錄:

*/5 * * * * admin /opt/sbin/rrdrun.sh

這樣每五分鐘就會透過 crontab 執行一次 rrdrun.sh,並且把記錄匯總到 log 檔裡,為節省系統資源,每次整點或半點才會重畫一次圖形。先別急,等個五到十分鐘,如果 crontab 沒問題有乖乖去執行,那麼你可以用瀏覽器查看自己的統計流量記錄,網址是 http://你的IP或網址/rrd,一切正確的話連過去你就會有圖形化的流量統計工具囉!

參考文章:
ASUS WL-Series RRDTool Tutorial

備註:
我發現,在 rrdtool.sh 裡面,被執行時是每十分鐘更新一次即時流量圖(Daily),每一個小時更新一次每週流量圖(Weekly),而每月和每年的的流量圖則是一天做一次(Monthly, Yearly),更新的時間和頻率可以自行調整,因為每次更新會重新產生 JPEG 流量圖,對系統 CPU 的 Loading 很大,而且要花一些時間,有時候上一筆 crontab 的工作還沒做完,下一筆就又來了,這也是之前我懷疑機器會當掉的一個因素,如果你也是用和我一樣 week 的 WL-500g (500gx 或更高檔版本可能會好一些),要裝這個服務可要自己微調一下~

回應: 3

 

2007-02-09

Crontab 的安裝和使用

Crontab 是負責系統去處理例行性工作的服務,如果你希望要求系統週期性或是在未來特定時間會去執行什麼工作,有點像 scheduler,這個服務就很重要了。很多作業系統會內建這個重要的服務,在 WL-500g 上面我們要自己安裝。安裝的步驟很簡單:

ipkg install cron

此時我安裝的版本是 4.1,裝好會產生一個像系統用「例行性工作表」的設定檔:/opt/etc/crontab,我們可以修改一下內容,把 Path 加一些目錄:

SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/opt/bin:/opt/sbin:/opt/usr/bin:/opt/usr/sbin
MAILTO=""
HOME=/
# ---------- ---------- Default is Empty ---------- ---------- #

MAILTO="" 引號裡面可以指定帳號或是或是完整的 e-mail,當 crontab 發生問題時會通知。分隔線下是定期執行某些工作的語法,欄位大致為:

代表意義分鐘小時日期月份執行身份執行指令
數字範圍0-590-231-311-120-7admin欲執行的執行檔及其參數

以下舉幾個範例:

/5 * * * * admin cp file1 file2 (每五分鐘用 admin 身份去執行命令, copy file1 到 file2 去)
2 4 * * * admin command (每天早上 4:02 去執行 command)
0 8,18 * * * admin command (每天早上八點和晚上六點各執行一次命令)
30 8-12 * * * admin command (每天的 8:30, 9:30 到 12:30 都執行一次命令)
59 23 24 9 * admin command (每年 9/24 23:59 的時候執行一次)

想實際測試看看嗎?我們可以做一個實驗,先做一個執行檔,它會把現在時間添加寫入一個 log 檔,我們把執行這個執行檔放入例行性工作,要它五分鐘做一次,我們只要等一段時間再去看那個 log 檔,就知道他是不是真的每五分鐘做一次囉!首先我們在 /opt/tmp 下面做一個執行檔 test.sh:

nano /opt/tmp/test.sh

檔案加入以下內容:(把現在時間寫入 log 檔 /opt/tmp/test.log)

#!/bin/sh
date >> /opt/tmp/test.log

檔案存好,記得更改這個檔案的屬性才能夠執行!

chmd 755 /opt/tmp/test.sh

然後用 nano 修改 crontab 增加一筆記錄:

nano /opt/etc/crontab

加入以下這一行:(內容是,每五分鐘,把當時的時間添加寫入 log 檔)

*/5 * * * * admin /opt/tmp/test.sh

等個五分鐘十分鐘或更長一點的時間,查看 log 內容 (cat /opt/tmp/test.log),看看裡面的時間記錄是不是真的是每五分鐘做一次。(實驗做完,記得把 crontab 的那一筆紀錄和那個測試執行檔及 log 刪除,不然會浪費你的空間和資源喔!)

參考文章:
鳥哥的 Linux 私房菜:例行性命令的建立
Crobtab Tutorial

2007-02-08

輕巧的 Mail Agent (esmtp)

Asus WL-500g 的運算能力不是很強,架設了那麼多服務或程式,如果出了問題,並沒有任何回報機制。一般我們想到的是透過 mail,如果出什麼狀況 e-mail 出來通知就好,但不可能在WL-500g 上裝一套 mail server 吧?!我們要的只是有個 Agent 程式幫我們寄信,達到通知的目的就好。一般 ISP 都有提供他們的 SMTP server 給他們的用戶做 mail relay (信件轉寄),所以我們只要把信件的目的地和內容轉交給它,就可以達到代發寄信的目的(很多垃圾廣告信也是用這個原理寄出來的)。我們需要安裝一隻程式:esmtp,它能模擬成 Liunx 的 sendmail,幫我們把信交給 SMTP server,請它代發。安裝如下:

ipkg install esmtp

這個動作就可以把這個 agent 程式裝好了,我裝的版本是 0.5.1,跟著會安裝 library libesmtp (1.0.4)。但是只有這樣好像是不夠的,因為我執行時會發生 sendmail: can't load library 'libssl.so.0.9.7' 這樣的錯誤,看來連 openssl 的套件有相關性也要一併裝上,因為要用到其中的一些 library(這好像蠻重要的,這邊不裝其他地方也要裝,這也是為什麼很多文件會漏掉的原因,所以還是先補上去吧!我裝的版本是 0.9.7d):

ipkg install openssl

最後只要設定幫你做 mail relay 的 SMTP server 在哪裡,信件就可以寄了!原來預設是沒有設定檔的,我們手動建立:

nano /opt/etc/esmtprc

然後填入關於你的 SMTP 設定(hostname 後面最好把 port 25 加上,如果沒有帳號密碼,則第二和第三行可以不要。假設和我一樣用 Hinet ,設定為 'msa.hinet.net:25'):

hostname = your.smtp.server:25
username = "user"
password = "password"

存檔後就搞定啦!如果你不想用自己的 ISP 提供的服務,想用別人免費(如GMail)的 SMTP,有些要求用 SSL 認證,你還要搞定憑證的相關設定,可以到這邊參考怎麼弄。如果要先測試 mail relay 用的 SMTP 設定和伺服器能不能用,可以 telnet port 25 過去,用下面的幾個命令測試:

HELO <你要寄去的對方郵件伺服器>
MAIL FROM: <寄件人,有些 SMTP 會檢查寄件人以杜絕廣告信,亂打會有問題>
RCPT TO: <收件人,要打完整的 user@domain>
DATA
<接下來就是郵件內容,完整的 mail header 要自己做>
Subject: This is a test mail
To: user@domain.com
This is the text of my test mail.
. <"." 是用來作為內容結束提示的,按下 enter 以後就寄出了>
QUIT

esmtp 設定檔搞定好,接下來要測試一下,寄一封出來看看(esmtp 的執行檔也叫做 sendmail):

echo "Test Message" | sendmail receiver@domain.com -f sender@domain.com

其中 -f 後面帶的是寄信人的電子郵件,只要信箱 receiver@domain.com 有收到,這樣就大功告成啦(如果執行後等了很久 hang 住,代表你設定檔內容可能有誤)。不過這樣寄出來的信很醜,標題沒有內容,所以建議可以先產生一個信件檔,再把檔案內容透過 esmtp 寄出會比較漂亮。而內容至少要有三個欄位,然後再帶入郵件內容:

To: receiver@domain.com
From: sender@domain.com
Subject: <郵件主旨>

信件內容

假設這個郵件檔檔名叫 mail.txt,那麼把這封信寄出去的語法為:(-f 後面可以省略,還是會寄到)

cat mail.txt | sendmail receiver@domain.com -f sender@domain.com

寄信需要花一點時間。如果最後跳出一些其他的訊息,裡面還有 (null) 之類的字樣,代表是你的 SMTP server 有回錯誤出來,可能是伺服器、帳號密碼錯誤,也有可能是寄件人或收件人錯誤,要確定所有的資料都是對的才行。上面的辦法還要做一個檔案,下面我寫了一隻程式程式,是一個 shell 執行檔,可以在一行內把所需的資料都填入,方便做系統自動回報使用。你可以在 /opt/sbin 產生一個 shell 檔(我把執行檔檔名就叫做 "mail"):

nano /opt/sbin/mail

填入以下內容:

#!/bin/sh

#path to sendmail
SENDMAIL='/opt/sbin/sendmail'

while [ -n "$(echo $1 | grep '-')" ];
do
 case $1 in
  -s) SUBJECT=$2
      shift;;
  -r) RECEIVER=$2
      shift;;
  -m) MESSAGE=$2
      shift;;
  -f) FROM=$2
      shift;;
 esac
 shift
done

# You must at least have mail subject and receiver
if [ -z "$SUBJECT" ] || [ -z "$RECEIVER" ]
then
  echo "Usage: `basename $0` -s <subject> -r <receiver> [-f <sender>] [-m <text>]"
  exit
fi

if [ ! -z "$FROM" ]
then
  SENDMAIL="$SENDMAIL $RECEIVER -f $FROM"
else
  SENDMAIL="$SENDMAIL $RECEIVER"
fi

(
echo "To: <$RECEIVER>"
if [ ! -z "$FROM" ]
then
  echo "From: <$FROM>"
fi
echo "Subject: $SUBJECT"
echo ""
echo "$MESSAGE"
) | (eval $SENDMAIL)

最後存檔,記得變更檔案屬性成可執行:

chmod 755 /opt/sbin/mail

這個執行檔可以用參數的形式用一行命令列寄信出去,用法如下:

Usage: mail -s <subject> -r <receiver> [-f <sender>] [-m <text>]

-s 後面帶的是郵件標題,-r 帶的是收件者,-f 可以指定你寄出去的寄件者,-m 則是郵件的內容了。使用的範例:

mail -s "這是郵件主旨" -r receiver@domain.com -f sender@domain.com -m "信件的內容"

這個 mail 工具很好用,當系統出錯時可以指定執行寄發通知給你,也可以設在 crontab 裡面向你定期回報系統狀態,不需要架設郵件伺服器就可以做到了喔!(其實如果機器整個當掉,mail 也別想寄出去了~)

參考文章:
esmtp Turorial

安全的 FTP Server: VSFTP

在 WL-500g 系統裡預設就有支援提供 FTP 檔案伺服器的服務,用的是笨蛋 FTPD (stupid-ftpd),記得一開始我們在設定韌體的時候就把它停掉了,而且也不讓它能夠跑起來。如果想要開設 FTP Server 讓人家能夠上傳或下載,還是另外換一套比較好用、比較安全的 FTPD 來使用,這裡使用的是 VSFTP (Very Secure FTP server,非常安全喔...Orz)。不過這個 VSFTPD 和一般獨立的服務不大一樣,它是一個 (x)inetd 的 daemon,需要透過 xinetd 來呼叫啟動,因此,我們要先裝這個服務:

ipkg install xinetd

裝好 (2.3.14) 之後,雖然有提示訊息要你去修改,等等再說,緊接著就裝 vsftpd:

ipkg install vsftpd

這兩個套件安裝應該都會很順利,裝完 vsftpd (2.0.1) 之後,去設定 ftp server 的設定檔,指定預設的根目錄:

nano opt/etc/vsftpd.conf

在最後一行加入以下設定:

secure_chroot_dir=/opt/public/ftp

上面這個目錄如果你的系統裡沒有,記得要手動建立:

mkdir -p /opt/public/ftp

接下來我們去處理 xinetd 剛剛的警告訊息。因為裝完 xinetd 預設也有一個 telnet 的 daemon,如果不處理,重開會跑兩個 telnet daemon,那樣事情就麻煩了! xinetd 所屬服務的啟動方法設定也很簡單,和現在系統啟動服務的方式相同(就是把執行檔放到某個目錄,像是 /opt/etc/init.d/ 目錄下的執行檔一樣),所以如果要停掉或啟動 xinetd 的所屬服務,也是到 xinetd 的設定目錄去處理。那個目錄是在 /opt/etc/xinetd.d/。先停掉(刪除)所有預設的服務:

rm /opt/etc/xinetd.d/telnetd
rm /opt/etc/xinetd.d/ftp-sensor

然後產生我們要啟動的 vsftpd:

nano /opt/etc/xinetd.d/vsftp

貼入下列的內容:

# description: The vsftpd FTP server serves FTP connections.
# it uses normal, unencrypted usernames and passwords for auth
service ftp
{
disable = no
socket_type = stream
user = admin
server = /opt/sbin/vsftpd
server_args = /opt/etc/vsftpd.conf
wait = no
nice = 10
only_from = 0.0.0.0/0
}

檔案做好,記得存檔並更改檔案權限:

chmod +x /opt/etc/xinetd.d/vsftp

產生一個空的 log 檔(目錄不在的話手動建立):

touch /opt/var/log/vsftpd.log

搞定,接下來手動把服務啟動。裝完 xinetd 時在 /opt/etc/init.d/ 也被產生了一個 S10xinetd 的執行檔,所有 xinetd 相關的服務都靠這個執行檔來啟動,因此要啟動你的 vsftpd,只要執行這個檔案:

/opt/etc/init.d/S10xinetd

切記,如果預設的 ftp server: stupid-ftpd 還在跑的話,因為兩個服務目前也是用到同一個 port (21),一定先砍掉 stupid-ftpd 的服務 (killall stupid-ftpd),再去執行上面的執行檔。服務跑起來以後(用 ps 檢查有沒有 xinetd 這個服務),如果你有裝 ftp client 程式 (像是 ncftp),你可以直接在本機試連 ftp server 會不會動作,如果沒有問題,你又想開放這個檔案服務讓外面的人可以連進來,那就又要去防火牆上打洞了。第一步先編輯防火牆設定的檔案:

nano /usr/local/sbin/post-firewall

裡面應該有 SSH 或是 HTTP 等等的紀錄了吧!同樣的,在下面這兩行中間的範圍內:

iptables -D INPUT -j DROP
......
iptables -A INPUT -j DROP

貼入以下幾行防火牆開 port 的設定,FTP 使用的是 port 21:

# Port 21 used for VSFTP server
iptables -A INPUT -p tcp --dport 21 -j ACCEPT
iptables -t nat -A PREROUTING -i $1 -p tcp --dport 21 -j DNAT --to-destination$4:21

存檔,一樣 記得 flush 到 flash 裡再重新開機:

flashfs save
flashfs commit
flashfs enable
reboot

這個時候你再用你安裝的 FTP client 軟體(像是 cuteftp 或 dos 視窗的 ftp 程式),直接 ftp 到你剛設定完的 FTP server 去(不要開 PASV 的被動模式好像比較快),上傳或下載測試一下(注意帳號的權限),這時候你應該就有了一台 FTP Server 囉!

參考文章:
vsftpd Installation and Setup

回應: 14

 

2007-02-07

為 Blogger 加入整合日曆 Archive Calendar

Blogger 提供讓你塞入 Sidebar 的文章匯總功能有三種介面,有階層(樹狀)、Flat 清單和下拉式選單,也可以調整排序和顯示的格式,不過,這樣總還是不夠。很多 BSP 提供的 Blog 都會有類似日曆的模組,除了會有類似萬年曆的功能,讓你知道「今夕是何夕」外,也可以透過這個日曆知道哪一天你發表過文章,透過點擊日期去知道當天你寫過什麼,所以這樣功能的日曆也叫做 "Archive Calendar",提供另外一種文章依日期分類匯總的檢視方法。

只是塞一個日曆並不困難,到處都可以找到 Javascript 程式複製貼上,但是要整合 Blogger 的 Archive 功能就麻煩了,以下分幾個步驟來進行。首先,在你的 Sidebar 上面要至少有一個 "Blog 存檔" (Archive) 的網頁元素 (Element),接下來去修改它的屬性,存檔頻率改成「每日」,勾選「先顯示最舊的文章」,樣式設成「Flat 清單」。因為 "Blog 存檔" 這個 Element 「看起來」只允許你存在一個,如果你想要有日曆而且原有的 Archive 也想保留,那就要去修改 HTML(不要展開小裝置範本),把下面這行複製一份,然後改一下 id(不重複就行),你就可以有兩個 "Blog 存檔",把新增的那個 Element 改成日曆就行了。不過如果你不需要那麼多 Archive 模組,這個動作就不要做了。

<b:widget id='BlogArchive1' locked='false' title='ARCHIVED POSTS' type='BlogArchive'/>

第二個步驟,塞入定義給 Calendar 用的 CSS (放在 <head> 標籤內就行,就是定義樣版的區段裡,請依照個人樣版修改,這次修改算是大變動,記得先完整備份你的 Template,這次要展開整個範本進行修改):

/* Archive Calendar Styles */
#Calendar {
  margin: 0px;
}
#CalendarTable table {
  border-collapse: collapse;
  padding: 0px;
  border: 0px;
}
#CalendarTable table th {
  padding: 1px;
  color: #777; 
  margin: 0;
}
#CalendarTable table td {
  height: 25px;
  color: #999;
  text-align: center;
  padding: 1px;
  margin: 0;
}
#CalendarTable table td a {
  display: block;
}
#CalendarTable .Today {
  color: #fff;
  background: #777;
}
#CalendarTable .Today a {
  color: #fff;
}
#CalendarTable .Weekend {
  color: #997777;
}
#Calendar .act {
  color: #fff;
  padding: 4px;
}

第三個步驟,也是是重頭戲:要貼入一堆 Javascript 函示。(一樣是貼在 <head> 標籤裡,如果你之前也有 Hack 放過 Javascript,放在一起就好)

<script type='text/javascript'>
//<![CDATA[
<!-- Script functions for generating Archive Calendar: ArchiveCalendar(), archiveurl(), YearMonth(), Calendar(), PrevMonth(), NextMonth(),  function GoToday(), refreshTable() -->
function ArchiveCalendar() {
  this.PrevMonth = PrevMonth;
  this.NextMonth = NextMonth;
  this.GoToday = GoToday;
  this.refreshTable = refreshTable;
  this.YearMonth = YearMonth;
  this.Calendar = Calendar;
  this.archiveurl = archiveurl;
  this.week_label = new Array("一","二","三","四","五","六","日");
  this.month_label = new Array("01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12");
  this.month_days = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
  this.today = new Date();
  this.cur_day = new Date();
  this.blogarchivename = new Array ();
  this.thisarchive = 0;
  this.base = '';
}

function archiveurl(idx) {
  url = this.base;
  url += this.blogarchivename[idx].slice(0, 4) + '_' + this.blogarchivename[idx].slice(4, 6) + '_' + this.blogarchivename[idx].slice(6) + '_archive.html';
  return url;
}

function YearMonth(date_obj) {
  year = date_obj.getFullYear();
  thisMonth = this.month_label[date_obj.getMonth()];
  out = year+'-'+thisMonth;
  return out;
}

function Calendar(date_obj)
{
  year = date_obj.getFullYear();
  thisDay = this.today.getDate();
  thisMonth = this.month_label[date_obj.getMonth()];
  nDays = this.month_days[date_obj.getMonth()];
  if (date_obj.getMonth() == 1 &&(((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)))
    nDays = 29;
  IsitNow = date_obj;
  IsitNow.setDate(1);
  startDay = IsitNow.getDay() - 1;
  if (startDay < 0) startDay = 6;
  var out='';
  out+='<table>';
  out+='<tr>';
  for (var index=0;index<7;index++)
    out+='<th style="width:25px;">'+ this.week_label[index]+'</th>';
  out+='</tr>';
  thisarchive=0;
  while (thisarchive <= this.blogarchivename.length -1)
  {
    if(this.blogarchivename[thisarchive].slice(0,4) == year && this.blogarchivename[thisarchive].slice(4,6) == thisMonth)
      break;
    else
      thisarchive++;
  }
  var tab_col=0;
  for (index=0;index<startDay;index++) {
    if (tab_col==0)
      out+='<tr>';
    out+='<td>&nbsp;</td>';
    tab_col++;
  }
  var archiveday;
  for (index=1;index<=nDays;index++) {
    if (index<10) {
      index_day = new Date(year+'/'+thisMonth+'/0'+index+' 00:01');
      archiveday = '0' + index;
    }
    else {
      index_day = new Date(year+'/'+thisMonth+'/'+index+' 00:01');
      archiveday = index;
    }
    if (tab_col==0)
      out+='<tr>';
    if (index==thisDay && this.today.getMonth()==date_obj.getMonth() && this.today.getFullYear()==date_obj.getFullYear()) {
      out+='<td class="Today">';
      if (thisarchive < this.blogarchivename.length && this.blogarchivename[thisarchive].slice(4,6)==thisMonth && this.blogarchivename[thisarchive].slice(6,8)==archiveday) {
        out+='<a href="'+this.archiveurl(thisarchive)+'" target="_top">'+index+'</a>';
        thisarchive++;
      }
      else
        out+=index;
      out+='</td>';
    }
    else {
      if (tab_col < 5)
        out+='<td>';
      else
        out+='<td class="Weekend">';
      if (thisarchive < this.blogarchivename.length && this.blogarchivename[thisarchive].slice(4,6)==thisMonth && this.blogarchivename[thisarchive].slice(6,8)==archiveday) {
        out+='<a href="'+this.archiveurl(thisarchive)+'" target="_top">'+index+'</a>';
        thisarchive++;
      }
      else
        out+=index;
    out+='</td>';
    }
    if (tab_col==6) {
      out+='</tr>';
      tab_col=0;
    }
    else
      tab_col++;
  }
  if (tab_col>0) {
    for (var si=0;si<(7-tab_col);si++) {
      out+='<td>&nbsp;</td>';
    }
    out+='</tr>';
  }
  out+='</table>';
  return out;
}

function PrevMonth() {
  thisMonth = this.cur_day.getMonth()-1;
  year = this.cur_day.getFullYear();
  if (thisMonth<0) {
    thisMonth = 11;
    year = year-1;
  }
  thisMonth = this.month_label[thisMonth];
  this.cur_day = new Date(year+'/'+thisMonth+'/1 00:01');
  this.refreshTable();
}

function NextMonth() {
  thisMonth = this.cur_day.getMonth()+1;
  year = this.cur_day.getFullYear();
  if (thisMonth>11) {
    thisMonth = 0;
    year = year+1;
  }
  thisMonth = this.month_label[thisMonth];
  this.cur_day = new Date(year+'/'+thisMonth+'/1 00:01');
  this.refreshTable();
}

function GoToday() {
  this.cur_day = new Date();
  this.refreshTable();
}

function refreshTable() {
  document.getElementById('CalendarMonth').innerHTML = this.YearMonth(this.cur_day);
  document.getElementById('CalendarTable').innerHTML = this.Calendar(this.cur_day);
}
//]]>
</script>

最後一個步驟,要修改範本顯示的版面囉!找到下面這一段:(如果你有兩個 Archive,要確認你找到的是要改的那一個)

<b:includable id='flat' var='data'>
<ul>
<b:loop values='data:data' var='i'>
<li class='archivedate'>
<a expr:href='data:i.url'><data:i.name/></a> (<data:i.post-count/>)
</li>
</b:loop>
</ul>
</b:includable>

然後整個取代改成下面這一段:

<b:includable id='flat' var='data'>
<!-- Generate Archive Calendar -->
<center>
  <table border='0' cellpadding='0' cellspacing='0' id='Calendar'>
    <caption>
      <a href='javascript:;' onclick='Calendar.PrevMonth();' title='Previous Month'>&lt;&lt; </a>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
      <a href='javascript:;' onclick='Calendar.GoToday();' title='Back to Today'> <span id='CalendarMonth'/> </a>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
      <a href='javascript:;' onclick='Calendar.NextMonth();' title='Next Month'> &gt;&gt;</a>
    </caption>
    <tr>
      <td class='act' id='CalendarTable'> </td>
    </tr>
  </table>
  <script type='text/javascript'>
    Calendar = new ArchiveCalendar();
    Calendar.base = &#39;你的 Blog 網址&#39;;
    var offset = Calendar.base.length;
    <b:loop values='data:data' var='i'>
      archUrl = &#39;<data:i.url/>&#39;.slice(offset);
      Calendar.blogarchivename.push(archUrl.slice(0,4) + archUrl.slice(5,7) + archUrl.slice(8,10));
    </b:loop>
    Calendar.refreshTable();
  </script>
</center>
</b:includable>

其中,紅色的網址記得要改成自己的喔!範本存檔,然後預覽看看,如果日曆有出來,就可以存檔收工囉!其實原理和標籤雲 (Label Cloud) 類似,利用 Javascript 搭配 Template 提供的資料(Daily Archive),將 Element 的樣式動態改變,變成想要的樣子,就是這個多功能的日曆囉!再嚴謹一點,最後一段可以加入 <noscript> 的判斷,套用原來那一段,如果使用者的瀏覽器不支援 Javascript (或是關掉),那麼Archive 的功能也還是存在能夠使用~(這裡懶得改了)

參考文章:
為 Blogger Beta 加上日曆

備註:
實際掛上這個模組,又看了原始碼以後,發現如果 Blog 的文章很多,搞 Daily Archive 文章會很多很慢,尤其當 Blog 成立越久,為了依照日期填入日曆的「每一天」的存檔,會增加首頁載入的時間,發表天數越長越嚴重!我還蠻喜歡這個日曆的,看看要不要改個 Monthly Archive 的版本,效能應該會比較好才是~

修改:
根據網友意見,我把日曆的第一天從原來的禮拜天改成禮拜一,也比較符合自己的習慣,另外日曆週末的部分也稍稍改變顏色(class: weekend),用來標示,也可以自行修改樣式。
(2007/04/02) 修正 bug,如果有其他 style 的問題,請看看下面的意見和回覆的內容能不能解決。

回應: 94

 

2007-02-06

Google Analytics

網站或 Blog 剛架起來的時候,多少都會好奇每天或總共有多少人會來參觀,Blogger.com 並沒有提供網站統計的功能,所以我也會意思一下裝個計數器之類的東西。我用的計數器是 StatCounter 的,除了樣式可以配合外,它還提供了相當完整的 Summary 資訊(很多人不知道,其實,點我的計數器就可以看圖表,左邊還有一些流量分析和訪客統計資訊),但還是有缺點,三不五時就會被我發現連不上(不知道是不是我自己網路的問題)。

後來,有聽說過 Google Analytics (孤狗分析?!)這玩意,顧名思義,它是一個免費的網頁流量分析服務,只要你的網站或是 Blog 擁有自己獨立的網址或網域(因此 http://aa.cbb.com/name or ~name 這種網址不能用),並且可以塞入 JavaScript 程式碼到模組裡(因此 M$ Live Space 這種不能用),累積一段時間孤狗就可以替你累積訪客瀏覽拜訪你網頁的資訊,並且做出各種分析圖表,除了流量之外,還可以知道你的訪客從哪裡來,喜歡看什麼內容。像我一樣無聊的,可以知道自己 Blog 被瀏覽的狀況,喜歡玩 Data Mining 的人,甚至還可以從裡面找出有助於行銷、廣告甚至網站內容規劃的參考資訊,聽起來真是神!

使用方法很簡單,首先當然是要有 Google 帳號,然後連到 Google Analytics 頁面去開啟服務,然後註冊你想分析的網站,填完資訊後會產生類似下面的程式碼:(下面是參考用,想用的自己申請~)

<script src="http://www.google-analytics.com/urchin.js" type="text/javascript"></script>
<script type="text/javascript">
_uacct = "UA-144796-3";
urchinTracker();
</script>

Google 的說明是講把以上程式碼塞在 <head><meta> 之後,</head> 之前,我還是照我自己的理解,為了不破壞 blogger.com 模版原始碼的結構性和美觀,把它拆開放:第一行是 Include 一段 Google 的 Javascript 程式碼,主要是定義了一些函示和變數在裡面,我把它塞在我所有自行定義的 Javascript 區段之前。而後面四行,則是宣告了一個類似 ID 的變數,以及執行那個 included 檔裡面的函示 urchinTracker(),我把它塞到 <body> 的後面,在本文資料出現前把該收集的資料送給 Google 作分析。

以下是本網站的匯總分析結果

後來我有稍稍比較了一下,Google Analytics 和我計數器服務提供的統計數據相當接近,但 Google 提供了更多訪客的統計圖表,重點是速度也比較快,而且可以同時監控多個網站。我不知道多加這些分析會不會增加我在 Google 的曝光率,不過看到更多關於自己網站的訪客資訊也挺不錯的~

回應: 6