2012年10月15日 星期一

xsl 裡輸出 enter 符號的方法


之前有一個案子, 一次輸出5000筆的資料時, 新的元件處理速度上比舊的元件快很多, 因為效能的關係, 程式碼裡的的xsl object, 由原本的:
server.createObject("microsoft.XMLDOM")

修改為:
server.createObject("Msxml2.FreeThreadedDOMDocument.6.0")
或:
server.createObject("Msxml2.DOMDocument.6.0")

造成原本用<![CDATA[
]]>
就可以產生 ENTER 符號, 不能 Work,
挑戰修改 xsl 屬性:
<xsl:output method="text" encoding="utf-8" omit-xml-declaration="yes" indent="no" standalone="yes"/>
一樣無效.


最後找到解決方法了, xsl 裡要輸出Enter 要使用:
<xsl:text>&#xD;</xsl:text>

2012年10月11日 星期四

分享我在AP 裡幫圖片自動縮圖心得及做法

分享我在AP 裡幫圖片自動縮圖心得及做法, 我在做縮圖的情況有2種: 產品和AP.

首先,我用的縮圖元件是ImageMagick, 請先在下面的 URL 下載安裝檔:

http://www.imagemagick.org/script/binary-releases.php#windows

ImageMagick


32位元的Windows 請下載這個檔案:
Version: ImageMagick-6.?.?-?-Q16-windows-dll.exe
Description: Win32 dynamic at 16 bits-per-pixel

如果是 x64 平台, 也可下載這一個版本:
Version: ImageMagick-6.7.9-10-Q16-windows-x64-dll.exe
Description: Win64 dynamic at 16 bits-per-pixel




產品面和 AP 面差在產品面是專門給產品用, 其他 AP 沒辦法直接存取, 算是一種客制化, 相對輸入的參數就會很簡單, 只要傳產品的id 進來即可.

  • 1. 產品面:
    這個的輸入參數有2個, 一個是文章 ID (必填), 一個是節點的 ID (非必填).

    在縮圖的時候, 會先把縮圖的參數設定檔取出來, 看看目前的文章ID 是不是符合到設定檔裡的那一個範本, 通常會這麼做, 就是因為網站規模比較大, 又要做的有彈性和有效率.

    縮圖的設定檔的回傳規則是,
    1.1: 先以節點 ID 為模式(pattern)來搜尋, 如果有符合節點的話就傳回要產生的縮圖設定檔.
    1.2 再以文章 ID所屬的表格(Table, 或單元) 為模式(pattern)來搜尋縮圖設定檔.
    1.3 再以文章ID 所屬的範本(Definition) 為模式(pattern)來搜尋縮圖設定檔.
    1.4 最後都沒找到的話, 就傳回 "預設的縮圖設定檔".
  • 2. AP 面:
    AP 面也可以用在產品面上面, AP面使用起來也很有彈性, 和產品面的差別在, 呼叫縮圖副程式時, AP面要把實體的圖片路徑傳給副程式, 產品面的話只需要傳入文章ID.

下面就以 AP 為例, 來看看如何套用縮圖副程式:
  • step 1: include 副程式, 如果是其他物件化導向的程式語言, 這一個 step 應該是 import 或 引用縮圖元件.
  • step 2: 新增文章時增加這一行指令:
    call gen_ap_image_filename(image_filepath, fileUploadPath, "180", "", "", "180")

    副程式的參數說明:
    1. image_filepath, 上傳的圖片實體路徑.
    2. fileUploadPath, 上傳的資料夾的實體路徑.
    3. resize_outputIconFolder, 要縮圖到子資料夾的名稱, 當同一個資料夾的檔案數太多時, 系統效能會變差, 所以縮圖並不一定要和原圖放在同一個資料夾下.
    4. resize_outputIconFilePrefix, 產生的新縮圖檔名, 是不要加一個前置字串, 像是 "s_", "m_", "l_" 之類的, 以區分大中小圖.
    5. resize_outputIconFilePostfix, 產生的新縮圖檔名, 是不要加一個後置字串, 像是 "_s", "_m", "_l" 之類的, 以區分大中小圖.
    6. resize_targetWidth, 圖片的 max-height 和 max-width, 以輸入 180 來說指的是圖寬不會超過 180px, 圖高也不會超過180px.

  • step 3: 修改文章, submit 後, 在更新資料(update table)前, 增加這一行指令, 用來刪除修改前的實體縮圖檔案:
    call delete_old_resized_image_db(image_fieldName, dbTableName, myWhereCondition, fileUploadPath, "180", "", "")
    說明: 這個附程式會先透過前3個參數, 取得 image 的實體 filename, 再透過第4個參數(fileUploadPath) 取得圖片的實體檔案路徑, 然後再用 FileSystemObject 刪除實體的縮圖檔案, 所以並不一定要透過上面這一個 delete_old_resized_image_db() 副程式來刪, 也可以直接用 FileSystemObject 刪除實體的縮圖檔案
  • step 4: 修改文章, submit 後, 在 update database後, 比照 step 2, 呼叫來產生縮圖.
  • step 5: 刪除文章時, 比照 step 3, 在刪除資料(delete recored) 前, 刪除實體的縮圖檔案.

