2013/09/04
修正 record feature 的一個 bug
先說明一下這個問題的原因:
由於 record feature 會使用 array_diff_assoc 函數來比較 start / stop 之間的差異,但是該函數在比較時無法處理非 scalar 的變數,所以會造成 error 使得在 stop record 時程式中斷。
另外在 view diff 表格時,render cell 時也是無法處理非 scalar 的變數。
我提出的 Patch 主要是在修正這兩個 Bug,Patch 檔在上面的連結裡面可以下載~
2013/08/31
SOAP 在 Windows 與 Linux 上的不同行為
當我把程式寫完並在自己的機器上測試通過後,佈署到客戶租用的虛擬主機上時,卻發現程式不能正常執行。
後來追蹤後發現 PHP 的 SOAP 模組,在不同平台上的行為會不一樣~
其差異點如下:
- 在 Windows 下,執行 SoapClient::__soapCall 時,$arguments 要多包一層 array
- 也就是說,如果在 Linux 下,$arguments 是 $data 的話;在 Windows 下要改為 array(&$data)
- 在 Windows 下,執行 SoapClient::__soapCall 的返回值是 array 結構;而在 Linux 下則是 object 結構
- 這部分的差異,可以參考這裡,用 objectToArray 函數來統一轉成 array 結構
2013/08/23
在 Migrate node 時匯入 body 中的圖檔
後來問了 Google 大神,請教先進,追了程式碼之後,還是不曉得該如何直接利用 MigrateDestinationMedia 來直接匯入多個檔案。
所以今天我研究了一下,寫了一個 FileMigration 類別來幹這件事。
有一些事要注意:
- 需要 simplehtmldom API 來做 html 碼的解析,以取出 img 標籤做進一步的處理
- 匯入的檔案的目錄結構,第一層必須是以 Drupal 的 language code 命名
- 可以在 prepareRow 或是 prepare 中呼叫 parseContentImage 來處理需要處理的欄位
- 需要在 prepare 中自行呼叫 MigrateDestinationMedia::rewriteImgTags 來改寫img 標籤
類別原始碼:
class FileMigration extends Migration {
const FAILURE = 0;
const SUCCESS = 1;
/**
* @var array
*/
protected $import_file_number = array(self::FAILURE => 0, self::SUCCESS => 0);
/**
* @param string $content
* @param string $language
* @return string
*/
protected function parseContentImage($content, $language) {
if (empty($content)) {
return '';
}
$html = str_get_html($content);
foreach ($html->find('img') as $image) {
$source = $image->attr['src'];
if (valid_url($source, TRUE)) {
continue;
}
$format_source = $this->formatImageSource($source, $language);
$file = $this->defaultFile();
$file->value = $format_source;
$file->destination_file = $format_source;
$file->field_file_image_alt_text = (empty($image->attr['alt'])) ? '' : $image->attr['alt'];
$file->field_file_image_title_text = (empty($image->attr['title'])) ? '' : $image->attr['title'];
try {
$this->processFile('image', $file);
$image->attr['src'] = $this->formatUrl($file->destination_dir . '/' . $format_source);
} catch (Exception $exception) {
$this->import_file_number[self::FAILUE]++;
$this->saveMessage($exception->getMessage());
}
}
return $html->save();
}
protected function formatUrl($uri) {
$scheme = file_uri_scheme($uri);
if (FALSE === $scheme) {
return ('/' == drupal_substr($uri, 0, 1)) ? $uri : '/' . drupal_encode_path($uri);
}
if ($scheme == 'http' || $scheme == 'https') {
return $uri;
}
$wrapper = file_stream_wrapper_get_instance_by_uri($uri);
if (FALSE == $wrapper) {
return '';
}
if (FALSE == is_subclass_of($wrapper, 'DrupalLocalStreamWrapper')) {
return $wrapper->getExternalUrl();
}
return '/' . $wrapper->getDirectoryPath() . '/' . file_uri_target($uri);
}
/**
* @param string $source
* @param string $language
* @return string
*/
protected function formatImageSource($source, $language) {
$part = explode('/', $source);
$und = FALSE;
while (true) {
$first = trim(reset($part));
if (empty($first) || ('..' == $first)) {
array_shift($part);
$und = TRUE;
} else {
if ($und) {
array_unshift($part, LANGUAGE_NONE);
} else {
array_unshift($part, $language);
}
break;
}
}
return implode('/', $part);
}
/**
* @param string $type
* @param stdClass $file
* @return array
*/
protected function processFile($type, stdClass $file) {
if (empty($file->type)) {
$file->type = $type;
}
if (FALSE == isset($file->uid)) {
$file->uid = 1;
}
if (isset($file->timestamp)) {
$timestamp = MigrationBase::timestamp($file->timestamp);
}
$file->preserve_files = FALSE;
$source = new MigrateFileUri((array) $file, $file);
$file = $source->processFile($file->value, $file->uid);
if (is_object($file) && isset($file->fid)) {
if (isset($timestamp)) {
db_update('file_managed')
->fields(array('timestamp' => $timestamp))
->condition('fid', $file->fid)
->execute();
$file->timestamp = $timestamp;
}
$this->import_file_number[self::SUCCESS]++;
$return = array($file->fid);
} else {
$this->import_file_number[self::FAILURE]++;
$return = FALSE;
}
return $return;
}
/**
* @return stdClass
*/
protected function defaultFile() {
$file = new stdClass();
$file->destination_dir = 'public://';
$file->urlencode = FALSE;
return $file;
}
protected function postImport() {
parent::postImport();
$total = array_sum($this->import_file_number);
if (0 >= $total) {
return;
}
$message = t("Imported !numitems files (!succeed succeed, !failed failed) - done with '!name'", array(
'!numitems' => $total,
'!succeed' => $this->import_file_number[self::SUCCESS],
'!failed' => $this->import_file_number[self::FAILURE],
'!name' => $this->machineName,
));
self::displayMessage($message, 'completed');
}
}
2013/08/14
使用 Demonstration site 取代 Backup and Migrate
在 Drupal 模組中,有兩個模組可以做這件事,一個是 Backup and Migrate 模組,另外一個是 Demonstration site 模組。
然而 Backup and Migrate 有個致命的缺點:那就是在還原資料庫時,他不會先 Drop 掉所有的資料表再還原。
這個缺點會有什麼影響呢?試想下列情況:
- 用 Backup and Migrate 備份 =>Test Backup
- enable 某個會建立資料表的模組 (例如: Commerce Cart) => Test Module
- 用 Backup and Migrate 還原 Test Backup
- enable Test Module
通常我們在使用還原功能時,並不會有習慣去 disable & uninstall 模組,所以這個缺點還蠻要命的~
另外 Backup and Migrate 還有個問題就是只支援 MySQL / MariaDB,其他的資料庫並不支援;不過這個問題我個人是覺得還好,畢竟比較常用的也是 MySQL。
最後要說一下 Demonstration site 模組的問題:
- 目前 7.x-1.0 這個版本有問題,不能正常還原,要用 7.x-1.x-dev 的版本才行
- Demonstration site 的更新沒有 Backup and Migrate 勤快
- 設定也比較簡單,沒有備份檔案或是其他的進階功能
- 在 bug report 那邊也是有 bug 擺了半年以上沒人處裡
2013/08/12
配合 Drupal Media 的 Player
我測試的結果,播放器有以下的問題 (至少有一點):
- 沒有支援 Flash fallback (純 HTML 5 的播放器都沒有)
- 沒辦法支援 Embedded Media Field
- 明明把長寬尺寸設定好,大小還是不對
下載:
2013/08/07
Drupal Media 的檔案管理
以前我使用 File Field 來做檔案管理,可是這種方式,在碰到使用編輯器插入圖片或其他檔案時,沒辦法使用 File Field Paths 來變更目錄與檔名;必須禁用編輯器的插入功能,先把檔案上傳到另外的 File Field 欄位中,再配合 Insert 模組來插入,實在很不方便。
跟 File Field 最大不同是,Media 的檔案管理方式,是以檔案的種類為基礎作集中式的管理。
Media 預設的檔案種類有四類:Image、Video、Audio、Document
在使用上配合 File Entity Paths,可以對 Body 欄位插入的檔案做管理。
目前我使用 3.6.X 的 CKEditor 編輯器,不用 4.X 的版本是因為 MediaBrowser 的 Plugin 會有問題。
使用 MediaBrowser 來插入圖片時,可以重複利用之前上傳的檔案,插入時也可以選擇不同的 Display 方式,彈性很大。
不過預設的 Default 顯示方式並沒有設定好,可以在
admin/structure/file-types/manage/image/file-display
找到設定介面,勾選 Enabled displays 裡面的 Image,儲存後再插入圖片時就會顯示出來。
2013/07/21
一顆硬碟,一個分割區
其實在每個階段,我給出的答案都不一樣。
時至今日,如果你問我,我自己又是如何做的呢?
目前我的電腦上的硬碟是這樣配置的:
- 一顆 SSD (64G):用來放系統
- 一顆 WD 黑標 (500G):用來放一般 HTTP、FTP 下載回來的東西 (通常檔案較小)、免安裝軟體、其他我自己或程式產生的資料、音樂、圖檔等等大雜燴
- 一顆 WD 紅標 (2T):用來放 BT、eMule、迅雷等等下載回來的檔案倉庫
我個人算是有輕微的收集癖好,所以要用到 NAS 來放,如果你時間很多且勤於整理的話,不用 NAS 也是可以~
在這裡還有一個重點,那就是這三顆硬碟都是 MBR / One Primary Partition,即標題所說的:一顆硬碟,一個分割區。
現今硬碟已經很便宜,主機板上的晶片組支援的 SATA3 也很多,不需要折磨自己只用一顆硬碟切來切去。
分割硬碟不如做好目錄管理比較重要。
2013/06/24
我痛恨駝峰式命名原則
甚麼叫駝峰式命名原則?
舉個例子來說,假設你有一個用來新增產品用的函數,你可能命名成
AddProduct
如果他是某個物件的方法,那你會這樣命名
addProduct
這個就是駝峰式命名法,目前主流的程式語言大都是推薦這樣的命名方式。
如上面的例子,駝峰式命名原則還分成兩種,如果首個單詞第一個字母也大寫的話,也被稱作帕斯卡命名原則。
相對於駝峰式命名原則,我個人比較喜歡底線命名原則,以上述例子來說,如果用底線命名原則來命名就會變成
add_product
我不否認駝峰式命名原則看起來比較美觀,而且名稱長度較少,但它的優點也僅只於此。
尤其是在一些 Script 語言中,你可以利用一個內容是函數名稱的字串去調用該函數來達到效果時,碰到駝峰式命名原則,你會發現沒有完美的解法~
我寫過剖析駝峰式命名原則字串的函數,從結論上講,不論是上面兩種駝峰式命名原則,還是名稱裡面包含連續大寫縮寫的名稱,我寫的剖析函數都能正確切開個個單詞 (前提是你沒亂命名)。
但能切開不代表能組合回去,原因是因為你沒辦法猜測組合回去的名稱被用在什麼情況下。
相對於駝峰式命名,底線式命名的原則還蠻死板的,他要求全部小寫,分隔單詞全都用底線。
這種命名方式很古老,但任何場合都能用,從各種程式語言到資料庫、表、欄位等等,全都一體適用。
分解組合這種命名方式也很簡單,甚至不用你自行來寫函數來剖析,大多數的程式語言都內建了處理分解組合這種字串的函數。
2012/12/04
Picasa 網路相簿的限制
- 相片大小上限:圖片一律不能超過 20 MB 及 5000 萬像素。
- 影片大小上限:上載的單一影片大小一律不能超過 1GB。
- 網路相簿數量上限:10,000 本
- 每本網路相簿的相片和影片數量上限:1,000
- 總儲存空間:「Picasa 網路相簿」可儲存 1 GB 的相片和影片。大小未達特定限制的檔案不會計入這個配額限制。
- 免費儲存空間限制:只要相片大小低於 2048x 2048 像素,影片長度少於 15 分鐘,就不會計入免費儲存空間配額。
資料來源:
2012/10/29
關於 Windows 8 升級二三事
上禮拜五 Windows 8 正式發表,同時也帶來了一股升級潮,與此同時,我也用我之前購買的筆電的隨機授權弄了一組折扣眷,花了 NT$439 做了升級。
先來說說花了這筆錢買到的是什麼?
嚴格說來,買到的是一組從 "正式管道" 來的 "Windows 8 專業版" 的 "升級用" 的啟用序號。
這裡有幾個重點,就是我上面引號內的文字,說明如下:
- 正式管道:這是微軟承認可用的啟用序號,不用擔心這組序號通不過啟用程序
- Windows 8 專業版:升級助手下載回來的是另外包裝的 ESD 版,但本質上還是 Windows 8 專業版,所以拿 MSDN 上的 Windows 8 專業版 ISO 來安裝,依然可以用這組啟用序號
- 升級用:這組序號是升級用的,所以不管你安裝的是升級助手下載回來的版本,還是從 MSDN 上的版本,安裝時硬碟必須要有裝過 Windows 作業系統,這組序號才可以啟用
目前網路上大家討論最多的還是這個序號能不能空機安裝,底下我來簡單說明一下。
我自己實驗過的步驟是先拿一顆硬碟,用第三方的磁碟分割程式分隔好並格式化,然後拿這個硬碟去安裝 Windows 8,結果就是第一次無法用該序號啟用,然後拿這顆硬碟再裝一次,就可以正常通過啟用程序。
所以結論是空機安裝是可以的,但可能必須安裝兩次。
底下是我碰過的一些問題與解法:
- 更改序號進行啟用程序的方法:用 Administrator 身分打開 Command Prompt,執行 slui 3
- 無法新增輸入法:把 Windows Search 功能裝上即可
- 有使用 RAM Disk 且改過 TEMP 目錄與 Pagefile 路徑到 RAM Disk 上,安裝某些 Windows Feature 會失敗:先把 RAM Disk 刪掉,安裝完需要的 Windows Feature 後再啟用
2012/09/28
AKB0048 的預言會成真嗎?
話說昨天 (現在過了半夜12點) 晚上回到家後,剛好看到某政論節目名嘴在討論中日之間因為釣魚台可能會發生戰爭。
我對名嘴的推論十分不以為然,雖然如此,我心中仍在想萬一真的發生戰爭,最糟的結果可能是引發第三次世界大戰~
那今年的這部動畫 - AKB0048 搞不好就會預言成真:直到秋夜原被導彈襲擊之前,AKB 的成員們仍堅持在劇場公演...
接下來請看動畫自行腦補~
2011/10/14
最近動向
話說今年(2011)上半年,我的眼睛突然發生問題,健康亮起紅燈後,我就把手上的事情一推256,整個給他放空。
後來在暑假的時候,到一個朋友的公司幫忙,原本是做顧問,不過上天大概看我閒不下來,就讓我的眼睛好了。
在那家公司待了一陣子,感覺可以告一段落,想說做網站的案子也做到煩了,剛好有另外一個朋友在找人寫網路設備的監控與分析的東西,這方面我剛好很有興趣做,就跳過來現在的公司。
今天下午朋友的公司在徵人,請我幫忙宣傳一下,我才想到我的部落格很久沒開張了~
徵人的條件在這裡:http://bbs.ecstart.com/forum.php?mod=viewthread&tid=41275#lastpost
看來應該是要做 SNS、eCommerce、Mobile 的應用,這三塊一直都是市場關注的方向,但我覺得重點是,去那邊上班可以挖 FIEND 的寶~
2011/01/04
USB 3.0 硬碟外接盒 + 3TB 硬碟入手~
昨天去光華買了以下的幾樣東西:
主要是我的 2TB 硬碟裝滿了,所以就想去買更大容量的硬碟~
順便想試試 USB 3.0 的硬碟外接盒,看看傳輸效果是不是真的跟 eSATA 一樣,所以就去敗家了。
先說硬碟,因為容量的關係,而且我想用一整個分割區當成一個磁碟(只用一個磁碟機代號),所以要選 GPT 的分割區形式。
再來是 NEC USB 3.0 晶片的驅動程式跟韌體更新程式,在這個站可以找到比較新的驅動程式跟韌體更新程式來用:
http://www.station-drivers.com/page/nec.htm
最後是 UASP 驅動程式,根據網路上的資料,這個驅動程式似乎有授權才能用,所以我這裡就不提供了~
不過根據我目前實測的結果,寫入速度的提昇是有,但只有一點點...
2010/12/29
強制 TinyMCE 貼上強制去除樣式(純文字)
目前我在 Drupal 上愛用的編輯器是 TinyMCE,最近碰到了一些由使用者貼上的文章會造成版面被破壞的問題。
我是使用 Wysiwyg 配合 TinyMCE 來載入,所以可以只改 Wysiwyg 模組裡面的檔案,來達到控制 TinyMCE 貼上的動作。
我用的 Wysiwyg 版本是 6.x-2.2,TinyMCE 的版本是 3.3.9.3
要改的檔案是:wysiwyg/editors/js/tinymce-3.js
在 init: function(ed, url) {...} 這個函數裡面新增以下程式碼:
// Force Paste-as-Plain-Text
ed.onPaste.add( function(ed, e, o) {
ed.execCommand('mcePasteText', false);
});
存檔,然後清空 Drupal 的 Cache 即可。
2010/11/17
MariaDB 試用
這幾天在找 solr 跟 sphinx 的相關資訊,在網路上找了許多資訊後,有個亮點突然吸引我:那就是 MariaDB 居然有把 Sphinx 的儲存引擎整合進去~
把 MariaDB 裝上後看看,卻沒發現有 Sphinx 的儲存引擎,上官網找了資料後才知道要先把 Plugin 安裝進去~
由於檔案都有了,所以只要下 SQL 命令即可,同樣也是要先作一次安裝 Plugin 的儲存引擎還有 OQGraph。
SQL 語法如下:
- INSTALL PLUGIN sphinx SONAME 'ha_sphinx.dll';
- INSTALL PLUGIN oqgraph SONAME 'ha_oqgraph.dll';
2010/08/11
exFAT 的磁簇大小
在一般硬碟上,如果你直接格式化它,是不會出現 exFAT 的選項可以選,你必須用 Disk Management 先把硬碟的分割區給刪除掉,然後用 New Simple Volume 把分割區切出來時,格式化的選項才有 exFAT 可選。
另外一個方式是用 CMD 去下 Format 命令。
不過問題在於,這時候的磁簇大小的範圍是 512 ~ 64K,沒有 32M 的選項可選 (我用來測試的硬碟是 500G),只有用 Format 命令的方式可以強迫指定磁簇大小為 32M。
後來我拿手邊兩個容量分別為 4G 和 16G 的隨身碟來試,兩者的磁簇大小都有 32M 的選項可選,不過最小的磁簇大小的選項有點差異,4G 的那個最低可以選到 512,16G 的那個則只到 1024。
底下是我剛剛把一些檔案複製到測試的 500G 的硬碟上的資料。
| Size | Size on Disk | Files | |
| 來源:NTFS/64K | 269G | 269G | 3582 |
| 目的:exFAT/32M | 269G | 349G |
值得注意的是,當複製小檔時,會有停頓的現象。
雖然我認為 exFAT 這樣的設計很雞肋,不過我想知道為什麼會有這樣的差異。有誰能提供進一步的資料嗎?
2010/08/09
如何在 Windows 下選擇合適的檔案系統
先講結論:
如果你的儲存媒體是電子式的 (例如:RAMDISK、SSD、Flash 隨身碟等),請用 exFAT。
如果你的儲存媒體是機械式磁性紀錄的 (例如:傳統硬碟),請用 NTFS。
不過有兩個例外:SSD 當系統碟使用、Flash 隨身碟當安裝碟使用時,則必須使用 NTFS。
如果你懶的管這兩個檔案系統的差異,那就都格式化成 NTFS,磁簇大小 64K 來使用就行了。
這幾天我在研究這兩種檔案系統的特點,試圖找出增加其搜尋、存取效能的關鍵以及適用的方式,底下是純理論的分析。
對於搜尋方面,在不靠 Windows Search Index 的前提下,由於NTFS 的目錄內容使用 B+ 樹的關係,所以在相同的儲存媒體中,效能會比 exFAT 來的好。
不過由於電子式比機械式在搜尋速度跟存取速度都有強大的壓倒性優勢,因此對於這兩個檔案系統在電子式的儲存媒體中的搜尋效能來說,並沒有很明顯的差異。
對於存取方面,磁簇大小則是關鍵,底下是這兩種檔案系統的磁簇大小的比較表,單位是 Byte。
| 檔案系統 | 最小 | 最大 | 預設 |
| NTFS | 512 | 64K | 4K |
| exFAT | 512 | 32M | 32K |
一般來說,磁簇越大,存取效能也越好,但相對的對於小檔空間損耗也比較大。
另外,檔案的 Fragmentation 對存取效能也是一個問題,由於 exFAT 一樣使用 linked list 來分配檔案空間,所以這個問題也許會變得很糟。
不過如同上面提及電子式的儲存媒體的特性,這個問題在電子式的儲存媒體中基本上可以無視。
雖然 exFAT 有許多新的特性,例如:TFAT、free space bitmap、ACL 等等,不過我在 Windows 7 裡面沒有確認到這些特性有被實做出來。
另外 exFAT 一樣是兩個 FAT 是連續存放的,如果該區域損毀則整個分割區的資料都無法重建,因此我很難就可靠度、搜尋的議題上,在硬碟中使用 exFAT。
以目前來說,硬碟的每 GB 價格仍然遠低於電子式的儲存媒體,因此在適合 exFAT 的環境中,很難出現真正使用大量的大型檔案的應用。
如果要給 MS 建議的話,我會說請把未來新版的 NTFS 的最大磁簇大小放大成跟 exFAT 甚至超過,這樣比較有意義。
2010/08/02
使用 SSD 該注意的事情
根據水球的鼓吹,我買了 SSD 回來當系統碟。
過去很少注意 Windows 7 系統的調教,不過因為據說 SSD 寫入次數比硬碟少非常多,所以上網找了一些文章來實驗。
我用來修改系統設定的是 Ultimate Windows Tweaker,簡稱 UWT。
底下是一些心得:
- 要裝 RAMDisk 把 TEMP、瀏覽器的 Cache、Search 的索引資料等等指到 RAMDisk 上。
RAMDisk 我是用 DataRAM RAMDisk,固定磁碟機代號的方法請看這篇。
簡單的說就是先改變產生出來的 RAMDisk 磁碟機代號,然後執行 Save Disk Image Now,再把 Load Disk Image at Startup 勾起來,然後按 CTRL+S 把設定存起來即可。(Save Disk Image at Shutdown 不用勾) - 要把 Windows Event Log 和 Superfetch 服務設定為停止,並把 Windows\Prefetch 裡面的所有檔案與目錄刪除。
- 把 SSD 系統碟的 Paging file size 設成最小值 16M(不可設成 No paging file 或是 0,這一步必須先做完第二步,否則會無法把 Paging file size 設成 16M),並用 UWT 設定 Disable pagefile。
- 停止磁碟重組的自動排程。
- 其他的調整可以看這篇。
2010/07/29
小紅傘怪異兩三事
我一直以來使用的防毒軟體都是免費的小紅傘。
我愛用它的原因主要是兩個:
- 防毒能力不錯。
- 可以設定檔案讀取、寫入、或是兩者動作出現時進行即時掃描。
尤其是第二個,我通常設定檔案讀取時才進行即時掃描監控。
這樣的設定對於在跑 BT 軟體或是 Web Server 蠻有幫助的,跟選擇兩者時比較起來系統的反應會比較快一些~
不過最近發現一些小問題:
- 移除熱插拔的外接 eSATA 硬碟時會鎖定,不讓我移除。
- 上網使用 WebATM 時讀不到金融晶片卡。
後來發現只要先把小紅傘的的即時掃描先關掉,在進行上述的動作就都沒問題。
WebATM 的解決方式還有另一個解法:把小紅傘對各種威脅的項目全部不要選取即可。
2010/06/06
i18n 各類翻譯的方法
i18n 模組對 Drupal 的各類翻譯方式幾乎都能進行翻譯的支援,雖然有些方式比較麻煩,不過還算堪用~
Translation table 模組可以幫忙一半種類的翻譯(分類、內容類型、選單),其他的部份則是需要用搜尋的方式進行翻譯,而且一次只能翻譯一句,比較不方便。
其中最麻煩的是 CCK 的翻譯,翻完之後不會立即生效,必須要跑一下 update 才會生效(對,你沒看錯,就是那個升級系統跟模組用的 update)。
簡單來說,CCK 的翻譯流程如下:
- Refresh Text Group(這裡最好全選,如果在翻譯時沒看到更新的字串,就再 Refresh 一次)
- 搜尋 CCK 字串做翻譯,一次一個字串
- Run Update
- Refresh Text Group