已提出到 https://drupal.org/node/2081333
先說明一下這個問題的原因:
由於 record feature 會使用 array_diff_assoc 函數來比較 start / stop 之間的差異,但是該函數在比較時無法處理非 scalar 的變數,所以會造成 error 使得在 stop record 時程式中斷。
另外在 view diff 表格時,render cell 時也是無法處理非 scalar 的變數。
我提出的 Patch 主要是在修正這兩個 Bug,Patch 檔在上面的連結裡面可以下載~
失落的技術
2013/09/04
2013/08/31
SOAP 在 Windows 與 Linux 上的不同行為
前些日子做了一個專案,過程中有用到 SOAP 去呼叫遠端的 Web Service。
當我把程式寫完並在自己的機器上測試通過後,佈署到客戶租用的虛擬主機上時,卻發現程式不能正常執行。
後來追蹤後發現 PHP 的 SOAP 模組,在不同平台上的行為會不一樣~
其差異點如下:
當我把程式寫完並在自己的機器上測試通過後,佈署到客戶租用的虛擬主機上時,卻發現程式不能正常執行。
後來追蹤後發現 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 中的圖檔
前兩天在試著用 MigrateDestinationMedia 來匯入在 Body 中的圖檔,不過一直無法匯入多個檔案。
後來問了 Google 大神,請教先進,追了程式碼之後,還是不曉得該如何直接利用 MigrateDestinationMedia 來直接匯入多個檔案。
所以今天我研究了一下,寫了一個 FileMigration 類別來幹這件事。
有一些事要注意:
類別原始碼:
後來問了 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 網站的時候,我們會花很多在測試模組上,這時候我們會需要一個能夠快速備份還原資料庫的功能~
在 Drupal 模組中,有兩個模組可以做這件事,一個是 Backup and Migrate 模組,另外一個是 Demonstration site 模組。
然而 Backup and Migrate 有個致命的缺點:那就是在還原資料庫時,他不會先 Drop 掉所有的資料表再還原。
這個缺點會有什麼影響呢?試想下列情況:
通常我們在使用還原功能時,並不會有習慣去 disable & uninstall 模組,所以這個缺點還蠻要命的~
另外 Backup and Migrate 還有個問題就是只支援 MySQL / MariaDB,其他的資料庫並不支援;不過這個問題我個人是覺得還好,畢竟比較常用的也是 MySQL。
最後要說一下 Demonstration site 模組的問題:
在 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
直接講結論:還是 JW Player 最好~
我測試的結果,播放器有以下的問題 (至少有一點):
下載:
我測試的結果,播放器有以下的問題 (至少有一點):
- 沒有支援 Flash fallback (純 HTML 5 的播放器都沒有)
- 沒辦法支援 Embedded Media Field
- 明明把長寬尺寸設定好,大小還是不對
下載:
2013/08/07
Drupal Media 的檔案管理
在 Drupal 中,檔案的管理始終是一個麻煩的問題。
以前我使用 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,儲存後再插入圖片時就會顯示出來。
以前我使用 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
一顆硬碟,一個分割區
在過去常有朋友問我,要怎麼分割硬碟,每個分割區多少空間比較好?
其實在每個階段,我給出的答案都不一樣。
時至今日,如果你問我,我自己又是如何做的呢?
目前我的電腦上的硬碟是這樣配置的:
我個人算是有輕微的收集癖好,所以要用到 NAS 來放,如果你時間很多且勤於整理的話,不用 NAS 也是可以~
在這裡還有一個重點,那就是這三顆硬碟都是 MBR / One Primary Partition,即標題所說的:一顆硬碟,一個分割區。
現今硬碟已經很便宜,主機板上的晶片組支援的 SATA3 也很多,不需要折磨自己只用一顆硬碟切來切去。
分割硬碟不如做好目錄管理比較重要。
其實在每個階段,我給出的答案都不一樣。
時至今日,如果你問我,我自己又是如何做的呢?
目前我的電腦上的硬碟是這樣配置的:
- 一顆 SSD (64G):用來放系統
- 一顆 WD 黑標 (500G):用來放一般 HTTP、FTP 下載回來的東西 (通常檔案較小)、免安裝軟體、其他我自己或程式產生的資料、音樂、圖檔等等大雜燴
- 一顆 WD 紅標 (2T):用來放 BT、eMule、迅雷等等下載回來的檔案倉庫
我個人算是有輕微的收集癖好,所以要用到 NAS 來放,如果你時間很多且勤於整理的話,不用 NAS 也是可以~
在這裡還有一個重點,那就是這三顆硬碟都是 MBR / One Primary Partition,即標題所說的:一顆硬碟,一個分割區。
現今硬碟已經很便宜,主機板上的晶片組支援的 SATA3 也很多,不需要折磨自己只用一顆硬碟切來切去。
分割硬碟不如做好目錄管理比較重要。
訂閱:
文章 (Atom)