如果是產品面的更新, 參考下圖,
1. 在修改前呼叫副程式: deleteThumbImages 把文章 ID 傳入, 刪除縮圖檔案.
2. 更新文章內容,
3. 產內縮圖, 傳入文章ID, 和節點ID(NodeID).


對於已經存在的資料, 可以參考看看下面的 sample 來整批的轉檔::
dim sql
sql = "select * from tablename"

dim rs
set rs = conn.execute(sql)
if not rs.eof then
    do
        dim id
        id = trim("" & rs("id"))
        
        '// 客製化, 處理縮圖
        '// in file: ???.ImageMagic.function.asp
        dim cust_image_fieldName
        dim cust_image_fieldvalue
        cust_image_fieldName = "RoomPhoto"
        cust_image_fieldvalue = trim("" & RS("" & cust_image_fieldName))
        call gen_ap_image_filename(cust_image_fieldvalue, "(你的上傳資料夾)", "180", "", "", "180")
        
        response.write "<br/>ID:" & id
        rs.movenext
        if rs.eof then
            exit do
        end if
    loop
end if

2012年10月9日 星期二

為什麼 table 被載入到 DOM 時, 會多加 tbody tag


今天看別人上課的ppt, 裡面問到一個問題:
為什麼 table 被載入到 DOM 時, 會多加 tbody tag?

--------------
Nickolay 說:

To summarize the excellent explanations given in the answers and comments by bobince, Kieron, Alohci and others:
  1. Firebug just displays the DOM of the parsed page. Due to complicated HTML parsing rules, the DOM will "differ" (in some sense) from the source HTML.
  2. In this case the TBODY element in the DOM is added by the HTML parser. Note that this weird parsing is limited to text/html documents and in XHTML the DOM corresponds closely to the source XML.
    • This behavior was specified in HTML 4. The content model (allowed children) for table is(CAPTION?, (COL*|COLGROUP*), THEAD?, TFOOT?, TBODY+) -- trs are only allowed intbody! The spec says that tbody's start tag is optional, which is supposed to mean that if the HTML parser encounters tr directly inside a table it inserts the tbody start tag omitted by the author.
    • To make matters more clear HTML 5 defines very detailed parsing rules, in particular for this case: "When the insertion mode is "in table", tokens must be handled as follows: [...] A start tag whose tag name is one of: "td", "th", "tr" -> Act as if a start tag token with the tag name "tbody" had been seen, then reprocess the current token."


--------------
原來 tbody 是一個重要的 tag, 下面這篇文章明確指出, 如果用 DOM 方法建立 table 時, IE 不會自己加一個 tbody, 此時當我們試圖添加 tr 在 table 之下時, IE 也不會成功。

A bug about table in M$IE
http://blog.roodo.com/rocksaying/archives/2690905.html


--------------
相關文章:
Why does firebug add <tbody> to <table>?
http://stackoverflow.com/questions/1678494/why-does-firebug-add-tbody-to-table

asp 呼叫 .net 的 web service 以 "e政府服務平臺單一登入介接" 為例

asp 呼叫 .net 的 web service 以 "e政府服務平臺單一登入介接" 為例, 原來用 asp 呼叫 wsdl 還滿簡單的.


1. 認識一下 e政府服務平台(Government Service Platform, GSP):

單一登入介接說明

架構圖, 我猜主要應該要看左半邊.



單一登入介接的效益
  • 減少機關/業務重複開發成本:
    各機關業務建置入口網站及業務服務時,對使用者都有認證、授權的共同需求,使用平台的單一登入機制,可以縮短建置時間並減少開發成本。
  • 支援不同的認證方式:
    使用者可使用自然人/工商/機關憑證或一般帳號密碼為登入認證方式。
  • 使用相同的登入帳號、單一的入口網址及登入介面:
    使用者在存取各機關服務時可以不必註冊多個帳號密碼,以達簡化使用及電子化政府便民之目的。


Web Service 位址:
https://www.cp.gov.tw/SEWebApplication/RSMediator.asmx

在登入成功取得token1後,呼叫GetUserProfile傳入token1,取得
userprofile,內含<SecureLevel>可以判定使用者的登入方式
. <SecureLevel>值的意義如下:
. IDPassword:代表帳號密碼登入
. PlatformX509或AnonymousX509:代表憑證登入

單一登入介接說明
其實, 就是登入的 URL 都連到 www.cp.gov.tw , 再把登入後要連的 URL 放在 returnurl 參數裡即可.

登入完成, www.cp.gov.tw 會用 post 的方式把 token1 放在 twGovT1 這個參數傳給 returnurl 裡的程式.


2. 寫 asp 程式呼叫 wsdl:
參考看看下面2支副程式:


'// purpose: 取得 e政府服務平台註冊服務模組介接服務 web service.
function getGspServiceXml(byval twGovT1)
    dim returnValue
    returnValue = ""
    
    '// 設定 Web Service 位址:
    dim ws_HOST
    ws_HOST = "www.cp.gov.tw"
    dim ws_URL
    ws_URL = "https://"& ws_HOST &"/SEWebApplication/RSMediator.asmx"

    '// Web Service SOAP 內容:
    SoapRequestStr = ""
    SoapRequestStr = SoapRequestStr & "<?xml version=""1.0"" encoding=""utf-8""?>"
    SoapRequestStr = SoapRequestStr & "<soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">"
    SoapRequestStr = SoapRequestStr & "<soap:Body>"
    SoapRequestStr = SoapRequestStr & "<GetUserProfile xmlns=""http://tempuri.org/"">"
    SoapRequestStr = SoapRequestStr & "<Token1>"& twGovT1 &"</Token1>"
    SoapRequestStr = SoapRequestStr & "</GetUserProfile>"
    SoapRequestStr = SoapRequestStr & "</soap:Body>"
    SoapRequestStr = SoapRequestStr & "</soap:Envelope>"

    '// 送出 SOAP
    dim SoapRequest
    Set SoapRequest = Server.CreateObject("MSXML2.XMLHTTP")
    SoapRequest.Open "POST", ws_URL & "?wsdl", False
    SoapRequest.setRequestHeader "Content-Type", "text/xml;charset=utf-8"
    SoapRequest.setRequestHeader "HOST","www.cp.gov.tw"
    SoapRequest.setRequestHeader "Content-Length",LEN(SoapRequestStr)
    SoapRequest.setRequestHeader "SOAPAction", "http://tempuri.org/GetUserProfile"
    on error resume next
    SoapRequest.Send(SoapRequestStr)
    if err.number <> 0 then
        '// debug, show error message
    else
        '// success.
        returnValue = SoapRequest.responseXML.xml
    end if
    getGspServiceXml = returnValue
    'set SoapRequest = nothing
end function

'// purpose: 取得 e政府服務平台註冊服務模組介接服務 web service 裡的內容.
function getGspDataByXPath(byval soap_return_xml, byval queryXPath)
    dim returnValue
    returnValue = ""
    dim soap_XML
    set soap_XML = server.createObject("Msxml2.DOMDocument")
    soap_XML.async = false
    soap_XML.loadXML(soap_return_xml)
    if soap_XML.parseError.reason <> "" then
        '// load soap xml error.
    else
        if not (soap_XML.documentElement.selectSingleNode("//GetUserProfileResult") is nothing) then
            dim ResultXml
            set ResultXml = server.createObject("Msxml2.DOMDocument")
            ResultXml.async = false
            ResultXmlString = trim("" & soap_XML.documentElement.selectSingleNode("//GetUserProfileResult").text)
            ResultXml.loadXML(ResultXmlString)
            if ResultXml.parseError.reason <> "" then
                '// load soap xml error.
            else
                returnValue = nulltext(ResultXml.selectSingleNode(queryXPath))
            end if
        end if
    end if
    getGspDataByXPath = returnValue
end function

function nullText(byref xNode)
    dim xstr
    xstr = ""
    if not (xNode is nothing) then
        xstr = xNode.text
    end if
    nullText = xStr
end function

主程式如下:
dim soap_return_xml
soap_return_xml = trim("" & getGspServiceXml(twGovT1))
if soap_return_xml <> "" then
    myData = getGspDataByXPath(soap_return_xml, myXPath)
else
    '// Error.
end if

呼叫完, 透過 token 從 web service 取到的 XML 內容如下:
SOAP 回應的XML內容

以我的個案, 要取得 email 來說, 
mailXPath = "//CPWSResponse/Result/UserProfile/ContactInfo/Mail"
mailData = getGspDataByXPath(soap_return_xml, mailXPath)

就可以取到 eMail, 由於我只有要取一次, 所以程式碼沒有寫的很有效率, 如果您要取的欄位很多, 可能要換一個寫法, 會比較有效率一點點.


附註:
如果要介接 e政府, 需要先填寫申請單:
1.e政府服務平臺服務介接申請表_SSO_Service Application.doc

如果沒有填, 就會出現下面的錯誤畫面:
domain name 還沒開通前的畫面.

要解決這個問題很簡單, 設一下 host 就解決了, 先用別的已經通過申請的  domain name即可.

之前 UI 是規畫 id/password 的輸入框是在客戶的網站上, 而不是連到 eGov  去登入, 挑戰直接 submit 到 eGov 的 https://www.cp.gov.tw/portal/Login.aspx, 結果會出現下面的錯誤訊息:
超有趣的錯誤訊息. ╰( ̄▽ ̄)╭

2012年10月4日 星期四

HTML 轉成圖檔 & PDF


HTML 轉成image 使用 wkhtmltoimage
HTML 轉成PDF 使用 wkhtmltopdf

使用 wkhtml2image 截取 google 首頁


相關文章:

將 HTML 轉成圖檔 & PDF 的工具: wkhtmltoimage, wkhtmltopdf
http://brooky.cc/2011/05/26/convert-html-to-image-and-pdf/

wkhtmltopdf 0.9.6 Manual
http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf-0.9.9-doc.html



wkhtmltoimage 0.10.0 rc2 Manual
http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltoimage_0.10.0_rc2-doc.html

wkhtmltopdf (project)
http://code.google.com/p/wkhtmltopdf/

2012年10月2日 星期二

[Javascript].carousel not sliding in ie9

有一個專案, 使用了 jQuery carousel 元件, 但在 IE9 的情況下, 顯示會有異常, 解決方法如下, 在 <head> 區塊裡, 增加 <meta> tag 即可.

<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
or
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"/>

or the HTTP header:
X-UA-Compatible: IE=Edge



增加 tag 後的畫面:
說明: 增加 tag 之後, IE9 會自動啟動相容性檢視, 就可以正常看到 carousel 元件的圖示.



相關文章:

carousel not sliding in ie9 intranet (2.02)
https://github.com/twitter/bootstrap/issues/2726

Why does IE9 switch to compatibility mode on my website?
http://stackoverflow.com/questions/3726357/why-does-ie9-switch-to-compatibility-mode-on-my-website

2012年10月1日 星期一

[SQL].刪除Databse時的注意事項


情況是, 我要更新database 到客戶的主機上, 由於Database 無法直接使用還原做覆蓋, 所以我就把database 刪掉, 打算重新再還原, 結果悲劇就發生了, 客戶給的 account 無法連進SQL Server, 因為無法開啟使用者預設資料庫.



結論:
1. 刪掉database 時, 要先檢查看看有沒有那一個帳號預設資料庫是要被刪掉的這一個database.
2. 研究看看 "還原" database 時, 是不是讓 database 先 off-line 就可以直接 overwrite.

Facebook 留言板