Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
*測試型號:Cisco MDS 9124 fabric switch
*管理 PC 作業系統:Microsoft Windows XP Pro SP2 中文版
*線材準備
##RJ-45 to DB-9 或 DB-25 母頭:視連接 console 的電腦而定
##Rollover RJ-45 to RJ-45 線
##(Optional) Crossover RJ-45 to RJ-45 線:PC 直接串接 Switch 時使用,若 Switch 可直接接至乙太網路交換器或集線器則使用一般網路線即可
*超級終端機設定:預設使用 ''com1'' 連線,每秒傳輸位元 ''9600'',資料位元 ''8'',同位檢查 ''無'',流量控制 ''無''
*第一次開機完成會詢問 admin 管理帳號密碼
*初次設定可使用 SETUP 設定
*設定完管理介面 IP 後,透過瀏覽器 (IE recommended) 輸入管理介面的 IP
*選擇安裝 Cisco Device Manager,過程中會先安裝 JRE,請確定網路連線正常,待 JRE 安裝完成後會繼續從 Switch 裡直接下載執行 CDM 管理介面
*檢查連接埠是否處於正常,請注意連接埠上所屬的連接埠類型,相互存取的 Storage 與 Host 間是否屬於相同的類型
*@@color:blue;font-size:1.2em;最重要的一步:選擇 Auto Config,將相互存取的連接埠勾起以建立相同的 Zone@@
*測試型號:HP MSA 2012FC
*管理 PC 作業系統:Microsoft Windows XP Pro SP2 中文版
*線材準備
##Micro-DB9 to DB9 主控台連接線 (Storage 應該會附)
##(Optional) Crossover RJ-45 to RJ-45 線:PC 直接串接 Switch 時使用,若 Switch 可直接接至乙太網路交換器或集線器則使用一般網路線即可
*超級終端機設定:預設使用 ''com1'' 連線,每秒傳輸位元 ''115200'',資料位元 ''8'',同位檢查 ''無'',流量控制 ''無''
*開啟主控台終端機後,在命令列模式 (CLI) 下輸入以下指令設定管理介面 IP
{{{
set network-parameters ip {ip_address} netmask {subnet_mask} gateway {default_gateway} controller a|b
}}}
**example
{{{
set network-parameters ip 192.168.1.110 netmask 255.255.255.0 gateway 192.168.1.254 controller a
}}}
*檢視目前管理介面網路狀態
{{{
show network-parameters
}}}
{{{
ping {IP-address}
}}}
*重新啟動管理介面
{{{
restart mc both|a|b
}}}
*重新啟動 RAID controller
{{{
restart sc both|a|b
}}}
*設定完管理介面 IP 即可透過瀏覽器進入 Web-based 圖型化管理介面
*SMU (Storage Management Utility) 登入預設帳號 @@color:red;font-size:1.4em;manage / !manage@@
!QoS (Quality of Service) 定義
*"A set of capabilities that allow you to create differentiated and guaranteed services for network traffic, thereby providing better service for selected applications and users."
*讓網路管理員可以建立具有差異性的網路服務流量,以利在所選擇的網路流量上提供更好的服務
*例如,QoS 可以針對重要的 (critical) 流量增加頻寬,減少不必要的流量所使用的頻寬,以及提供始終如一的網路回應 (network response)
*QoS Mechanisms
|QoS 流程|Admission Control|Congestion Management|Congestion Aviodance|Traffic Conditioning|Link-Efficiency Management|
|內涵|Classifying|Queuing|Dropping|Limiting|Fragmenting|
|Methods|ACLs<br>IPP/DSCP<br>IP RTP<br>NBAR<br>RSVP<br>Trust|PQ/CQ/WFQ<br>IP RTP Priority<br>CBWFQ/LLQ<br>2Q2T/1P2Q2T<br>WRR|Tail<br>WRED|Limiting<br>GTS/FRTS<br>RSVP<br>CAR<br>Policing|LFI<br>FRF.12<br>cRTP|
!Classifying Traffic
*ACLs (Access Control Lists)
*CoS (Class of Service) / IPP (IP Precedence) / DSCP (Differentiated Serive Code Point)
*IP RTP with CBWFQ (Class Based Weighted Fail Queuing)
**主要用於維持 VoIP 的服務品質 (UDP 16384 ~ 32767)
**Cisco Device only
**須看 software 與 Router 都必須支援
*NBAR (Network-Based Application Recognition)
*RSVP (Resource Reservation Protocol)
*Trust Boundary
**Catalyst 6000 Switch
**Particularly useful for a VoIP network
!Queuing Traffic
*PQ (Priority Queuing)
*CQ (Custom Queuing)
*WFQ (Weighted Fair Queuing)
*CBWFQ (Class Based WFQ) = CQ + WFQ
*IP RTP Priority = PQ + WFQ
*Low Latency Queuing (LLQ) = CBWFQ + PQ
*2Q2T / 1P2Q2T (Catalyst 6000)
*WRR (Weighted Round Robin) (Available on Cisco 8510 FE Ports only!)
!Dropping Traffic - Congestion Avoidance
*Tail Drop:Simply dropping the incoming packets when the buffer is full.
*WRED (Weighted Random Early Detection)
!Limiting Traffic - Traffic Conditioning
*GTS (Generic Traffic Shaping)
*FRTS (Frame Relay Traffic Shaping)
*CAR (Committed Access Rate - Limiting on Routers)
*Limiting on Switches - Catalyst 6000
!!Shaping VS Limiting
| Shaping | Limiting |
|Attempts to throttle traffic<br>Router buffers traffic bursts<br>Packets dropped when buffers are full<br>Generic Traffic Shaping(GTS)<br>Frame Relay Traffic Shaping(FRTS)|No packets dropped until rate limits reached<br>All packets dropped that exceed the burst rate limit<br>Committed Access Rate(CAR)|
<<toBalaManager>>
///%
比目魚的天空 - 藍色的網路技術!!!
''flatfish''@2008 powered by TWtBala - TiddlyWiki 2.3.0
<<tagsTree network "" 1 4 index label>>
<<tagsTree ICND "" 1 4 index label>>
<<tagsTree wireless "" 1 4 index label>>
<<tagsTree security "" 1 4 index label>>
<<tagsTree supplement "" 1 4 index label>>
<<tagsTree twcms "" 1 4 index label>>
<<tagsTree KMKConfig "" 1 4 index label>>
<<tabs txtMainTab "最近更新" "依更新日期排序" SideBarTabs/Timeline "分類" "所有標籤" TabTags "設定文章" "說所有設定文章" TabMoreShadowed>>
<<search>> | [[首頁]] | [[土芭樂 3.0 - 數位新思路|http://tbala.net/]] | [[TiddlyWiki 練功坊|http://tiddlywiki.tbala.net/]] | <<newTiddler label:"新增文章">> <<saveChanges>>
//%/
<<forEachTiddler
script '
window.fetItemsPerPage = 15;
function getHeader(context,count) {
if (!window.fetStartIndex || window.fetStartIndex < 0)
window.fetStartIndex = 0;
// ensure not to page behind the last page
if (window.fetStartIndex >= count)
window.fetStartIndex = Math.min(Math.max(window.fetStartIndex-window.fetItemsPerPage,0),count-1);
createTiddlyButton(context.place,"<",null,
function(e) {
window.fetStartIndex -= window.fetItemsPerPage;
story.refreshTiddler(context.viewerTiddler.title,null,true);
});
createTiddlyButton(context.place,">",null,
function(e) {
window.fetStartIndex += window.fetItemsPerPage;
story.refreshTiddler(context.viewerTiddler.title,null,true);
});
var startNo = window.fetStartIndex+1;
var endNo = Math.min(count,window.fetStartIndex+window.fetItemsPerPage);
return "("+startNo+" - "+endNo+ " of "+ count + " items)\n";
}
'
write
'(index >= window.fetStartIndex) && (index < window.fetStartIndex + 20) ? "* [["+tiddler.title+"]]\n" : ""'
begin
'getHeader(context,count)'
>>
// /%
With a little scripting you can use the ~ForEachTiddlerPlugin to display the result "by pages". I.e. you don't display the full result list, but (e.g.) 10 at a time.
Using a "pagewise" display may be useful when the result may get very large. This way you avoid scrolling the window to see the result. It also speeds up things since less items need to be display at a time.
''Code''
{{{
<<forEachTiddler
script '
window.fetItemsPerPage = 10;
function getHeader(context,count) {
if (!window.fetStartIndex || window.fetStartIndex < 0)
window.fetStartIndex = 0;
// ensure not to page behind the last page
if (window.fetStartIndex >= count)
window.fetStartIndex = Math.min(Math.max(window.fetStartIndex-window.fetItemsPerPage,0),count-1);
createTiddlyButton(context.place,"<",null,
function(e) {
window.fetStartIndex -= window.fetItemsPerPage;
story.refreshTiddler(context.viewerTiddler.title,null,true);
});
createTiddlyButton(context.place,">",null,
function(e) {
window.fetStartIndex += window.fetItemsPerPage;
story.refreshTiddler(context.viewerTiddler.title,null,true);
});
var startNo = window.fetStartIndex+1;
var endNo = Math.min(count,window.fetStartIndex+window.fetItemsPerPage);
return "("+startNo+" - "+endNo+ " of "+ count + " items)\n";
}
'
write
'(index >= window.fetStartIndex) && (index < window.fetStartIndex + 10) ? "* [["+tiddler.title+"]]\n" : ""'
begin
'getHeader(context,count)'
>>
}}}
// %/
<<forEachTiddler
where
'tiddler.tags.contains("plugin")'
>>
<<forEachTiddler
where
'tiddler.tags.contains("macro")'
>>
<<forEachTiddler
where
'tiddler.tags.length == 0'
>>
|連線|使用者模式|//command//|特權模式|//command//|整體設定模式|//command//|介面模式|線路模式|
|AUX|''Router>''|//enable//|''Router#''|//configure terminal//|''Router<br>(config)#''|//interface 類型 , slot/port//|''Router<br>(config-if)#''||
|~|~|~|~|~|~|~|~|~|
|控制台|~|~|~|~|~|~|~|~|
|~|~|~|~|~|~|//line 類型//||''Router<br>(config-line)#''|
|telnet|~|~|~|~|~|~|~|~|
|~|~|~|~|~|~|~|~|~|
|功能|1.基本檢視<br>2.網路測試||1.完整檢視<br>2.系統管理<br>3.除錯||1.整部設備設定<br>2.路由功能||該介面設定|該管理介面設定|
|指令|<<slider 指令列表 使用者模式指令 檢視指令 "完整的使用者模式指令">>||<<slider 指令列表 特權模式指令 檢視指令 "完整的特權模式指令">>||<<slider 指令列表 整體設定模式指令 檢視指令 "完整路由器整體設定模式下的指令">>||<<slider 指令列表 介面模式指令 檢視指令 "完整的介面模式指令">>|<<slider 指令列表 線路模式指令 檢視指令 "完整的線路/管理模式指令">>|
|說明|比較metaSAN與Sanbolic Melio FS兩個SAN架構下Storage存取的效能差異|備 註|
|測試環境|測試平台:Windows Server 2003 Enterprise Edition R2中文版;效能測試軟體PC Wizard 2008 (freeware)||
|測試步驟|1、 在兩部Server安裝完作業系統及相關驅動程式後,確定一台Server可以存取Storage,利用效能測試軟體測試無安裝任何SAN管理軟體環境下Storage I/O效能,測試完成後做系統完整備份<br>2、 分別在兩部Server安裝metaSAN評估版,並上網取得啟動序號,設定完metaSAN並確定SAN Storage功能正常後,使用效能測試軟體測試取得Storage I/O效能數據,完成後做系統完整備份<br>3、 將系統還原至原始系統狀態,並安裝Sanbolic Melio FS軟體,待設定完成後,將Storage的Volume執行Melio format,確定存取正常後使用效能測試軟體取得Storage I/O效能數據,完成後做系統完整備份||
|測試結果|1、 經比對後發現,Sanbolic Melio FS僅在隨機讀取(Random reading)效能勝過不安裝任何軟體以及metaSAN,請參考(圖一)<br>2、 metaSAN在效能上因延用NTFS檔案系統,因此效能與未安裝前相當,請參考(圖一)<br>3、 CPU Usage上,metaSAN則略勝Melio FS一籌,由於Melio FS自己特有的檔案系統格式,因此推斷其可提高Random reading的效能,但需要額外的CPU運算,請參考(圖二)<br>4、 經多次測試後,粗估誤差約為10-15%間||
|待測試|||
<html><img src="image/Drive Info - Local Drive.jpg"/></html>
Local Drive 測試環境
<html><img src="image/Drive Info - SAN Storage.jpg"/></html>
SAN Storage 測試環境
<html><img src="image/san_soft_IO_perf.jpg"/></html>
圖一
<html><img src="image/san_soft_cpu_usage.jpg"/></html>
圖二
*附件:<html><a href="download/2008.12.08-SAN軟體效能測試報告(Sanbolic Melio FS & metaSAN).xls">測試報告數據</a></html>
!資訊安全的重要
*企業資訊化的過程,
**電腦系統可能被入侵、攻擊、破壞
**資料可能被攔截、竊取
**資料可能被不當刪除、修改
!資訊安全 (InfoSec)
*資訊安全是一種防止與偵測未經授權 (unauthorized) 而使用、竊取、破壞資訊系統的一種過程與程序 (process and procedure)
*經過妥善的規劃、實施必要的安全措施 (Counter Measures)、持續不斷的檢討改進以維持資訊的安全
!BS 7799 (ISO27001) 資訊安全管理系統標準定義
*全球資訊安全管理標準
*『資訊對組織而言是一種資產,與其他有形資產一樣有價值,都需要妥善保護,資訊安全使資訊不受不受威脅,確保營運,將損失降到最低,以得到豐厚的投資報酬率和商機』
!!資訊安全的取捨分析 (TOA)
*資訊安全的本質:
**不方便
**犧牲效率
**增加成本
**不信任與衝突
*資訊安全的管理需做取捨分析 (Trade-Off Analysis)
*原則 —— 任何資訊安全措施均不應該影響到企業流程的正常運作
!何謂 ISA (Internet Security and Acceleration)
*微軟企業等級的防火牆,提供以下功能
**防火牆
**代理伺服器 快取服務
**VPN
!ISA 2006 標準版本與企業版本比較
|特性|標準版本|企業版本|
|硬體支援|4 CPU、2GB Ram|無限|
|原則|僅存在本機|ISA Array (一部設定,套用在所有 ISA)|
|Cache Array Routing Protocol (CARP)|無|有|
|負載平衡|無|有 (最多 32 hops)|
|ISA 組態儲存|本機|ADAM|
|中央管控|無|有|
|Price|$1499|$5999|
!ISA Server 2006 架構
*可管控 OSI 第三至第七層
|Policy<br>Engine|Web Proxy Filter|RPC filter|DNS filter 做入侵偵測|...|Application filter|
|~|>|>|>|>|>| Application Filter API |
|~|>|>|>|>|>|>| Firewall Service (封包過濾、狀態檢視) |
|~|>|>|>| Firewall Engine (封包過濾) |>|>| IP Stack |
|~|>|>|>|>|>|>| NDIS (微軟資料連結層協定) |
!IISA Server 2006 網路模式
!!ISA 歷史
|NT 3.5|Proxy 1.0|
|NT4|Proxy 2.0|
|Win 2000|ISA 2000|
|Win 2003|ISA 2004|
|最新|ISA 2006|
!!網路模式
*自 ISA 2004 起支援多重網路
*ISA Server 自己屬於一個獨立網段,不屬於內部網路,也不屬於外部網路,也不屬於 DMZ 區
*VPN 視為一個獨立網段
*可指定網路間的關係:NAT (會轉址) / Route (不會轉址)
*每個網段都可以設定獨立的政策
*所有介面都可做封包過濾
*支援隨插即用與需求撥號 (Dial on demand)
*資訊安全的工作說穿的就是風險管理
!!安全管理概念
*目標:
**安全管理是將公司的風險降低到一個可以接受的程度,並且持續維持這個可以接受的程度的過程
*管理學中 4 個風險管理的策略:
##Reduce 降低風險
##Avoid 避免風險
##Transfer 移轉風險
##Accept 接受風險
**資訊安全管理的目標在『降低風險』到一個可以接受的程度,因為不可能『避免風險』
*安全管理必須由上往下 (Top-down) 實施,由上層管理人員至一般員工必須參與
**包括掃地的阿桑都需要接受教育訓練,例如擦桌子不能水擦,也是資安的一環
*為達到安全的管理目標,安全性管理區分下面三個控制層面
**管理控制
**技術控制
**實體控制
!資訊安全的基本原則 —— C.I.A.
*管理人員在管理的環境內,確保、提供以下的安全性的服務
**安全性基本服務:
***機密性 (Confidentiality)
***完整性 (Integrity)
***可用性 (Availability)
**其他安全性服務
***不可否認性 (Non-repudiation):電子商務
***存取控制 (Access Control):1999 年聯合國資訊安全組織提出
***身份驗證 (Authentication):1999 年聯合國資訊安全組織提出
!CIA vs DAD
*DAD
**揭露 (Disclosure)
**竄改 (Alteration)
**破壞 (Desruction)
*CIA 提供了資訊安全基本服務
*DAD 提供了防禦的目標
!機密性 (Confidentiality)
*定義:Prevent disclosure of data,避免資料洩漏
*意義:
**確保資料傳遞與儲存的私密性
**避免未經授權的使用者有意或無意的揭露資料內涵
*方法:
**機密性資料傳遞和儲存時務必加密處理
***Scramble:搗亂,透過對原始資料 scramble,讓其他人看不懂
***加密法一定可以破解,只是時間問題
**採用安全性協定 (例如:SSL、IPSec)
**Steganography (隱匿法):Secret Data Embedded Within Normal Object
***將重要機密資料嵌入正常的物件中 (例如 image file)
!完整性 (Integrity)
*定義:Prevent modification of data,避免資料被不當修改
*意義:避免未經授權的使用者或處理程序竄改資料
*所使用的文件經傳送或儲存過程中必須__證明__其內容並未遭到竄改或偽造
*方法:
**透過 HASH 雜湊,因 HASH 具備 Diffusion
***常用的雜湊函數:MD5、SHA (微軟密碼使用 MD4 加密)
!可用性 (Availability)
*定義:Ensure reliable timely access to data,確保可以可靠、即時、(精確)的存取到資料
*意義:讓資料或服務隨時維護在可用狀態,且需確保不能中斷服務 (高度可用性,high availability)
*可用性評估統計量透過 DownTime:一年系統中斷服務的百分比
**美國高度可用性標準:<= 0.01% (三『0』)
**業界高度可用性趨近四『0』,<= 0.0001%
**最常使用在高度可用性的技術:Cluster
*方法:
**容錯能力 (fault torelance):避免單點失誤 (Avoid single point fo failure)
***例如磁碟陣列
**效率調整 (performance tuning)
!!Denial of Service
*針對頻寬的可用性攻擊
*由於目前網際網路主流攻擊方式為 DoS,因此造成可用性較其他兩項難以達到
!不可否認性 (Non-repudiation)
*意義:
**管理的環境必須有機制,避免有心人士否認其曾經做過的事情,包括送出信件、接收文件、存取資料
*條件:
**法律上的不可否認性
**數學上的不可否認性
*方法
**數位簽署 (Digital Signature):目前僅數位簽署具備上述兩個條件
***電子郵件透過 S/MIME 可使用數位簽署
!!範例1:HTTP + SSL = HTTPS
*具備加密性、完整性
*不提供可用性、不可否認性
**若導入用戶端憑證驗證,則可具備不可否認性
*驗證服務:SSL 提供機器連線階段驗證,但無使用者驗證
*不具存取控制 (AC)
!!範例2:防火牆
*主要為提供存取控制 (AC)
!資訊安全管理基本名詞
*Identification 身份
**使用者對系統宣稱其識別,身份對於驗證與授權是必要的
*Authentication 驗證
**測試或檢查使用者所宣稱的身份是否屬實
*Authorization 授權
**經過驗證後,系統決定將被允許執行何種存取和權限
***NTFS Permission
*Accountability 權責歸屬
**系統必須有能力確認使用者在機器上做過什麼動作,並且為使用者做過的動作負責
**稽核 (audit) 和紀錄日誌檔
***稽核在做紀錄,但紀錄不一定會稽核
*AAA:Authentication、Authorization、Accountability
!資訊安全四項最重要工作
*資料分類
*風險管理
*安全政策
*安全認知、教育訓練
!資訊分類 (Information Clasification)
*資訊安全管理實務的首要工作為資訊分類
*不同的資訊對公司有不同的價值與重要性
*資訊分類的好處:
**幫助了解哪些資料是重要的、哪些不重要
**確認哪些資料需要做何種防護措施
**資訊分類可以協助管理人員有效的降低資料保護成本
*原則上:資料的擁有者 (Data Owner) 需要負責定義資料的敏感性等級
**在公司內,資料擁有者是公司老闆
!!資料分類等級
*政府資料分類等級 (依據一旦外洩,會對國家產生傷害的嚴重程度加以劃分)
**Unclassified 未分類
***資料洩漏對國家並未造成損失
**Sensitive but Unclassified (SBU) 敏感但未分類
**Confidential 機密
***資料洩漏會對國家造成損失
**Secret 極機密
***資料洩漏會對國家造成嚴重損失
***在美國最多保留 20 年,後則降為 Confidential
**Top Secret 絕對機密:
***資料洩漏會對國家安全造成極為嚴重且立即的損失
***在美國最多保留 30 年,後則降為 Secret
***對於 Top Secret,在美國需兩個條件
####軍方機密需三顆星以上
####需知原則 (Need to Know):對於有權限觀看的人員必須要知道的才能觀看
*私人組織資訊分類等級 (由低到高)
**Public:僅需提供完整性
**Sensitive:
**Private:員工醫療資訊
**Confidential:商業機密、設計藍圖、企業併購案、顧客資料
*資訊分類的考量要素:資料的價值 (最重要)、資料的年代、資料的有效生命周期 (例如牽涉到商標權的問題,米老鼠)、特定的私屬關聯性、法律的要求 (Legal requirements)
!!聚合 (aggregation)
*聚合是將一群原本被分成較低安全層級的資料,一旦整合後會得到較高安全層級的資料,則會將這群較低安全層級資料一開始分類就放在較高安全層級
!!需知原則 (Need to know)
*限制員工在他的職責領域內必須要知道的資料才能讀取
*限制信任的員工做壞事
!!資料分級的角色與責任
*擁有者 (Owner)
**決定資料的保護和使用
**決定資料的分類、並定期檢視資料的分類是否要變更
***在美國通常以一年為期
**擁有者通常會將資料保護的責任委託給其他保管人 (custodian)
*受委託的管理人 (Custodian)
**被擁有人賦予保護資料責任的管理人
**負責實施安全措施 (如 ACL) 以保護資料
**定期備份、@@color:red;測試備份資料的有效性@@,必要時執行還原資料
***國內做到定期備份,但測試備份資料並不完善
*使用者 (User)
**經過擁有者授權而擁有足夠的存取權限,而且因工作的需要而必須使用資料的人員
*不同的人員必須經過差別的
##僱用審核
##責任區分 (separation of duties)
##教育訓練
!風險 (Risk):風險管理、風險分析
!!風險 (Risk)
*風險是遭受損失的可能性
*風險的內涵:發生的頻率、嚴重性
**Rik combines the frequency and severity of a specified occurrence)
*剩餘風險 (residual risk)
**實施因應對策後還需要承擔的風險
!!風險的相關名詞
*Asset 資產
**公司組織必須要加以保護的資源、程序、電腦架構
*Risk 風險
**遭受損失的可能性
*Threat 威脅
**任何對電腦、網路或資料具備潛在危險性的現象或事件,可能來自人為或資然發生
*Vulnerability 漏洞、弱點
**系統上的缺陷或遺漏,其可為有心人士利用 (exploit) 作為攻擊的缺失
*Exposure 暴露
*threats × vulnerability × asset value = total risk
*因應措施 (Safeguard; countermeasure)
**針對特定安全性威脅所採用的對策,以便有效的降低風險
**沒有任何因應措施能將風險降至 0
!!弱點、威脅、資產與因應措施的關係
|威脅 →|因<br>應<br>措<br>施|減<br>少<br>威<br>脅|資產<br>(Assets)|
|威脅 →|弱<br>點|遭<br>受<br>威<br>脅|~|
|威脅 →|因<br>應<br>措<br>施|減<br>少<br>威<br>脅|~|
!!風險管理 (Risk Management)
*是一種辨識、控制與降低安全性風險的機制與過程
|>|>|>|>|>|>| 風險管理 |
|>|>| 風險評估與分析 |>|>|>| 風險對策與控制 |
|資產評價|弱點威脅|衝擊損失| 規避<br>(avoid) | 降低<br>(reduction) | 移轉<br>(transfer) | 接受<br>(accept) |
!!風險分析 (Risy analysis) 重要工作
*Identify risks
*Quantify the impact of potential threat
*Privide an economic balance between the impact of the risk and the cost of the safeguards
*風險分析也提供了一個為了避免潛在威脅所必要的防護措施成本與評估可能損失成本的比較資訊 (cost / benefit comparison)
!!風險分析與管理的步驟
*資產確認與評估 (assed identification and valuation)
*風險確認與分析 (risk assessment and analysis)
*選擇對策 (countermeasures selection)
*監控 (monitoring)
!!Quantitative (定量) v Qualitative (定性) Risk Analysis
*兩種風險管理的方法
#Quantitative 定量
**分析過程儘量將各種評估資訊予以量化及數值化
**一個純粹屬量的風險分析是相當困難的,甚至不可能,例如商譽
#Qualitative 定性
**分析過程中並不會將各種評估資訊予以數值化,牽涉到判斷、直覺、和經驗的使用,或用排序,區分等級的方式來取代數據
!!風險分析 (risk analysis) 公式
*Asset Values
*Exposure Factor (EF) 暴露因子
**當一項威脅發生時,所造成特定資產的損失百分比值
*Single Loss Expectancy (SLE)
**來自單一威脅所造成的特定資產損失金額
**Asset Value($) × Exposure Factor (EF) = SLE
*Annualized Rate of Occurrence (ARO)
**估計特定威脅每年發生的頻率
***例如洪水每十年發生一次,ARO=0.1
***一年發生 50 次,ARO=50
*Annualized Loss Expectancy (ALE)
**每年來自特定威脅所估計的特定資產損失金額
**SLE × ARO = ALE
**經營者最在乎的就是 ALE
!!定性風險分析方法介紹 —— Delphi 技術
*整合一群專家 (部門主管) 意見的分析方法,在不聚集專家到同一地點的情況下獲得一致性的意見
*設計一份問卷給予部門主管詢問各項威脅後造成的風險分析,將各題目的最少人選的選項剃除,而做成第二份問卷,再拿給同一群人填寫,回收問卷後仿照前面的方式逐步剃除最少人選的選項,而選出最後大家共同的答案
!!風險控制對策
*降低風險 ( Risk Reduction)
**降低風險發生的機率
***例如架設防火牆,可以減少被攻擊的可能性,但攻擊一旦發生,造成的損失還是一樣多
**降低風險發生的衝擊損失
*移轉風險
**資訊安全很少在做移轉風險
**保險:難以做成資訊安全的保險,因為風險難以評估
**Proxy Outsource:主機代管,也是一個處理移轉風險的方式
***主機代管:將伺服器主機委託給 ISP 業者,透過簽約的方式交由 ISP 負責,一旦發生損失將依據契約賠償
*接受風險
**主事者知道這個風險的存在,但並未針對這個風險採用任何因應措施
**當風險產生的衝擊並不顯著,且因應措施皆會花費大筆的支出時,則可能考慮接受風險
*規避風險
**將風險發生的機率降到 0
**將風險發生產生的衝擊降到 0
**資訊安全的規避風險只有一招:不要提供服務
!!監控 (Monitoring)
*風險管理團隊需定期檢視對策是否生效
**採用的對策是否有效的降低或消除風險
**採用的對策是否帶來新的威脅或弱點
**採用的對策是否需要修正
***例如是否導入 UTM (整合式威脅管理)
!安全政策 (Security Policies)
*是一份用來描述公司如何保護他們資產的文件
*定義企業組織如何實施安全性的一群規則、原則與實務 (少有實務,但並不是不能有)
*上層主管需要決定公司內部資訊安全應該扮演什麼樣的角色、與每個人在資安環境中的職責
*為組織安全性管理的指導方針
*安全政策具備 Living document 特性:沒有寫完的一天,因駭客攻擊的技術日新月異,因此會需要經常的調整
!!安全政策一般內涵
*政策目的
*適用範圍
*法令及標準依據
*違反處置原則
*修定原則
**什麼人、什麼情況下有權利修訂安全政策
**通常由高階主管開會決定
!!政策類型 (Policy Type)
*Regulatory (管制、規定的)
**整個組織都一定需要遵循
**非常詳細
**通常都有罰則
*Advisory (建議性)
**希望公司大部份員工遵循
*Informative (訊息性)
**提供安全性資訊給員工,使員工在使用電腦下該有的警覺性
**例如教導員工何謂釣魚、如何避免,然後公告在公告欄
!!制定功能性安全性政策
@@font-size:2.0em;http://www.sans.org@@ 提供全世界最好的安全性政策的範例 (Resource / Policies)
!!落實安全性政策
*標準 (Standards)
**強制定義各種軟硬體的使用規格與行為
**範例:公司高層制定出『維護高階主管行動裝置使用的安全性政策』,資安部門必須針對該項定義各項軟硬體使用規格與行為,第一步:防止主管筆電、PDA 遭竊,例如採購主管的筆電必須具備行動鎖、行動警示器等
*程序步驟 (Procedures)
**提供符合標準與指引的詳細步驟
**SOP, Standard of Procedure
*指引 (Guidelines)
**建議的實施方式,粗略的建議方案,方便員工遵循的指引
*基準 (Baseline)
**基準是系統、網路或設備所必須堅持的最低安全性水平
**例如需要通過 CC EAL 4 (1 ~ 7)、ICSAlabs 的資安設備、防毒 VB 100、NSS
***@@font-size:2.0em;http://www.icsalabs.com@@
***個人防火牆,神推:居易
!!政策關係
*安全性政策屬於策略性 (Strategic) 文件,必須考慮未來三五年制定
*標準、指引、程序步驟為戰術性 (Tactical) 文件,撰寫時需要有彈性,因地制宜
!安全性認知、教育訓練 (Security Awareness、Education & Training)
*人員通常是資訊安全上最弱的一環
*所有員工均需要安全性的觀念與認知
*必須由上至下 (top-down) 的教育與受訓 (training and education)
*經由安全認知的訓練,使用者可以了解他們在安全性環境的角色及責任
*安全性認知 (Awareness)、訓練 (Training) 與教育 (Educatin) 被視為加強員工了解學習安全性知識和技能的大三元
**安全性認知著重於人員對安全性有著持續的警覺和關注
**安全性訓練讓人員擁有必要的安全性技術與能力,通常是短期且針對特定的個別技術
**安全性教育通常會整合安全技能和其他必要的產業觀念與原則,通常會是較廣泛且長期的目標
!!對員工實施安全性認知的優點
*可以有效的降低非法的存取企圖
*提升安全控制與管理的效率
*幫助人員避免資訊的欺騙與濫用
*Authorization = Access Control
*定義:限制資源存取的處理方式及程序,以保護資源不會被非經授權者存取或授權者作不當的存取
!重要專有名詞
*主體 (Subject)
**一個可以請求存取物件的主動單位 (active entity),可能是使用者、群組或處理程序
*物件 (Object)
**一個包含訊息而可以被存取的對象,可能是電腦、資料庫、檔案目錄等資源
**電腦可能是主體,也可能是物件
*存取模式 (Access Model)
**表示主體與物件間的互動關係及架構
!存取控制類型
*Deterrent Control 嚇阻控制
**透過嚇阻降低蓄意攻擊的可能性
**例如 banner <<toBalaFlashPlayer "flash/02-01-microsoft_windows_banners.swf" "有影片有真相" "800" "620">> <<toBalaFlashPlayer "flash/02-01-microsoft_windows_banners-2.swf" "有影片有真相" "800" "620">>
*Preventive Control 避免控制
**避免可能傷害事件的發生
*Detective Control 偵測發生
**發現或偵測可能傷害事件的發生
*Corrective Control 修正控制
**更正還原已發生的傷害事件
!!範例
*防毒軟體:Preventive、Detective、Corrective
*Audit logs:Detective
*加密技術:Preventive
!存取控制方法 (Method; Layer)
*管理控制 (Administrative Controls)
*邏輯或技術控制 (Technical Controls)
*實體控制 (Physical Controls)
**Facility protection:建築物、機房的管制
***電腦機房環境:
****溫度:21 ~ 23 ℃
*****若僅有機器,可以往下調,至少 0 ℃ 以上
****溼度:45 ~ 60%
*****太乾會產生靜電、太溼會容易生銹
*****若有人過敏可能要在 40%
****落塵:若機房有磁性裝置,需要額外的清靜機搭配防塵櫃,例如磁帶機非常怕落塵
***預防網路斷線:提供備援線路 (Backup line)
****備援線路必須要是不同的類型
*****以 ISP 為例:主要線路為海底電纜,備援線路為衛星訊號
*****RAS 中的 Demand Dial 也是一種備援線路的解決方案
**警衛、上鎖、監控、環境控制
*上述三種控制缺一不可
!存取控制模式
*定義主體如何存取物件的架構
*DAC, Discretionary Access Control 自訂式存取控制
**__資源擁有者__決定是否允許他人存取自己擁有的物件及所允許的存取權限
**大部份流通的作業系統均採用 DAC 的存取方式,例如 Windows、Linux
*MAC, Mandatory Access Control 強制性存取控制
**以使用者與該資訊或物件之間的安全等級作為存取依據,並不能因個人因素修改
**給予每個物件和每個主體一個敏感標籤 (sensitivity label; security label),然後依據標示的保密等級和種類來區分物件能否被存取
**通常只有管理員 (非擁有者) 可以變更資源的安全性分類
**常被視為較 DAC 安全的做法
**效能較差
**SELinux 就是 Rule-based access control 的例子
!!實作 MAC 的方法
*規則為基礎的存取控制 (Rule-based access control)
**依據管理員預先定義好的規則來決定使用者是否允許存取,使用者無法變更規則
*Lattice-based access control
**學理上存在,實務上不存在
##將主體與物件排列成一組元素對
##每個元素對均設計有存取上限及下限 (defines greatest lower-bound and least upper-bound)
##主體對物件的存取擁有最低上限和最高下限的使用權利
!!MAC vs DAC
*DAC
**擁有者決定如何保護及共用資源
**美國軍方橘皮書列為 C-level
*MAC
**系統決定資料如何共用
**通常較 DAC 安全許多
**美國軍方橘皮書列為 B-level
**常使用在系統安全格外重要的地方,例如軍方
!!Role-based access control (RBAC) 角色為基礎的存取控制
*一種 nondiscretionary access control 的控制方法
*是否允許存取資源是依據使用者在公司內擁有的角色、職稱來決定
*大部份作業系統透過群組的概念來實作 RBAC 的控制方法
*降低管理負荷,適用於員工變換頻繁的環境
!!DAC 實作透過 Access Control List
*微軟作業系統中資料夾、檔案安全性頁籤 進階選項就是 ACL (微軟稱為 DACL, Discretionary access control list, 判別控制清單)
**其中每一個項目都是一個 ACE (Access Control Entry),針對每一個主體設定允許或是拒絕做特定的操作
*Linux 要使用 ACL,需在 /etc/fstab 第四個欄位加上 acl,透過 setacl / getacl 設定
**chmod、chown 不是 ACL 的 Implement
!最低權限賦予原則 (Rule of Least Privilege)
*設定存取權限的最高指導原則
*定義:任何主體在指定權限的時候,僅僅賦予他在完成公司指派的工作所需要的最低權限而已,絕對不能多,多了就違反原則
*權限等級要符合 Least Privilege、權限對象也要符合 Least Privilege
**微軟 Windows server 2003 共用資料夾的共用權限預設的 Everyone - Read 違反原則,因為 Everyone 超出權限對象
!身份驗證 (Identification, Authentication)
*存取控制需要搭配有效的身份驗證
*身份驗證乃是立論於下面三個因子 (factors)
**Type 1:你知道的某些東西 (Something you know),例如 PIN (Personal Informatin Number)、密碼、口令 (Passphrase)
***最常使用到的因子
**Type 2:你擁有的某樣東西 (Something you have),例如 ATM 或 Smart card
**Type 3:(生理上)你是什麼咚咚 (Something you are),生物特徵驗證工程 (Biometrics),例如指紋辨識
*Two-Factor Authentication:採用嚴謹的驗證 (strong authentication),使用兩種或兩種以上的驗證方式
**例如 ATM 提款,需要 Type 2 的提款卡加上 Type 1 的密碼
**早期採用的磁性提款卡,極容易被複製,因此不算是嚴謹的驗證方式
!!Token-based Authentication
*凡是需要使用者持有並作為身份驗證的產品稱為 token
*常用來支援較安全的一次密碼 (one time password)與雙因子驗證
*主要類型:
**時間同步裝置 (Time synchronous)
***認証伺服器與 Token 卡需維持相同時間
**事件同步裝置 (Event synchronous)
**非同步裝置 (asynchronous)
***認証伺服器會先傳送一個隨機動態的數字給使用者 (Challenge),使用者必須將這個數字輸入 Token 裝置中,並寄算出相對應的動態密碼 (Response) 傳回予認証伺服器
!!密碼驗證 Password Authentication
*密碼為資訊系統下最普遍的身份驗證因子
*密碼驗證安全依據 (Password authentication security dependent on)
##Password type 密碼類型
***靜態密碼 (Static password)
****每次登入都使用相同的密
***動態密碼 (Dynamic password); 一次密碼 (one time password, OTP)
****每次登入均使用不同的密碼
***採用密碼原則,定期更改密碼取得動、靜態密碼的協調
##Authentication protocol 驗證協定
##Password store & Maintain methods 密碼儲存與維護
!密碼破解法 (Password Attacks)
*暴力破解法 (Brute force); 窮舉法
**藉由合法字元的組合不斷的嘗試直到猜測正確為止
**理論上所有密碼都能透過暴力破解法破解,只是時間長短而已
***IOphtcrack
*字典查詢法 (Dictionary)
**利用一些常用的密碼和收集較可能的字彙,再藉由不斷地改變組合一直到破解密碼為止
**若字典檔收集得宜,將可較快速破解
***Crack
***John the Ripper
*混合攻擊 (Hybrid Attack)
**混合上述兩種
*Trojan horse login program
**Keyboard Logger 鍵盤側錄
!密碼控制與管理
*為了避免容易猜測或破解的密碼
**密碼檢查器
**密碼產生器
**密碼原則:密碼使用期限、密碼長度限制、記憶密碼歷程
**限制密碼企圖登入失敗次數 (Lockout policy):僅限於 Intranet 帳號
!Biometrics 生物特徵驗證工程
*可避免 Type 1 容易忘記、Type 2 可能丟掉的危險
!評估生物特徵驗證工程
!!辨識能力準確度指標
*錯誤拒絕率 (False Rejection Rate, FRR)
**拒絕正確使用者進入的發生機率
*錯誤接受率 (False Acceptance Rabe, FAR)
**讓不該接受進入的人進入的錯誤機率
*以上兩項在相同的技術下,理論上不能兩種一起降低
*同等錯誤率 (Equal Error Rate; Crossover Error Rate, CER)
**FRR 與 FAR 的交匯點就是 CER
**CER 越低表示準確度指標越高
*常見的生物特徵驗證準確度比較
|生物特徵法|回應時間|精確指標<br>CER|
|虹膜<br>Iris scan|2.5 ~ 4|0.5%|
|視網膜<br>Retinal scan|4 ~ 7|1.5%|
|指紋<br>Fingerprint|5 ~ 7|2%|
|手掌掃描<br>Hand geometry|3 ~ 5|4%|
|語音辨識<br>Voice pattern|10 ~ 14|10%|
!生物特徵工程其他量因素
*註冊時間
**個人提供可供日後評估的生物特徵樣本給系統所花費的時間
**一般可接受的註冊時間為兩分鐘
*註冊產出率 (Throughput rate)
**每分鐘比對的數量
**一般可以接受的比對產出率為每分鐘 10 個主體
!單一簽入 (Single Sign-On, SSO)
*使用者只需要驗證身份一次,就可以存取各種足夠存取權限的資源,直到登出為止
**不管公司多大,一個員工只要維護一個帳戶
**只要登入一次,在登出之前,都可以存取各種足夠權限的資源
*使用者只需要記憶一個密碼,可以使用較嚴謹的密碼,存取資源較快
*達到 SSO 的兩個條件
**需要一個能維護所有員工的帳號密碼的資料庫
***LDAP Database
**需要一個能驗證身份的協定
***Kerberos
!Kerberos
*源自於 MIT 的 Athena 計劃
*命名源自於希臘神話中三個頭的地獄看門狗,意義用以看守『驗證 (Authentication)』、『Accounting』、『Audig』
*可用於 Single sign-on system
*對稱性加密技術 (Symmetric key cryptography)
**使用相同的金鑰加密與解密
*最新版本 Kerberos V
!!領域 (realm)
*一個完整個 Kerberos 系統服務,必須包含以下部份
**Kerberos 伺服器:
***例如 DC
**應用程式伺服器:需要驗證的伺服服務、網路服務必須支援
***例如 MS Exchange、SQL、IAS、RIS (WDS)
**用戶端支援
***Windows 需 2000 以上版本
*Kerberos 環境必須符合:
**收集所有使用者的 ID 和 Hash 過的密碼註冊並儲存在 Kerberos 伺服器資料庫
**Kerberos 伺服器必須和每一部應用伺服器共享一把金鑰,所有的應用程式伺服器都必須向 Kerberos 伺服器註冊
*以上的環境就稱做 Realm
!!KDC
*Key Distribution Center
**提供驗證服務
**保管所有使用者和服務的加密金鑰
**提供金鑰發行功能
**KDC 區分為驗證服務 (Authentication Server, AS) 與票證發放服務 (Ticket Granting Server, TGS)
#使用者登入,驗證成功
#KDC 提供使用者 TGT (Ticket Grant Ticket)
**只是一張入場券,代表在網路環境下的身份
#使用者要存取網路資源,將收到的 TGT 送給 KDC,KDC 驗證其是否為正確的 TGT
#KDC 驗證收到的 TGT 經判對為正確的後,發給使用者對於要存取資源的 ST (Service Ticket)
#使用者將收到的 ST,提供給應用程式伺服器 (或其他網路資源)
#應用程式 (網路資源) 比對 ST 與其 ACL 是否有足夠權限
#爾後使用者若需要存取相同支援,只需要提供效期內 (微軟 AD 環境下預設 600 分鐘) 的 ST 給網路資源
#若使用者要存取其他網路資源,則從步驟 3 開始重新要求另外一份 ST
!!Kerberos 的問題
*所有應用程式都必須支援 Kerberos
*所有機器都需要時間同步
*Kerberos 使用 UDP 88,通常會被防火牆阻擋
*如果票證時間過長,可能會被做 Relay 的攻擊
**目前票證都有一定使用期限
***微軟服務票證 (ST) 600 分鐘
***使用者票證 (TGT) 10 小時
!SESAME
*歐洲推廣的驗證服務
*支援非對稱性加密,支援憑證,容易管理,稽核較詳細,支援代理驗證
**Kerberos 也支援代理驗證,NTLM 不支援
!!TEMPEST (__T__ransient __E__letro__m__agnetic __P__ulse __E__manation __S__tan__t__ard
*抑制電磁外洩標準
*所有電子設備都會有電磁外洩的可能性,有心人士透過特殊裝置有機會取得這些電磁紀錄
*在資訊安全非常重要的場合使用,例如軍方
!!Banners
*Banner 不該包含系統資訊,例如 OS、版本、硬體等訊息
**Linux banner:
***/etc/issue、/etc/issue.net (網路登入)
***/etc/motd 登入後
!入侵偵測系統 Intrusion Detection System
*一個安全性機制,通常利用監控網路流量、不斷檢測稽核日誌檔,來偵測網路上任何違反安全性的行為
*主要目的在即時偵測、警示回報、反應處理
*入侵偵測與防火牆是網路安全的兩大重要因素
*將 IDS 與 Firewall 結合後,第一時間更改防火牆設定,阻擋攻擊與入侵事件
*分為兩種:
**主機型 IDS
**網路型 IDS
!!主機型 IDS (Host-based IDS)
*通常裝在一部需要保護的特定機器 (提供重要服務的伺服器),監控此系統的行為,藉由蒐集日誌檔 (從各項日誌檔),以找出不當的安全事件或攻擊事件
*主要缺點:
**受限於只有紀錄項目吻合事先定義的規則或特徵才會發出警告,沒人能保證某個攻擊一定會被紀錄下來
**現今許多攻擊並未針對某台特定主機攻擊
!!網路型 IDS (Network-based IDS)
*持續監控網路流量,並搜尋已知的攻擊型態
*NIDS 會檢查資料串流中的每個封包,以辨識各種攻擊
**NIDS 本身就是 sniffer
*缺點
**資料流量大時容易產生失誤或誤判
**若針對應用層的安全漏洞進行攻擊的駭客手法 (例如 IIS),就無法發揮偵測與反制的效果
**交換器網路環境下,需安插在 wan port
!!混合 IDS
!!入侵偵測的方法 1:特徵偵測
*做法
**收集各種已知的入侵模式或特徵,並建立資料庫 (Attack signature database)
**比對資料庫的樣式 (Pattern) 或規則 (Rules),以判斷是否有入侵行為
*優點
**能夠辨識已知的入侵威脅
**明確識別威脅
*缺點
**無法辨識未知的攻擊
**需經常更新特徵資料庫
**特徵或規則的增加可能影響效能
!!入侵偵測的方法 2:統計異常偵測
*做法
**建立使用者與系統正常使用的標準值,與容許區間!重要專有名詞
**比對標準值,以判斷是否有入侵行為
{{{
例如某台伺服器網路使用行為標準值 50%,容許空間 10%,則超過 60% 會提出警告
}}}
*優點:
**可偵測到突然升高的網路流量
*缺點:
**無法偵測未超過行為標準的惡意活動
**標準值與容許區間很難定義
!!IDS 可能的錯誤 False Positive & False Negative
*Fasle Positive 誤報
**誤將正常的行為判定為攻擊
*False Negative (安全假象) 漏報
**誤將攻擊行為當成正常行為
!!IDS 的問題與限制
*通常只能偵測出已知的攻擊模式
*隱匿攻擊
*誤判率過高
**容易造成『狼來了』效果
*缺乏立即有效的回應 (無法在偵測的同時採取措施)
!其他 IDS 延伸技術
*蜜罐 (Honeypots) 或誘捕型入侵偵測系統 (Deception Systems)
**是一個故意設計成有缺陷的系統,通常用來對入侵者的行為進行警報或誘騙
**主要設置在最前端
**擁有一個資料收集引擎,專門收集攻擊事件的特徵,再將這些特徵送至後方 IDS
**目的在於解決 IDS 未知的攻擊與隱匿攻擊
!!入侵防禦系統 (IPS, Intrusion Prevention System)
*希望能在零誤判率的情況下偵測出攻擊行為,並立即加以阻擋
**零誤判難以達到,但重點在於立即加以阻擋
*IPS 最簡單的解釋
**IPS = IDS + Firewall
**IPS = IDS + ARS (自動回應系統,Automatic Response Systems) or IDS + IRS (Intellengance)
*分析攻擊行為為基礎 (Behavior-based)
**所有網路流量經過設備 (In-line)
**封鎖有問題的風包與連線
!!IPS vs IDS
*主要差異在 1. 運作模式;2. 回應機制
**IPS 運作上大多採 IN-Line 模式運作
***佈署在網路重要節點,流量必經之處
**IPS 強化主要防護能力
***IPS 偵測到攻擊後,立即將封包攔阻或修改防火牆規則
*IPS 問題
**可能攔掉企業有用的正常流量
**影響網路效能
***效果非常顯著
!!檔案完整性分析
*主要在偵測與發現檔案或物件是否被竄改過
*無法當成主要
!!Snort 免錢的 IDS
*Apache、PHP、MySQL 必備
*提供 HIDS、NIDS、Honeypots 功能
!ISA 2006 系統需求
*記憶體:512 MB
*CPU:733 MHz P3 (標準最多支援四顆 CPU)
*硬碟空間:至少 150MB 的 NTFS 磁碟空間 (不包括用於快取的硬碟空間)
*網路卡:兩片以上,若需要 DMZ 區則要三片以上
*作業系統:
**Windows Server^^TM^^ 2003 SP1
***安裝 ISA Server 2006 SP1 則需 Windows Server 2003 SP2
**Microsoft Windows
!ISA Server 其他安裝注意事項
*機器不能有任何 NAT 功能 (RRAS、ICS)
*機器上不能啟動任何防火牆或封包過濾功能
*機器上不能啟動任何 VPN 功能 (RRAS VPN)
*機器上『最好』不要啟動其他的網路服務 (如 DHCP、DNS、WINS、......)
!ISA Server 2006 安裝
*只有兩個元件可以提供安裝選擇
**主程式只能裝在 Server 級作業系統
**管理工具可以裝在 XP、Vista 等用戶端作業系統
*ISA 安裝過程最重要的設定:內部網路
**一台都不能多:多了就有可能網路不通
**一台都不能少:少了就有安全上的疑慮
*防火牆用戶端連線
**勾選『允許未加密的防火牆用戶端連線』,代表網路環境內有老舊的 ISA 用戶端軟體,且不願意更新
***通常都是建議更新用戶端軟體
!!ISA Server 2006 安裝後
*ping gateway 可以通
*瀏覽任何網頁得到 403 代碼
!!ISA Server 2006 SP1 安裝
!!ISA 2004 升級到 2006
*升級最大的好處
**儘量保留舊版的組態與規則
*2004 先移除『防火牆用戶端安裝共用』與『訊息過濾程式』
*有兩種升級方式
**直接安裝升級 ISA server 2006
**將組態、規則匯出遷移
***通常較建議做遷移
!安裝後檢查
*預設安裝目錄
**C:\Program Files\Microsoft ISA Server
*安裝的網路服務
**Microsoft Firewall (最主要)
**Microsoft ISA Server Control (最主要)
**Microsoft ISA Job Scheduler
**Microsoft ISA Server Storage
**MSSQL$MSFW
**MSSQLServerADHelper (未啟動)
!認識 ISA Server 2006 預設組態
*預設只有 Administrators 可以管理 ISA 伺服器
*ISA 伺服器本身與其他網路透過路由
*VPN 用戶端與其他網路是透過路由
*__內部網路與網際網路透過 NAT__
*預設安裝時建立了 30 條系統原則規則 (隱藏)
**只有一條『拒絕所有流量的存取規則』的系統原則規則看的到
***不能刪除、不能修改、只能放最後面
**其他隱藏的系統規則原則
***可以修改、不能更改順序、不能刪除
*VPN 預設關閉
!Windows 驗證協定
*LAN 驗證協定
**LANMan
**NTLM v1
**NTLM v2
**Kerberos:Windows 2000、Windows XP
*IIS
**Basic
**Digest
**Integration
*Remote Access
**MS CHAP v2
**MS CHAP v1
**CHAP
**PAP
**SPAP
**EAP + TLS
!LAN 身份驗證比較
| 身份驗證協定 | 支援的平台 |說明|
|LANMAN|所有的 Windows|最大有效長度 14 字元,實際區分為兩個七字元長度加密,且密碼全部轉換為大寫|
|NTLM|NT4、Windows 2003、2000、XP|較 LANMan 安全 <br>仍易受 Session Hijack、Replay、Man-in-the-middle 的攻擊|
|NTLM v2|Win 2k3、2k、XP、NT4+SP4、Win9x+DS clients|比 v1 更安全,加強連線安全,提供安全通道的建立並具備雙向驗證 (Mutual Authentication)|
|Kerberos|Windows 2003、2000、XP AD 環境|安全性最高|
!Kerberos 的優點與限制
*提供較快速有效率的驗證 (fast authentication)
*雙向驗證
*委託驗證
*互通性 (Interoperbility)
**Internet 標準,可與其他作業系統做驗證
*微軟 Kerberos 限制:
##需 AD 環境
##需 Windows 2000 以上作業系統
!身份驗證的安全性疑慮
*微軟 AD 環境下的密碼存放在 AD 資料庫,透過 MD4 + Syskey + locked 保護 AD 資料庫
*Windows 9x / ME 內定上並非強制身份驗證
*LANMan 與 NTLM v1 兩種老舊驗證協定及加密安全性差,易受破解
*老舊的 LM Hash 的密碼儲存方式容易被破解
!提升網路驗證及密碼使用的安全性指引
*確保所有驗證伺服器的實體安全性
**將 DC 鎖在機房,避免一般使用者接觸
*避免儲存 LM Hash 密碼 <<toBalaFlashPlayer "flash/02.1-01-disable_storage_lan_manager_hash.swf" "有影片有真相" "800" "620">>
**電腦設定 → Windows 設定 → 安全性設定 → 本機原則 → 安全性選項 → 網路安全性:下次密碼變更時不儲存 LAN Manager雜湊值
**AD 環境下,在 @@color:red;Default Domain Controller Policy@@ 中設定
*強制所有機器使用 NTLM v2 或 Kerberos 驗證協定 <<toBalaFlashPlayer "flash/02.1-02-force_all_machines_using_NTLM_v2.swf" "有影片有真相" "800" "620">>
**Win 9x / ME 需安裝 AD client extension,否則不能登入
*關閉 Cached Credentials <<toBalaFlashPlayer "flash/02.1-03-disable_cache_credential.swf" "有影片有真相" "800" "620">>
**Cache Credentials 預設儲存前十次使用者登入的機密資料,有心人士只要有 Localsystem 或管理員權限就能存取此資料,造成密碼外洩
***@@color:white;font-size:1.4em;lsadump@@
***@@color:white;font-size:1.4em;john the ripper - Linux 破解密碼@@
**在某台特定機器會有多人使用的情況下關閉,個人使用的機器建議啟用,因可以防止與 DC 斷線的情況下仍然可以登入正常運作
!Linux 密碼原則
*/etc/login.defs 簡易版
*PAM 強大密碼原則
!ISA Server 用戶端類型
*網頁代理用戶端 (Web Proxy Client)
**用戶端只能存取 HTTP、HTTPS、FTP 資源
**將用戶端的請求送至 ISA Server 的 8080 連接埠
***squid-based 的 Proxy 連接埠為 3128
*Secure NAT 用戶端
**用戶端可以存取大部分的 TCP、UDP 服務
***複雜的 TCP、UDP 無法存取:會使用兩個連接埠以上的應用程式
****例如串流媒體、FTP
**用戶端預設閘道設至 ISA Server 即可
*防火牆用戶端 (Firewall Client)
**利用防火牆用戶端軟體將網頁請求送至 ISA Server 的 8080 埠,非 Web-based 的服務請求則送到 1745
!!用戶端類型比較
||網頁代理用戶端|安全 NAT 用戶端|防火牆用戶端|
|設定方式|設定瀏覽器的代理伺服器|設定預設閘道|安裝防火牆用戶端軟體|
|支援的平台|全|全|只支援微軟作業系統|
|驗證|支援使用者驗證 (被動的支援)|僅 VPN 使用者能驗證,或是採用 host IP 限制存取|支援使用者驗證 (主動的支援)|
*三種用戶端皆會提升網頁下載速度 (開啟快取功能)
*網頁代理用戶端與防火牆用戶端支援用戶端 DNS 代理
*網頁代理、安全 NAT 支援非微軟平台、不需裝額外軟體
*防火牆用戶端支援用戶端 DNS 代理,需安裝防火牆用戶端,只支援微軟平台,只支援 Winsock,不支援 TCP/UDP 以外的協定
*安全NAT 支援 TCP/UDP 以外的協定
!!選擇適當的用戶端
*不想安裝額外軟體與設定:SecureNAT
*只想提升網頁存取速度:網頁代理用戶端
*發佈的伺服器:SecureNAT 用戶端
*提升網頁存取速度並存取 Winsock 資源:防火牆用戶端
*控制用戶端應用軟體與網際網路的溝通:防火牆用戶端
*非微軟平台存取網際網路:網頁代理用戶端或 SecureNAT用戶端
*限制只經過身份驗證者才可以存取網頁:網頁代理用戶端或防火牆
!ISA 2006 用戶端設置
!!網頁代理用戶端
*設定瀏覽器代理伺服器:位址指到 ISA 的『內部』IP,Port:8080
**不需要設定 DNS
!!設定安全 NAT 用戶端
*設定預設閘道至 ISA Server
*需指定 DNS Server 位址
!!設定防火牆用戶端
*安裝 client 軟體
**若需要安裝在 Vista 作業系統,則需至微軟網站下載新版用戶端軟體
**安裝完會自動監控網路,並設定 IE 瀏覽器代理伺服器
*注意:安裝 client 軟體時,輸入的 ISA 主機位址必需要能正向解析與『反向解析』
**最好的做法是編寫 hosts 檔
**只輸入 IP 無法反解析為 hostname 也不行
**輸入 hostname,ISA 預設不接受廣播流量,因此也得不到解析出來的 IP
!!新增第一條防火牆規則
!!代理 SSL 連線
*需要有一個有效的機器憑證
!!User level 身份驗證
*Secure NAT 不支援 user level 驗證
*驗證類型類似 IIS
**摘要
**整合
***預設
***僅 IE 支援
**基本
**SSL 憑證
**Radius
***只有 RADIUS 必須單獨驗證,不能與其他驗證方式混用
****新增 RADIUS 伺服器時,等候逾時拉長,要使用使用者憑證驗證勾選最下面
!IAS 伺服器自動探索 - W(S)PAD
*網際網路標準
*若使用者有經常在不同的網路環境漫遊,需要連接到不同的 ISA 伺服器的需求
*運作方式
**啟動 ISA 自動探索回應功能
***網際網路標準使用 80 Port,因此 ISA 不能有 (也不應該有) web server
##DHCP (適合用動態指定 IP 位址的網路環境):
###在伺服器右鍵選單:預先定義的選項
###資料類型 string
###代碼:252
###字串:輸入 ISA 伺服器『內部』IP的 URL 或是能被解析的 FQDN、hostname
###在領域選項中新增剛剛定義的選項
##DNS(適合用靜態指定 IP 位址的網路環境):
###新增一筆 CNAME:wpad 指定到 ISA 的 FQDN
###that's all
!網路基礎
*略
!網路設備與安全性
*網路上設備若能處理 sessions 以上,及能佈署在 In-line 模式下的設備都能稱為閘道器
*與網路安全性相關的兩項設備
**交換器
***在現今網路環境安全性與效率上扮演重要角色
**路由器
!!交換器 Switch
*Hard implemented Multi-port bridge
*可避免碰撞,全雙工的 Switch + 網卡,可幾乎完全避免
*大幅降低被竊聽的可能
*L3 Switch 可以用不同的 VLAN 切割廣播領域 (透過 IP 切割)
**L2 藉由 Port 切割
*要購買 Switch ,要求安全性時需要支援 802.1X
!!路由器
*透過過濾廣播流量,並依據網路層位址來過濾封包
*Screening Router:開啟封包過濾功能的路由器
!!L4 Switch 第四層交換器
*負載平衡
*進階封包過濾
!無線網路
*無線電波
**無線電話網路:GSM、GPRS、WAP、3.5G
***WWAN 無線廣域網路
**藍芽:WPAN 無線個人網路
**HomeRF
**802.11:802.11b/g、802.11n
***WLAN 無線區域網路
**802.16 WiMax
***WMAN 無線都會網路
!!特性
*優點
**機動性高
**不需佈線
*限制
**低頻寬
**干擾
**傳輸距離
**傳輸可靠性差
**安全性不足
***透過最新的技術可彌補其不足
!企業佈署無線區域網路
*IEEE 802.11 (並未商品化)
**使用無線電波 2.4GHz 的 ISM 頻段 (Industrial Scientific Medical Bands, 工程科學醫療頻道)
**傳輸速率 1 ~ 2 Mbps
**有效傳輸距離 < 100 公尺
**採用 CSMA/CA 取代乙太網路的 CSMA/CD
*IEEE 802.11b
**與 IEEE 802.11 完全相容
**可達 11 Mbps
*IEEE 802.11a
**改採 5GHz
**可達 54Mbps
**干擾性較 2.4Ghz 小,但在部份國家並非開放可直接使用
**距離較短
*IEEE 802.11g
**2.4GHz
**54Mbps
**與 802.11b 可共用
**範圍較 11b 小
!!WLAN 運作方式
*IEEE 802.11b 定義兩種模式
**Ad-hoc Mode 用戶端對連模式,點對點分享,無安全性
**Infrastructure Mode 透過存取點 (Access Point, AP) 來使用網路資源,提供基本的驗證與加密
***早期 AP 叫作 wireless bridge,用來橋接第一層不同型態的網路
!!802.11 安全機制
*身份驗證 (Authentication)
**開放系統 (Open System):接受空白的 SSID
***開啟 SSID 廣播
**封閉系統 (Close System):需輸入正確的 SSID
***關閉 SSID 廣播
**共享金鑰 (Shared Key):使用 WEP 密鑰作為分享金鑰,64 / 128 bits
***採用挑戰回應,密碼攔截不到
*資料加密 (Confidentiality)
**WEP (Wired Equivalent Privacy)
***RC4 對稱性加密技術
****授權費低廉
****不受美國國防法內的加密演算法輸出限制
**需在 AP 與用戶端輸入相同的密鑰
*完整性 (Integrity)
**CRC32,不是雜湊,沒有雜湊的擴張性
***用 CRC 檢查完整性,本身就不完整
!!WEP 安全性弱點
*IV 問題:長度太短,廠商設計不良導致金鑰容易被破解
**沒有嚴格規定 IV 如何產生
**攔到足夠數目的封包即可破解 WEP
***64bit 25 萬個封包;128bit 50 萬個封包
*缺乏適當的金鑰管理機制
**Distribution of Symmetric Shared Keys
*RC4 加密演算法有弱點
*完整性的保護很弱 (CRC32)
*沒有使用者身份驗證機制
!基本安全性機制
*WEP Key + SSID + MAC 認証
**啟用 WEP 金鑰
**關閉 SSID 廣播,更改預設值
**在 AP 註冊用戶端無線網卡
!802.1x 組成元件 2001
*IEEE 802.1x 標準定義了 port-based 的網路存取控制,可以用來驗證乙太網路的網路存取
*定義了三個元件
**驗證者 (Authenticator),AP
**請求者 (Supplicant)
***用戶端可使用 username/password,但最好還是透過憑證
**驗證伺服器 (Authentication Server),通常為 Radius 伺服器
***Radius 一定需要機器憑證
***Linux:openRADUIS / freeRADIUS (新名字)
***Microsoft:IAS (Internet Authentication Service)
*兩種驗證方式
**EAP + TLS = 憑證驗證
**PEAP + MSCHAP = 密碼驗證
!!802.1x 解決 WEP 的問題
*提供 user-level 的身份驗證
*IV 變為兩倍 - 48 位元
*使用動態金鑰,將 WEP 再封裝一層,動態金鑰的暫時性密碼完整協定 (TKIP, Temporal Key Integrity Protocol)
!WPA (Wi-Fi Protected Access), 2002
*驗證分為企業驗證方法、SOHO 驗證方法
**企業驗證使用 802.1x
**SOHO 驗證使用 Pre-shared Key (PSK)
**金鑰管理 TKIP
**加密演算 RC4
**完整性:Michael (Message Integrity Check, MIC),雜湊演算
*只剩下 RC4 彆腳的演算法
!802.11i, 2004 = WPA2, 2005
*驗證與 WPA 同
*金鑰:CCMP & WRAP 取代 TKIP,較安全,但與 WEP 不相容
*加密演算法:AES,美國最新的對稱演算法
*完整性:Cipher Block Chaining Message Authentication Code (CBC-MAC),Michael 的一種
!WPA2 未問世前的安全性解決方案
*WLAN + VPN
*硬體 VPN 價格不菲
*用戶端需佈署 VPN 用戶端軟體 (通常為 IPsec-based),設定複雜
!端點安全性 End-Point Security
*Windows server 2008:NAP;Cisco:NAC
**Windows server 2003 遠端存取隔離服務
*目前使用第四版 IP (IP v4),藉由 NAT 與 CIDR 出現,暫緩了升級到 IP v6 的迫切境
!TCP/IP 安全上的弱點
*TCP/IP 問世之初根本沒有網路駭客黑客,因此並無安全性考量
*TCP 無法避免
**並沒有避免未經授權的存取
**並沒有避免被竄改
**並沒有避免 message eavesdropping
*TCP 三向交握的弱點
##用戶端送出 1SYN 封包
##伺服端送出 2SYN/ACK 封包
***包含 Windows size 資訊
##用戶端送出 3ACK 封包建立連線完成
*連接埠弱點
**應用層每個服務都有個唯一的連接埠,例如 telnet: tcp/23
**經常被駭客探索網路資源的工具
***透過 port scan
*IP 選項 (options) 弱點
**IP 封包最後一個欄位
**經常被用來傳播惡意程式的攻擊指令
*預設上並無提供任何加密機制
**容易被竊聽 Sniffer
**FTP 解決方案
***Linux:sFTP、scp、FTPs
***Microsoft (windows server 2008):FTPS
*預設上並無任何驗證機制
**並沒有連線階段的驗證
**容易被連線攔奪 (Session Hijack)、IP 偽裝 (IP Spooling)、封包重送 (Relay) 的攻擊
*ICMP 弱點
**早期為了故障排除通常會開放
**常被用來做阻絕服務攻擊 (DoS)
*Fragmentation (封包切割) 的弱點
**因不同的第二層協定,規定的 MTU 不同
**駭客經常送出無法重組的封包,造成機器當機
*ARP cache 弱點
**基於效能考量會維護一個快取區
***微軟預設維護 3 分鐘
***有經驗的管理員會將重要伺服器設定靜態 ARP 快取
**ARP Poison,透過 ARP 封包將使用者 arp 快取區的 Gateway MAC 指定到駭客指定的機器,達到在交換器網路下的封包竊聽功能
!Public / Private Address
*假若公司需要連結存取 Internet,可採用
**Public Address Schema
**Private Address Schema
***RFC 1918 定義 IANA 所保留的 IP 位址,建議一般組織內部使用這些 IP
***需透過 NAT 轉換機制
****出 NAT 轉換來源 IP 為 Public IP,來源 Port num 改成 High port,重新寫入 checksum
****進 NAT 轉換目的 IP 為 Private IP,目的 Port num 改成原來的來源 port,重新寫入 checksum
!NAT 實作
*實在不想寫
!!防火牆的 Virtual Server 功能
*又被稱為伺服器發佈 (publish)、DNAT 或 Port Redirect、Port forwarding、static translation
!!NAT 的限制
*所需要的狀態資訊不見得都可以取得並順利轉議
**有些協定除了 Header 外還內嵌 (embed) IP address 在封包資料中,這些內嵌的不見得可以被轉換 (例如 RPC)
**有些應用程式需要預先設定或協調來源/目的連接埠值
**NAT 可能會妨礙某些加密系統 (例如 IPSec)
***要支援 IPSec 要啟用 NAT-T 功能
*LDAP、SNMP、DCOM、RPC、L2TP、IPSec
*NAT 裝置需針對上述協定特別處理才能夠順利轉譯
!Availability
*為避免網路的資訊系統造成單點失誤,所以必須提供備援機制 (Redundancy)
!RAID 磁碟陣列 (Redundancy Array Inexpensive Disk)
*由多顆硬碟組成一個邏輯儲存單位,並將資料儲存在多個磁碟機中不同的地方
*磁碟陣列的概念可以利用軟體或硬體技術實作
*1987 年柏克萊大學三位教授提出磁碟陣列概念:
**提升磁碟使用效率
**提供磁碟使用穩定性
**降低磁碟使用成本
*由上面概念制定出六個等級:Raid-0 到 Raid-5
!!現代 RAID 的特徵
*支援熱抽換磁碟 (hot swappable drive)
*支援熱抽換的冷卻風扇、及電源供應器
*與主機、作業系統獨立
*支援熱抽換與熱備援用硬碟機 (Spare Drive) 並且線上自動資料重建 (Automatic Rebuilding)
*現今市場上常用的 RAID 為 0, 1, 5
**RAID 0:striped volume,以 block 為單位跨磁碟區存取,藉由兩顆以上硬碟平行讀寫,提升磁碟效率
***微軟固定 64K,Linux 由 64 ~ 512K 可自訂
**RAID 1:mirror,同時寫入兩個磁碟機,固定兩顆
***讀取時,理論上效率會較沒有 mirror 的硬碟快,可避開磁碟競爭
***寫入時,理論上效率較慢
**RAID 2:Byte-level Hamming Code (漢明碼) Prity,實作複雜很少使用
**RAID 3:Byte-level XOR Parity,可用 XOR 運算重建資料
**RAID 4:Block-level XOR Parity,同位原資料儲存在特定磁碟機
**RAID 5:Striped Volume with Distributed (Interleave) Parity:同位元資料分布於組成
***承接 RAID 0 讀取效率的優點,但寫入難以估計,會慢但慢多少由實作的運算處理決定
***當其中一顆磁碟毀損,會在備援磁碟機內寫入仍正常運作中的磁碟機做 XOR 運算
***磁碟使用率大於 66.7%
**RAID 10 / 01 / 1+0 / 0+1:RAID 0 + RAID 1,RAID 10:先做 Mirror 再做 Striped;至少要四顆硬碟以上
**RAID 6:6 以上可應付雙點失誤,貴,慢,少人用,除非特殊考量,資料保護是唯一重點才考慮使用
!叢集技術 (Clustering)
*避免伺服器單點失誤,提供高度可用性的伺服器:永不中斷服務
*基本上由兩套相同之電腦設備與作業系統組成,當單一電腦故障,另外一台自動接受
**2-node failover 錯誤移轉
**需具備 Internal Network (Fiber channel)
**Heartbeat
**Load Balance
***窮人的負載平衡技術:透過 DNS 的 Round-Robin
*叢集機器經常負責 Mission Critical Job
!備份
!!制定備份策略 (P.408、P.409)
*定義哪些資料、以什麼方式、每隔多久、什麼時間、備份到哪、如何維護、保護及還原
!!比較備份媒體
||優點|缺點|
|磁帶|容量大<br>成本低|循序存取、速度較差<br>易受環境影響、保存不易<br>容易發生故障難以維護<br>需定期清理磁帶機|
|磁碟|速度快<br>容易使用設定|較高的單位儲存成本<br>攜帶性差|
|光碟|儲存壽命長<br>攜帶方便|容量不足<br>速度較慢<br>大多只能寫入一次<br>燒錄器與光碟片有相容性問題|
*經常使用的備份方式:DDT,Disk-to-Disk,Disk-to-Tape
!!備份類型
*完整備份 (Full Backup)
*增量備份 (Incremental Backup)
**只備份上一次備份後 (不管什麼備份) 有所變更的資料
**清除 archive bit (標示已備份)
***資料有所變動後會自動變成 1,資料備份後會變為 0
***xcopy /m 會將 archive bit 變成 0
*差異備份 (Diffrential Backup)
**只備份上次『完整備份』後有所變更的資料
**不會清除 archive bit
*完整備份通常與增量、差異備份兩種合併使用,以達到較佳的備份與還原備份
**備份窗口夠大、且需要備份的資料量不大的時候能用完整備份是最理想的
*微軟的備份多『複製』、『每日』
**複製:不檢查 archive bit 全部備份,但不更動 archive bit
**每日:只備份今日有變更的檔案
!!備份方法
*線上備份 (On-Line Backup)
**在系統提供服務時,同時進行資料備份作業
**優點:
***不需中斷服務
***不需在營業時間外進行備份
**缺點:
***伺服器效能受到影響
***開啟的檔案可能無法備份
****微軟透過『陰影複製』解決
*離線備份 (Off-Line Backup)
**優點:
***較佳的備份效能
***可完整的備份所有檔案
**缺點:
***服務中斷
!虛擬私人網路 (Virtual Private Network)
*定義:利用公眾網路來連結存取公司內部資源
*企業虛擬私人網路 (Virtual Private Network) 的技術在利用公眾網路 (Public Internet) 的骨幹做私人的資料傳輸
!!VPN 優點
*降低遠端存取與特定點對點之間的通訊成本
**早期 VPN 發展的唯一目的
**用來取代點對點的 Lease-Line (專線)
*企業網路連線架構的彈性與延展性極高
**只需要能夠連上 Internet,不受限於連線方式
**極適合營運據點變動頻繁的產業,例如:超商
*在不同的企業環境中建立『一定程度』的遠端連線安全性
!!VPN 連線類型
*Site to Site VPN
**用來連接多個不同地理區域的
***防火牆對防火牆、Gateway 對 Gateway、網路對網路
*Client to Site VPN
**提供用戶端使用者連接企業內部網路
!!穿隧技術 (Tunneling)
*Protocol encaspulationn and pass through
**把某種協定的封包在封裝一層再與以傳送
*VPN 將封包轉成 Internet 可以處理的 TCP/IP 封包、並且加密
**重新封裝:將不能在 Internet 上傳遞的 DLC、IPX/SPX、AppleTalk、NetBEUI 等協定封裝成 TCP/IP
**加密:IPsec、PPTP、L2TP
***微軟 RRAS 僅支援 PPTP、L2TP
!!PPTP 點對點通道通訊協定 (RC2637)
*被視為微軟特有技術
*將 PPP 封裝到 IP 封包,再加上一層 GRE 表頭
*接下來的次要連線封包使用 TCP 1723
*使用 MPPE 加密 40、56、128 位元,使用 RC4 演算法
*優點
**大部份微軟視窗平台均支援
**方便、簡單
*缺點
**不支援 Token-Based 驗證
**__不支援機器驗證,安全性較差__
***有可能被駭客使用連線攔奪,重撥服務的攻擊
**MPPE 安全性較弱
!!L2TP 通道通訊協定 (RFC 2661)
*純粹的通道技術,本身並無加密
*將 PPTP 與 Cisco 的 Layer 2 Forwarding (L2F) 合併
*__支援非 Internet Based 的 VPN__ (Frame Relay、ATM)
*使用 UDP 1701
**必須使用在穩定可靠的網路環境
**在網路環境不穩定的情況下,封包掉了也不會重送
*經常使用 IPSec 加密
**L2TP/IPSec VPN 與 IPSec VPN 不同
*缺點:
**L2TP over IPSec 在 NAT 環境可能無法使用
**IPSec 設定使用不容易
!!IPSec
*由 Internet Engineering Task Force (IETF) 制定的網路層安全惜定
*IP v4 必須手動啟用,IP v6 預設直接啟動
*IPSec 包括三個子協定
**IKE、ISAKMP (IP Security Association Key Management Protocol):金鑰管理協定,IPSec 連線的兩端成功的建立安全性管道,交換金鑰
***一定要使用
**ESP (Encapsulation Security Protocol):加密、驗證完整性
***只驗證 Payload 的完整性,不驗證 Header
***可以單獨使用,讓 ESP 加密且驗證;或是與 AH 合用,讓 ESP 加密,AH 驗證完整性
****微軟預設單獨使用 ESP,但 Linux 預設合用 ESP 與 AH
**AH (Authentication Header):提供驗證完整性的安全性服務
***驗證機器
***完整性:驗證表頭的完整性,不驗證 Payload,因此安全性較佳
***開啟 AH 後無法通過 NAT
!微軟 IPec
*IPSec 是 IETF 主導發展 IPv6 的一部份
*定義資料加密、完整性、與金鑰管理
*IPSec 在 OSI 的網路層,因此與上層協定、執行的應用程式無關
**是網路層唯一的安全性協定
***SSL 需要瀏覽器支援
***SSH 在傳輸層
!!IPSec 優點
*開放的工業標準
*確保傳輸的私密性、完整性、雙向驗證的安全性能力
*透明性:IPSec 為網路層安全性協定
*彈性能力:可使用在主機間的安全傳輸 (transport mode)、也可用在網段間的安全傳遞 (tunneling mode)
!!IPSec 原則
*Windows 2000 開始支援
*一個或一個以上的安全性設定,每一個安全性設定都必須對於哪一種類型的流量需要使用哪一種安全性驗證方法來傳送
**驗證方法:Kerberos、憑證、PSK
|>|>|>| IPSec 原則 |
| 規則 1 | 規則 2 | 規則 3 | ... | 規則 N |
!!使用 IPSec
*設定方式
**個別機器設定
**群組原則
*設定工具
**IP 安全性原則編輯器
**群組原則編輯器
*選擇或設定適當的 IPSec Policy
**預設 IPSec 原則
***Vista 已拿掉
**自訂 IPSec 原則
!!預設的 IPSec
*用戶端(僅回應)
**不會主動腰由使用 IPSec,但會對 IPSec 的請求予以回應
**只有一條規則
*伺服器(要求安全性):Request
**不管是要連出,或是連入的連線都主動要求 IPSec,但若對方不支援、或是安全性規則與自己不同,則不使用 IPSec
**預設允許 ICMP 不做安全性傳送
**預設有三項規則
*安全性的伺服器(需要安全性):Require
**設定給需要高度安全性防護的伺服器使用
**會主動要求連線的對方使用 IPSec,且不允許不安全的連線
!!!注意:微軟的預設 IPSec 原則給 AD 環境下使用,因驗證方法採用 Kerberos,若獨立伺服器或是工作群組環境下需使用,必須更改原則,改用憑證或是 PSK 驗證
!!考題1:企業內部的 SQL 伺服器要求高階主管查詢資料時需要加密傳輸,而一般員工並不需要存取 SQL 安全性傳輸,則三者各應該指派何種預設原則
*SQL 伺服器:request
*高階主管機器:response
*一般員工機器:do nothing
!!預設 IPSec 原則的限制
*獨立機器或沒有信任關係下不同網域內的機器無法直接使用
*解決方法
**修改預設 IPSec 規則 (不建議)
**建立自訂式原則 <<toBalaFlashPlayer "flash/03.4-customized_IPSec.swf" "有影片有真相" "800" "620">>
***通道結束點
****上:Tranport mode,端點對端點
****下:Tunnel mode
***網路類型:選擇如何連線
***篩選器
****ICMP
****所有流量類型
****自訂新增
*****網路芳鄰傳輸:SMB/CIFS
******Windows 2000/2003 改名 CIFS
*******Windows 2008 改回 SMB2
******NetBIOS:UDP 137/138、TCP 139
*****關閉 NetBIOS 直接使用 TCP 445
***篩選器動作
****工作階段索引鍵設定值:
*****IKE 參數,Client 與 Server 必須吻合,產生新金鑰間隔 (傳輸多少流量後產生新鑰,預設 100000KB;連線多久後重新產生新鑰,預設 15分鐘)
***驗證方法
****使用 Kerberos (僅限於有信任關係的 AD 環境下)
****使用機器憑證
****使用 PSK
!!IP 安全性監視器
*主要模式 (phase 1):交換過第一次金鑰
*快速模式 (phase 2):後續交換過金鑰
!!IPSec 服務
*IPsec policy agent
!SSL VPN
*SSL VPN 使用已存在瀏覽器的 SSL/HTTPS 協定作為安全性傳輸機制
*較不需要用戶端軟體或硬體支援
*用戶端不需要複雜的設定:簡單
*方便應用於全球資訊網類的應用程式
**Web-based 化越高使用 SSL-VPN 越方便
*優點:
**節省成本
**與平台無關
**較適合需要較高行動立的連線方式
**無 NAT 問題
*適合 Client to Site
*微軟 Windows Server 2008 支援的 SSTP (Security Socket Tunneling Protocol) 就是類似 SSL VPN 的一種做法
**需要使用 Vista 以上的用戶端
!VPN 的安全策略
*Tunnel Protocol
**PPTP
**L2TP/IPSec
**IPSec
***L2TP/IPSec、IPSec 被視為安全的協定
*身份驗證方法
**PAP、CHAP、One-time password (Token card)、憑證
***使用密碼驗證只有 MSCHAPv2 被視為安全,因其支援雙向驗證
*加密處理:與通道協定密切相關
**方法:
***MPPE (PPTP 使用)
***IPSec
**演算法:AES、3DES、DES、......)
***有 AES 選 AES,沒有則選 3DES,再沒有就選 DES
****Windows Vista 才支援 AES,WS2k3 僅 3DES、DES
****Cisco 預設使用 AES
**位元數:(128 > 56 > 40),在 RAS 中的 RAP (Remote access policy 中設定),IPSec 最低 56 位元
!遠端存取驗證協定
*PAP (Password authentication protocol)
**網際網路標準
**早期設定在 PPP 伺服器和使用者間的身份驗證
**只使用雙向交握程序 (2-way handshaking)
**只有在登入時進行身份驗證,使用明碼傳送,容易遭到入侵
**優點:效率佳、相容性高
**國內 ISP 業者透過 uuencode 加密 (其實只是編碼),用戶端透過 uudecode 解密
**驗證初始在客戶端
*CHAP (Challenge Handshake Authentication Protocol)
**網際網路標準
**使用在 PPP 伺服器與使用者間身份認証
**使用三向交握 (3-way handshaking),且可設定連線期間的任何時刻再次驗證
**使用 Challenge/Response,並未將密碼真正傳送
**產品常將加密演算法寫在前面,例如 MD5-CHAP
**驗證初始在伺服端,由伺服器提供 Challenge 字串
*MS-CHAP
**修正 CHAP 初始封包不加密的弱點
**MS-CHAP v2
***具備雙向驗證能力
***只有 v2 能完全避免連線攔奪(session hijacking)、中間人(man in the middle)、重送(relay)的攻擊
*EAP (Extensible Authentication Protocol)
**並不是特定的驗證機制,而是用於 PPP 連線過程中協調出驗證技術的架構
**最常被使用在智慧卡驗證,因智慧卡在廠商實作上有極大的差異,因此需透過 EAP 協調驗證協定
**透過遠端存取原則設定
*RADIUS (Remote Authentication Dail-in User Service)
**網際網路標準
**集中驗證 (Central Authentication)、集中紀錄帳戶資訊 (Accounting)
**當 NAS 愈來愈多,使用者在存取資源時,每一台 NAS 自行做身份驗證時,則安全性資料庫會隨著 NAS 數量而增加,造成資訊安全性下降,因此要透過 RADIUS 伺服器,集中身份驗證,因此只需要維護一份安全性資料庫
**微軟 IAS 就是 RADIUS 伺服器,微軟設計 IAS 與安全性資料庫必須存在同一個網域內,等候逾時:微軟設計太短,建議設 10秒;初始分數:優先權,越高優先權越高 (最大 30);連接埠:1812 (網際網路標準,不要改);自動使用訊息驗證器 (使用機器憑證驗證)
***啟動 RADIUS 驗證後,RAP (遠端存取原則) 會消失,必須在 IAS 上設定
***開啟 Accounting:IAS 中遠端存取紀錄
**Linux:freeRADUIS
!TACACS+ (Terminal Access Controller Access Control System)
*提供與 RADIUS 相同的功能
*非 Internet 標準 (常用在 Unix)
**TACACS+ 常被視為 Cisco 專屬協定
*特性:
**雙因子驗證
**使用者可以更改密碼
**Security token can be resynchronized (安全性設定變更後可以立即生效)
**可以記錄使用者消耗掉的連線數,可避免 DoS
!!回撥 (Call Back) 與 Caller ID
*僅適用在遠端撥入使用者,一般的 VPN 不適用
*Caller ID 在台灣翻譯:來電顯示
**需要兩端 modem 與電信線路支援
*Call Back 另外一個好處:公司付費
**確認身分後中斷連線,由伺服器端撥號撥給使用者
!!Diameter
*原來針對公司內部使用手持設備:PDA、手機與公司建立無線連線而 RADIUS 並無支援的解決方案
*主要支援無線,且未來數年將取代 RADIUS 成為驗證伺服器
*RADIUS 無法支援無線
!防火牆的定義
*安全性的機制,用來區隔兩個或多個信賴度不同的網路,例如可信賴的內部網路、不可信賴的網際網路間
*預先建立安全性規則,篩選過濾其中有疑慮的流量 (包含對內與對外),確保網路的安全
**微軟 Windows XP 的個人防火牆:一半的防火牆,只管制對內
*防火牆可集中管理、落實安全政策
!!為何需要防火牆
*網路攻擊、入侵與破壞日益嚴重,企業極需要一套有效的安全性防禦技術
*單純的主機安全性防護病無法解決或滿足整體網路的安全性需求與問題
*企業需要一個可以集中落實安全性政策的機制
*企業需要一個可以集中的稽核紀錄所有進出 Internet 流量,以利後來的安全性查核與分析
!!防火牆能做些什麼
*防火牆的位置 - 咽喉點:防禦的首關,不能 bypass (繞道)
*封包過濾
*紀錄流量
*入侵偵測:現在的防火牆都具備一定的入侵偵測能力
*預設模式使用 NAT 模式,有效隱藏公司內部網段,提升進一步網路安全性
!防火牆安全性政策
*說明誰 (who) 可以在什麼時候 (when) 用什麼方式 (how) 存取哪些 (what)網路服務與資源
*防火牆管理員的工作在於如何落實公司內的安全性政策
!!預設的防火牆安全性政策
*Deny by Default
**正面表列 (Positive list)
**所有封包都過濾,要放行的必須訂定過濾規則
**較安全的策略
*Allow by Default
**負面表列 (Negative list)
**所有封包都放行,要過濾的必須訂定過濾規則
!防火牆類型
!!實作類型
*軟體防火牆
*硬體防火牆
**硬體防火牆因使用專屬作業系統,一般作業系統安全性漏洞常被發掘出,因此__有可能__較軟體防火牆安全
**硬體防火牆叫軟體防火牆快:迷思,若硬體防火牆使用 ASIC 實作的確可以較軟體防火牆快,但使用 flash 則不會
!!學理上防火牆類型 - 以如何過濾篩選封包的方法分類
*封包過濾防火牆 (Packet-filtering Firewall):第三層防火牆
**靜態封包過濾
**動態封包過濾
*狀態檢視防火牆 (stateful Inspection Firewall):第四層防火牆
**常簡稱 SPI
*代理防火牆 (proxy)
**電路閘道器 (Circuit Level gateway):第四~五層防火牆
***常說支援 Socks 4、Socks 5
**應用程式閘道器 (Application-level gateway):第七層防火牆
***常用 Deep Inspection (深度檢視) 稱之
!!封包過濾的原理
*早期:封包過濾路由器
*第一代防火牆:
*主要特性:
**只檢查 IP、TCP、UDP、ICMP 封包表頭
**只會做獨立個別的封包篩選
*檢查特定表頭欄位
**來源 IP
**目的 IP
**協定 (TCP、UDP、ICMP)
**TCP、UDP 的來源埠
**TCP、UDP 的目的埠
**ICMP 的訊息種類 (旗標)
!!封包過濾防火牆優缺點
*優點
**建置簡單便宜
**效率佳
**具透通性 (Transparent),用戶端機器無須任何設定
*缺點
**難以設計一套長期又有效的過濾規則
**無法處理應用層協定,對於資料段或特定應用服務弱點的攻擊方式無法攔截
**缺乏使用者驗證能力
***至少需要第五層
**安全性較差
*不適合企業單純使用
*許多網路系統均可以提供封包過濾功能
**作業系統 (MS:微軟 TCP/IP 堆疊裡的 TCP/IP 篩選;Linux:iptables)
**路由器 (Cisco:IOS ACL)
**特定網路服務 (MS - RRAS:封包過濾功能)
**其他網路設備 (L3 / L4 交換器)
*整合這些具備封包過濾能力的設備裝置、服務,將有助於建構一個多層次保護的防禦系統
!!靜態 / 動態 封包過濾
*靜態:防火牆內的封包過濾規則透過管理員手動設定
*動態:防火牆內的封包過濾規則,透過網路上特定的行為而自動產生
**iptables -m state 可使用動態
**Internal:high port → Internet:80,在使用者連上 internet 會自動開啟 Internet:80 → Internal:high port,且一段時間後自動關閉
*動態較安全、較彈性
!狀態檢視防火牆 Stateful Inspection Firewall
*一種動態封包過濾的防火牆技術,能夠更細分 (more granularly)
*會建立每一個資料流的狀況表,根據前後關聯狀況來判斷是否允許或拒絕封包通過
*透過連線狀態來判斷是否為合法授權的連線封包
*通常是一個安全性與效能上妥協的防火牆類型
*只有支援到 SPI 的防火牆,才能阻絕掉目前知名的 Dos 阻絕服務攻擊
!代理伺服器 Proxy Server
*用戶端與 Internet 不允許直接連線
!!Circuit-level Proxy 電路層閘道器防火牆
*又稱 Circuit Relay
*不允許用戶端直接與網際網路連線
*處理表頭、連線狀態,與 SPI 類似,額外處理中斷連線、建立連線的工作
*屬於一種 common proxy
*用戶端使用的應用程式必須要支援網路代理協定
**網際網路標準為 SOCKS,區分為四版、五版
***五版可支援 TCP、UDP 代理,可支援 User level 驗證,四版則僅支援 TCP 代理,並且不支援 User level 驗證
**SOCKS -aware Application
*優點:
**通常較應用程式代理閘道器快速
**對許多用戶端同時與多重伺服器連線,二者具備透通性
*缺點
**需要修改用戶端應用程式或 TCP/IP 協定堆疊
**除了 TCP、UDP 外,並無法限制其他協定 (如 ICMP、GRE)
!!Application level proxy 應用層閘道器防火牆
*常被稱作深度檢視防火牆
*一種更深度檢視封包內容的代理服務
*下層協定的 Payload 就是上層協定的整體
*防火牆主機上執行的特定應用層篩選器,負責應用層的訊息過濾與轉送服務
**可以處理較複雜的應用層服務
***例如 FTP、VPN、Stream media、VoIP
**內容過濾:『content filter』,檢查封包內容本體,確保特定應用層通訊協定
***HTTP、SMTP、IM (Instant message, 包含 P2P)
**可支援嚴謹的使用者驗證
*優點
**安全性較高,可做資料本體的內容過濾
**紀錄稽核較詳細
***越上層的防火牆越接近使用者,第七層防火牆能紀錄幾月幾日某使用者瀏覽某網站禁止或允許
*缺點
**需針對特定應用程式撰寫
**效能較差
**設定複雜,可能因設定不當而造成存取問題
!不同防火牆類型比較
!混合型防火牆
*Hybrid firewall
*許多防火牆可同時提供封包過濾、狀態檢視、代理閘道器的功能
*利用混合型防火牆以循序性的方式套用多種過濾篩選方式,可加強安全性
*防火牆即使使用相同產品、相同規則,使用不同的架構也影響不同的防護強度
!防火牆架構設計考量
*不同的防火牆架構提供不同的防禦效果與安全程度
*防火牆架構的重要因素
**採用幾層保護
***中古城堡的城牆
***越多層越安全,但效能越差
**是否整合路由器的封包過濾功能
**是否建構非軍事區 (Demilitarized zone,又稱 Screened Subnet 或 Perimeter Network)
**如何建構非軍事區
!防火牆架構
*雙介面主機防火牆
*屏蔽式主機防火牆
*屏蔽式子網路防火牆
!!雙介面主機防火牆 (Dual-Homed)
*網際網路 - 防火牆 - 內部網路
*一個防火牆有兩張獨立的介面
*Multi-homed
**Software,一個網卡設定多個 IP
**Hardware,有兩張以上的實體網卡
*一個網卡連接 Intranet,一個網卡連接 Internet
*通常建議採用網路服務代理型的防火牆
*採用一層,沒整合路由器封包過濾功能,沒有架設 DMZ 區的能力
*安全性較差,有被單點突破的可能,沒有建議非軍事區的可能
*簡單成本低效率高
*適合小規模公司、SOHO、個人使用
*可新增第三個介面,提供建立 DMZ 區的可能 (Three-homed, Triple-homed, Three-leg)
!!屏蔽式主機防火牆 (Screening Host)
*網際網路 - 屏蔽式路由器 - (DMZ) - 防火牆 - 內部網路
*在防火牆主機前端加裝一個屏蔽式路由器開啟封包過濾功能,避免防火牆主機直接與網際網路連接
*屏蔽式路由器:從 Internet 送進來的封包只接受送到防火牆外部介面的封包;從 Intranet 送出去的封包只接受來自防火牆外部介面的封包
*防火牆做:內容檢視、代理連線、
**路由器作:封包過濾
*因保護功能簡單的路由器比較容易,因此較安全
*提供兩層保護,整合路由器封包過濾功能,路由器與防火牆之間適合做 DMZ 區
*又稱:2-Tier 防火牆、微軟稱 Back-to-Back 防火牆
!!屏蔽式子網路防火牆
*網際網路 - 屏蔽式路由器 - (外部 DMZ) - 防火牆 -(內部 DMZ) - 內部網路
*包含兩個屏蔽式路由器
*外部路由器負責過濾來自 Internet 的封包
*內部路由器負責過濾來自 Intranet 的封包
*提供三層防護,整合兩部路由器封包過濾功能,提供兩個建立 DMZ 區的可能,更有彈性
!非軍事區 (DMZ)
*原為南北韓停戰協定中,南北雙方各沿著北緯 38 度線,各自向後撤退 2 公里,形成一個避免衝突的非軍事區
*介於內部網路與 Internet 的區域 (子網路)
*公司內部建構 Web server 與 DNS server 需放在 DMZ 區
**避免不了與 Internet 接觸的伺服器設置在 DMZ 區
*可延緩駭客攻擊,無法避免
*學理上有 DMZ 的架構會比沒有 DMZ 的架構安全
!!DMZ 的防火牆架構
*Three-homed 防火牆
**較簡單的架構
**防火牆具備三個介面,設定複雜
**需設定主機端點對主機端點的流量控制
**不允許防火牆外向內部網路的流量
**公司內部網路向網際網路的流量依據公司網路安全政策即可
*Back-to-Back 防火牆
**較安全,設置較簡單
!防火牆的安全度
*企業連上網際網路,使用了防火牆不見得一定安全,但不採用防火牆或過濾機制就絕對不安全
*防火牆安全性取決於
**企業的防火牆安全性政策
**防火牆的安全性政策是否落實設定
**防火牆的架構與建置方法
**防火牆的功能性是否能落實安全性政策
!防火牆的限制
*防火牆並非萬能
**無法管制內部的駭客
**無法管制到不經過他的網路連線
***定期做 War (Demon) -dialing,偵測是否有撥接的使用者
***無線做 War-Driving,使用無線入侵偵測
**無法防制新的威脅
***防火牆幾乎都是依據已知的威脅而設
**無法有效的防範病毒
***必須要整合防毒 Port,也不建議使用,會影響防火牆效能
***改購買防病毒閘道器
!現代防火牆功能
*基本功能:
**封包過濾、封包狀態檢視、代理存取、紀錄與警示代理、使用者驗證、發布內部伺服器、網路位址轉換 (NAT)
*進階整合功能:
**入侵偵測
**VPN
***通常不具備 VPN 不建議購買,因 VPN 不容易通過防火牆
**內容過濾
**防毒閘道
*越來越多新型防火牆強調『All-in-One』,或者整合式威脅控管 (Unified Threat Management, UTM)
**嚴重效能問題
**把多個功能整合在一個 IC 電路板上,但功能陽春許多而不敷使用
**購買時,需注意效能問題 (跟廠昌凹一個測試機,開啟全功能看效能能不能忍受),功能 (未來三~五年會用到的功能即可)
!攻擊問題的起源
#最基本的原因在於企業網路有弱點 (Vulnerability) 的存在
#弱點暴露出來被有心人士探索得知
**有心人士可能為內部員工或是外面的駭客
**根據經驗通常內部與外部有心人士比例為 7:3 或 8:2
*因此必須
#定期做弱點掃描
**比駭客更早找到弱點
**微軟的 MBSA 工具
***微軟對其作業系統與應用程式進行安全性評估
****掃描微軟作業系統與應用程式的安全弱點
****檢查未更新的修正檔
****檢查不安全的預設組態設定
****提供發現弱點的解決方案
***對要掃描的電腦有管理權限,適用於 AD 環境,且須在能連接上 Internet 的環境
****Configure computers for Microsoft Update and scanning prerequsities 須打勾,以檢查與下載 windows update 最新版本
***命令列指令:MBSACLI,搭配排程工作
****mbsacli /r 192.168.195.130-192.168.195.254 /n os+iis+sql+password
**@@color:red;font-size:1.4em;Nessus@@,全世界 Open source 最好的弱點掃描工具,掃描最新兩週內的弱點需付費
***主從架構
***建議安裝 Linux 版本,報表功能較強
**SARA,只能裝在 Linux 上,純 Web-based 介面
#提供有效的安全性修正檔案的 套用/更新 機制
**企業有效的安全性修正檔案機制須符合以下四個條件
***自動 Automatic
****Auto Update 自動更新
***強制 Enforce
***即時 Realtime
****Auto Update 勉強貼近即時條件
***集中 Central
**透過 Windows Software Updte Services (WSUS) 提供上述四個條件的解決方案
**要錢的 Systems Management Server 也行
!網路攻擊類型
*主動攻擊類型 (Active Attack)
**在攻擊行動裡面,有心人士經由修改資料流量、或是創造新的資料流量
*被動攻擊類型 (Passive Attack)
**在攻擊行動裡面,有心人士並未修改或創造新的資料流量
**主要攻擊為攔截 (Interception) 封包,最常用的是流量分析 (Traffic Analysis) 與竊聽 (Sniffering)、敏感資訊的窺視 (Shoulder surfing)
!常見的網路攻擊形態
*連接埠掃描
*密碼破解攻擊
*阻絕服務
*電腦病毒
*蠕蟲
*特洛伊木馬
*假造 IP 位址
*連線攔截
*重播攻擊
*網路竊聽
*社會工程
!網路攻擊問題的緣由 (弱點)
*http://www.sans.org
*TCP/IP 通訊協定本身的安全性問題
*作業系統與應用程式的瑕疵
*網路與資訊系統管理不當
*惡意程式的誤用
!網路入侵攻擊目的
*意圖竊取各種資料、軟體
*破壞資料,系統,使電腦無法正常運作,可能是商務上理由、政治上因素
*惡作劇
*炫耀能力
!!連接埠掃描 (Port Scanning)
*不算真正的攻擊,通常是網路攻擊前的資料蒐集和準備工作
*目的在於掃描目前啟動的機器與提供的服務
!!密碼攻擊
#暴力破解 (Brute-force)
**利用密碼所有可能一直嘗試直到破解為止
#字典攻擊 (Dictionary-based attack)
**收集被害人身邊經常出現的字彙反覆測試
#混合攻擊法 (Hybrid)
*因應策略:Physical Security (例如鎖在機房)、Pasword Policy (enforce strong password)、Account lockout、use of the nonkeyboard characters
!!阻斷服務 DoS, Denial of Service attack
*阻絕攻擊
**攻擊者藉由佔用主機或系統的資源或服務,讓系統的可用度 (Availability)降低,導致一般使用者會無法正常使用系統或主機所提供的服務
***網路頻寬
***CPU
***記憶體
***磁碟 I/O
****常見的郵件炸彈即是針對管理員帳號塞爆其信箱空間,導致磁碟 I/O 停擺
|Attack|→|Zombie<br>botNet|→|Victim|
*DDoS 分散式阻絕服務
**DDoS 算是 DoS 的一種,只是並非以一對一的攻擊模式,而是發動多對一的方式,同時對於一個攻擊目標
*跳板攻擊 Bounce of Attack
|Attack|→|Master|→|Zombie|→|Victim|
|~|~|~|→|Zombie|→|~|
|~|→|Master|→|Zombie|→|~|
|~|~|~|→|Zombie|→|~|
|~|→|Master|→|Zombie|→|~|
|~|~|~|→|Zombie|→|~|
*西元兩千年以來駭客最常使用的方式,目前為止並沒有一個真正有效的防範措施
!!減緩 DoS 攻擊的原則
*所有公司連接網際網路的路由器都需設定好 Ingress Filtering、Egres Filtering
**Ingress Filtering:都不處理來源不合理,或是目標是廣播的封包
***來源是偽裝的
**Egress Filtering:不處理來源不合理,或是目標是廣播的封包
!!網路上知名的 DoS 攻擊手法
*TCP SYN (SYN flood)
**利用 3-way handshaking 的弱點,攻擊方假造自己 IP 送出 SYN 要求連線,當受害者收到 SYN 封包會放在 TCP/IP 緩衝區 (Buffer),送出 SYN/ACK 的同時若收到 ACK 回應則從緩衝區中踢除 buff 起來的 SYN 封包,但駭客利用這個弱點,反覆送出假造來源的 SYN 封包,而
**因應措施:
***微軟:參閱 __神__ windows server 2003 系統安全實務講義 P68
****HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters "SynAttackProtect"=dword:1
***Linux:延展 Buffer 空間
****echo 1> /proc/sys/net/ipv4/tcp_syncookies,加入 /etc/rc.local 開機檔中
!!Smurf
*利用 ICMP 弱點,將假造的封包送到廣播位址,然後廣播位址會傳送大量的 icmp 回應封包給目標電腦,造成更多倍數的 echo reply 封包傳回
!!Ping of Death
*利用發出過大的 Ping echo Request 造成緩衝區溢位
!!Spoofing (Masquerade, 偽裝)
*用來改變網路封包的來源位址
**隱藏入侵者身分與位置,以避免被追查
**欺騙路由器或防火牆,假裝入侵者是來自於可信任的網路
**加強其它攻擊或 DoS 攻擊的能力
!!LAND Attack
*被譽為把 Spoofing 發揮到淋漓盡致的攻擊手法
*攻擊者利用 IP 偽裝的技術將送出的封包,其來源與目的 IP、Port number 與目標機器的 IP 位址、提供服務的 Port number 一樣,將會造成類似無窮回圈的效果,等到 TTL 一到才會停止
!!Teardrop
*利用 IP 封包的切割與重組的原理,讓提供網路服務的主機因誤判封包大小能當掉
*IP 表頭的欄位 Fragment ID 與 offset
**ID:表示這些封包都同屬一個 IP 封包,因不同網路協定下被切割開
**offset:第一個封包隨機,第二個封包是第一個封包隨機值加上第一個封包長度,第三個封包是第二個 offset 加上第二個封包長度
*攻擊方故意送出一些奇形怪狀的小片段 (Fragment),讓目標因為封包大小與 offset 無法配合而造成封包重組上困難
!連線攔奪 (Session Hijacking)
*使用在密碼很難破解的一種特殊的入侵環境
*駭客在企業內部安插一部 Sniffer (或 Sniffer agent),攔截用戶端向重要伺服器的連線,在用戶端向伺服器身分驗證成功後,透過自動工具偽裝為伺服器 IP 對用戶端送出 FIN 或是 RST (強迫結束),在此同時偽裝成剛剛驗證成功的用戶端向伺服器存取重要資料
*缺點:不能常用,常用會被抓包
!重送攻擊 (Relay Attack)
*攻擊者在竊聽 (Sniffer) 網路後,擷取並紀錄通訊雙方傳送的憑證資訊,之後便重送這份憑證資訊以假冒某個特有共用金鑰的使用者,以達到存取系統的入侵目的
!連線攔奪與重送攻擊的避免方式
*金鑰採用較短的有效時間限制
*採用完整雙向驗證 (Mutual Authentication) 的安全性協定
**微軟驗證環境下符合雙向驗證的條件
***LAN:NTLMv2、Kerberos
***VPN:MS-CHAPv2、EAP 搭配憑證
***IIS:整合式驗證
**Linux 符合雙向驗證
***SSHv2、Kerberos
**無線網路下符合雙向驗證
***TKIP、802.1x 、802.11i (WPA2)
*Mallicious code = Malware
!病毒 (Virus)
*刻意撰寫的的程式,通常會不斷的複製、感染正常的__本機__程式或資料,或破壞__本機__系統的正常運作
*常見的病毒分為
**開機型病毒 (BootStrap Sector Victor)
***會存在 MBR,將原先的 MBR 程式放在硬碟中第二個 Sector,也會放在每一個分割區的第一個 Sector
***format 並無法清除,必須重造 MBR,微軟作業系統可透過 Recovery console 的 fixboot 與 fixmbr,最後使用 ERD
**檔案型病毒 (File Infector Virus)
***寄主在某些程式檔:.exe、.dll、.com、.sys 等,隨著這些檔案的被執行、開啟而感染
**複合型病毒 (Multi-Partite Virus)
***兼具開機型與檔案型病毒的特性
**巨集病毒 (Macro Virus)
***通常是用直譯式語言 (Interpreter),最常見的 VBA (MS Office),最常感染的對象是 MS Office 文件
**千面人病毒 (Polymorphic / Mutation Virus)
***每次複製繁衍一次後,其病毒特徵碼 (virus signature) 就變更一次,無法藉由特徵碼辨識,必須透過行為偵測比對
!蠕蟲 (Worm)
*通常為自我包裝 (self-contained) 程式,並不需要寄主程式 (host application) 或寄主區域 (host area) 就可以不斷複製自己由一部電腦到另一部電腦 (self-replicating)
*通常具備較快速的網路散佈能力
*大部分蠕蟲感染途徑透過 Email、網路檔案傳輸、USB
*蠕蟲將耗盡電腦資源 (CPU、網路資源)、修改或破壞系統設定,甚至系統當機
!特洛伊木馬 (Trojan Horse)
*又稱後門程式,通常會佯裝成其他有特定用途的檔案,誘使他人下載、開啟或執行
*許多駭客利用木馬植入他人電腦後,再經由遠端遙控功能或鍵盤測錄功能來竊取帳戶密碼、電腦資料、甚至於竄改程式資料,並進行電腦活動的監控
*只要使用者在網路上下載軟體或開啟電子郵件內的附件即可能被植入
!!木馬運作特性
*木馬與病毒主要差異
**通常不會複製或感染其他檔案
**通常為獨立的執行檔
*通常需事先植入目標電腦 (Email、檔案下載)
*木馬程式通常必須要讓目標電腦執行程式一次 (通常放在系統自動啟動設定區)
**透過服務
**HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\run
*木馬程式須監聽特定的連接埠,以等待各種指令
!邏輯炸彈 (Logic bomb)
*屬於惡意程式內的一段程式碼,通常隱藏在惡意程式中並且不會隨意執行到,一旦某個預先設計的條件吻合時,他就會啟動
*13號星期五
!間諜軟體 (Spyware)
*在沒有系統管理者或擁有者同意下,進行特定的行為,例如監控使用者的上網行為,收集使用者資訊 (Cookie, Password, 通訊錄)
*間諜軟體帶來的問題
**可能降低系統效率,甚至當機
***間諜程式可能寫成 kernel mode 特權等級最高,當與另外一隻特權等級相衝突 (例如防毒軟體) 可能會造成當機
**侵害隱私權
**竊取資訊
**軟體綁架 (hijack)
***IE 綁架
!廣告軟體 (Adware)
*在執行時顯示或傳播廣告訊息
*早期常搭配間諜軟體,收集使用者的行為偏好資訊傳回,而將使用者有興趣的資訊傳給使用者
!惡意程式隱匿技術 - rootkit
*用來隱匿不被察覺的技術,在微軟作業系統底層攔截 hook 訊息
**例如 netstat -ano 檢查有無開啟未知的監聽埠時,如果惡意程式有 rootkit,則有可能被惡意程式攔截系統底層資訊,先一部篩掉惡意程式的資訊,而使用者無法察覺;或是程序管理員也有相同的問題
*就算是神,遇到中共網軍的 rootkit 惡意程式也沒有把握能逐一清除,砍掉重練比較保險
*Sysinterals - rootkitreveal:Rootkit 掃描程式
!魁儡網路 (botnet)
*殭師網路 (zombie network)、機器人網路 (Robot network)
*可自動執行的電腦程式,或機器人程式,非法安裝在電腦系統中
!防範惡意程式
*大多來自網頁檔案下載、電子郵件、P2P、USB,@@color:red;font-size:1.2em;故不使用管理員身份上網及接受郵件@@
**一般使用者登入,使用 runas
*使用防毒軟體
##定期更新病毒定義檔
##啟動開機防護
***監控網頁下載與電子郵件
##定期做完整的磁碟掃描:國內這點作的不夠
***開機防護只是做病毒碼比對,做完整的磁碟掃描可以針對行為特徵比對
**以上對於病毒、蠕蟲、一般的木馬均有不錯的防範效果
*防毒程式雖可偵測與移除蠕蟲,但軟體漏洞仍需要修補,不然會一再感染
*針對木馬、間諜廣告軟體需使用專門的防間諜、防廣告軟體與偵測此種惡意程式 (例如 Internet Security Suite)
**微軟免錢防間諜軟體: Defender
***Windows vista 以上版本內建
****購買自 Giant 公司
**防間諜軟體移除間諜軟體前,需注意是否是自己所需要的 sniffer、遠端遙控等
**免錢防廣告軟體:ad-aware
***偵測到廣告軟體直接移除沒問題
*線上惡意程式掃描
**http://housecall.trendmicro.com
**__BitDefender http://www.bitdefender.com__
**http://www.kaspersky.com.tw/virusscanner
*免費防毒軟體
**Avia Antivir Personal
**Avast
**AVG Anti Virus
*防間諜軟體
**Microsoft Defender
**Spybot - search & Destroy
**PC Tools spyware doctor (要清除必須購買付費版)
*防廣告軟體
**Ad-aware
*USB 防毒
**Wow! USB Protector
*防毒認證 VB100 、AV test
*最常用的被動式攻擊
*攔截封包並檢視分析內容
*取得或破解私密性資料
*Sniffer 的運作方式就是將乙太網路卡設定成隨機處理模式 (Promiscuous Mode),處理所有的封包
**正常運作下的乙太網路卡是 Normal mode,只會處理目的端為自己或廣播封包
**需透過驅動程式來轉換模式,最著名的就是 winpcan
*因應:全面使用交換器取代集線器,大幅減少竊聽機率,重要機密加密傳輸
*利用非技術性手段,取得系統存取的資訊,例如佯裝為服務、管理人員或目標員工以謊稱或騙取密碼
*因應措施:
##加強員工資訊安全教育
##變更不當或不安全的運作流程
***利用憑證+讀卡機登入驗證身份的商業性網站
***利用螢幕小鍵盤點選密碼取代傳統鍵盤,以防被木馬側錄
!釣魚 (Phishing)
*冒充知名網站以誘騙他人上網並進而取得使用者信用卡或密碼等機密資料
!!防止 Phishing 騙術:教育
*IE7、Exchange server 2007、Outlook 2007 中有 Anti-phishing 功能
*要使用銀行的商業性網站,記得跟銀行申請憑證與讀卡機
!(匯出)備份組態規則
*匯出機密資料:主要是匯出憑證,並且會設定額外一組密碼
*可作為日後還原、遷移PAD等用途
!ISA 伺服器管理主控台介面
|主<br>控<br>台<br>樹<br>狀<br>目<br>錄|詳細窗格|工<br>作<br>窗<br>格|
*工具箱:設定防火牆功能的元件
!!直覺化管理介面使用特性
*大量採用設定精靈
*一目了然的監控儀表板
*具備拖曳 (Drug & Drop) 功能的工具箱
*簡化架構變更的網路範本
**邊緣防火牆 (一層式的防火牆)
**外圍三向 (3-Leg)
!遠端管理 ISA Server
*兩種方式
**遠端桌面
**ISA 管理主控台:需先裝在管理員機器 - 至少需要 XP SP1 與 Windows Installer 3.1 (including in SP3)
*要讓系統管理員的機器加入系統原則順序 2、3 的規則
!委派管理
*三種管理角色
**ISA Server 監視稽核員
***只能看到監視儀表板內的內容
***不能變更任何設定
**ISA Server 稽核員
***可執行所有監視工作,可以設定監視儀表板內容
**ISA Server 系統完整權限管理員
***對整個 ISA Server 擁有完整權限
!ISA Server 安全性管理原則
*實體安全性 (鎖在機房、禁止他人本機登入)
*選擇較高的安全性範本
**最好選擇 securews、或 hisecws
*關閉 netbios over TCP/IP
*關閉 file and printing share for microsoft network (server service)
**停掉一切共用資料夾功能
*強制更新 OS 與 ISA 的安全性修正檔案
**建議使用 WSUS、SMS
*關閉沒有必要的服務
*密碼學是利用數學方法來對資料加密和解密的科學
*密碼學名詞
**Plaintext; cleartext 明文
**Ciphertext 密文
***明文經過 金鑰(Key) + 演算法(Algorithm) 的黑箱作業 - 搗亂(Scramble) 加密後變成密文
**Decipher 解密
**Cryptanalysis 密文破解
***在沒有原始金鑰或假造金鑰的情況下解密
!加密技術的強度
*加密技術的強度是指密碼破解所需要花費的時間與資源
*加密技術強度的高低通常牽涉到下列的因素
**演算法強度 (演算法則的複雜度)
**金鑰保護機制
**金鑰的長度
***通常長度越長越安全
***目前密碼學加密技術的強度通常決定在金鑰長度
*Kerckhoff Principle
**密碼系統的安全性不在演算法的保密,而是取決於金鑰的保護機制
!計算上的安全 (Computationally Secure)
*計算上的安全兩個條件
**破解所需要的成本高於該訊息的價值
**或是破解所需要的時間超過該金鑰的壽命
!金鑰 (Key)
*本質上是一組相當長度的數字或符號字串,大小通常以位元 (bit) 為單位
*通常為演算法則內的一個變數 (Variable),所以不同的金鑰會產生不一樣的密文
*就密碼學而言,金鑰越長則密文則越保密
!資訊隱藏:隱匿法 (Steganography)
*和密碼學不同,資訊隱藏是無法偵測到的,經常使用資訊隱藏來輔助加密
*經常使用在著作權保護
!密碼學分類
*密碼學系統常可以根據三種不同的觀點來分類
##將明文轉成密文所使用的方法
***取代 (Substitution) :將字元取代變成不可辨識
***置換 (Transposition):重新排位子
***相乘 (Product):將上述兩種合併使用,既取代又置換
##鑰匙的使用個數:資訊安全密碼學最常見到的分類
***對稱性加密技術 (Symmetric) 或稱秘密金鑰加密技術 (secret-key):加密解密使用相同金鑰
***非對稱性加密技術 (Asymmetric) 或公開金鑰 (Public Key):加密解密使用不同金鑰
***雜湊 (Hash):加密不使用金鑰
##處理明文的方法
***資料區段加密 (Block cipher),將明文切成數個區塊 (n 個字元或位元),逐一針對每個區塊透過將同的金鑰與相同的演算法則加密,將數個加密過後的加密區塊組合
***資料流加密 (Stream cipher):以字元或位元為單位,經過與金鑰作特定的運算後得到密文
****串流加密在數學上無法證明其安全性,所以不常採用,但優點為速度快
****RC4 為少見著名的資料流加密
!古典加密技術
*將明文中的字元用其他字元或符號來替代
*Caesar 加密法
**最早且最簡單的取代加密法,由西元前 50 年羅馬皇帝 Julius Caesar 採用
**將每個字母用其後的第三個字母來取代
***明文:ATTACK AT DAWN
***密文:DWWDFN DW GCZQ
*Caesar 加密演算法:C = E(P) = (P+k) mod(26)
*Caesar 解密演算法:P = D(C) = (C-k) mod(26)
*古典加密技術為取代、對稱性加密方法
*演算法過於簡單且金鑰空間 (Key space) 太短,易被破解
!古典密碼技術 (續)
*Monoalphabetic 加密法 (單一字母加密法)
**一種簡單的加密技術,方式是產生一份英文字母的加密對照表,如 A→Z、B→D、C→F......
**一對一字母對應
*金鑰空間 26!
*破解方法:收過夠多的字母可藉由英文常出現的字母頻率來破解
*多字母加密法 (Polyalphaphetic)
!對稱加密技術 (Symmetric Encryption)
*對稱性密碼學、傳統或秘密金鑰 (Symmetric Encryption,convetional)
**加密解密使用同一把鑰匙
**需要傳送和接收雙方均擁有相同的一把金鑰
*一個好的對稱性加密演算法要避免 Key Clustering
**Key Clustering:當兩把金鑰將相同的明文經過加密後產生相同的密文
*優點
**較快速
**使用大的金鑰將難以破解
*缺點
**需要有一個安全性機制將金鑰安全性分送至交易的雙方
***金鑰管理、金鑰的發送 (Key Distribution) 變得相對的困難
**提供私密性 (confidential) 的安全性能力,無法提供 authenticity 與 non-repudiation
**當要與 N 個人交換訊息則需保存 N 把金鑰,以管理員角度則需保管 N(N-1)/2 把金鑰
*對稱性加密的金鑰通常稱為 Secret-Key
!非對稱性加密技術 (Asymmetric Encryption)
*每個使用者擁有一對金鑰:公開金鑰 (Public key) 與私密金鑰 (Private)
*演算法必須滿足以下條件:用公開金鑰加密後,僅有該配對的私密金鑰能解密,用私密金鑰加密後,僅有配對的公開金鑰能解密
*優點
**金鑰的發送相對於對稱性加密技術容易
**提供私密性、身分驗證、與不可否認性
**非對稱性加密技術在金鑰的保管、維護與延展性上彈性較高,與 N 個人交換訊息只需要保管自己的私密金鑰與公開金鑰
*缺點
**效率較差
***根據數學家的講法,大約慢 1000 ~ 10000 倍
!對稱性加密技術演算法
!!One-time pad (Vernam Cipher)
*被視為簡單、但完美不可被破解的加密技術
#選取一個隨機數
#隨機數與要加密的明文一樣長度
#密文 = 明文 XOR 金鑰
#明文 = 密文 XOR 金鑰
*雖然簡單又完美,但不常使用的原因在:
##目前的電腦不可能產生真正的隨機數
##要與明文相同長度的隨機數,攜帶使用非常不方便
!常用的對稱性加密演算法則
!!Data Encryption Standard (DES)
*存在最久、最被廣泛使用的對稱性加密技術演算法
*1977 年由美國國家標準與技術協會 (NIST) 採用為聯邦資訊處理標準,使用到 2000 年
*採用混淆 (Confusion) 與擴散 (Diffusion) 原理
**混淆就是將明文轉換成其他的樣子,讓金鑰與密文之間的關係盡量複雜化
**擴散是指名文中的任何一個小地方的變更都會擴散影響到密文的各部份
*DES 採用 56 位元的金鑰來對 64 位元的資料區段進行加密,需經 16 回合 (Round) 的運算
*主要缺點:56 位元的金鑰長度太短,以目前的電腦計算能力,通常只需要花費一些時間找出 DES 金鑰
**在 90 年代 2^^56^^ 變成可被破解,以今日而言,其可被快速破解
!!DES 運作模式
*ECB (Electronic Code Book):每個區塊加密是獨立的,並無與其他區塊的關連性,可以平行加密
**Loading 較輕,效率最高,但容易被各個擊破,以致安全性最差
*CBC (Cipher Block Chaining):每個 64 位元的區塊加密前須和前一回合產生的 64 位元的密文做 XOR 運算再加密
**第一個明文區塊則透過系統隨機產生的明文數值 IV (Initial Vector, 初始向量),做 XOR 運算後才加密
***IV:為了使加密第一個區塊能順利進行而由系統產生的明文數值
*CFB (Cipher Feedback Mode):每個區塊與與前一個密文區塊在刺加密後作 XOR 運算
*OFB :
**S~~0~~ = 加密(IV), S~~i~~ = 加密(S~~i-1~~)
**密文區塊~~i~~ = 明文區塊~~i~~ XOR 加密(S~~i-1~~)
!Triple DES
*1992 年發現 DES 可以反覆使用來增加強度,因此產生 3DES (運算 48 回合)
*3DES 可以使用兩把或三把金鑰,如果是兩把,則是第一把與第三把金鑰
*採用三把金鑰長度為 168 位元,採用兩把金鑰則是 112 位元
*__比其他演算法都慢__
*好處:
**美國聯邦法規定 128 位元以上的加密演算法都必須經過同意才能出口,而 3DES 仍算是 56 位元加密演算法
*類型:DES-EEE3、DES-EDE3 (使用第一把加密、第二把解密、第三把加密)、DES-EEE2 (使用第一把加密、第二把加密、第一把加密)
**DES-EDE3 最常被使用
!IDEA 加密演算法
*使用 128 位元的金鑰對 64 位元的區塊作加密楚哩,需經八回合的加密運算
*較 DES 難破解
*商業用途需購買授權
!Blowfish
*1993 年由當年 28 歲的 Bruce Schneier 提出,目標想取代 DES 與 IDEA
*對稱性加密技術,採用 __變動長度金鑰__ (32bit ~ 448bit)
*較 DES 快速許多
*簡單且容易實作
*Blowfish 不需授權,可以提供所有使用者使用
!Twofish
*1994 年 Bruce 提出演算法則,1997 年正式發表
*改良原先 64 bit 的區塊變為 128bit 的區塊,理論上區塊越大越安全
*以目前數學家推演出的超過 2^^90^^ 的金鑰長度即超過目前計算機在人類生命周期能破解的範圍,因此取 2^^128^^
*採用 128、192、256 位元金鑰
*unpatented、license-free
!進階加密標準 (AES, Advanced Encryption Standard)
*為了取代 DES,NIST 於 1997 年 4 月正式公告徵求下一代的塊狀密碼器 AES,以保護敏感但非機密的聯邦資料
*五個 Finalist:MARS(4)、RC6(5)、Twofish(3)、Serpent(2)、Rijndael(1)
**目前為止仍未被證明其中有被攻擊的弱點
*Rijndael 勝出的原因:容易在硬體上實作
*AES 演算法大量使用位移 (Shift)
*當初送審的的 Rijndael 演算法支援 25 種運算模式,128, 160, 192, 224, 256 多種區段及金鑰長度的加密技術
*修改後的 AES 演算法使用 128bit 為區塊大小,金鑰長度可為 128, 192, 256 位元
!RC 系列
*由美國 RSA 公司所提出
*搜尋 RSA 公司網站 FAQ,提供淺顯易懂的密碼學知識
*RC2 使用 64 位元的區塊加密
*RC4 是少見的串流加密
*RC5、RC6 除了北美地區少見
!!RC5
*採用 32、64、128 位元區塊,可變動長度金鑰 0 ~ 1024 位元
*原作者建議採用 RC5 32/12/16:區塊長度 64 位元,編碼 12 回合,金鑰長度為 128 位元
!Diffie - Hellman Key Exchange (1976)
*當初設計是一種公用金鑰加密演算法,可讓兩個通訊實體協商決定共用密鑰
*一種讓交換金鑰通過不安全網路的方法
!RSA
*RSA 是目前最普遍的公開鑰匙加密法
*RSA 金鑰長度不定,目前為 1024 位元以上才安全
**比對稱加密技術要長的原因
***對稱加密的金鑰通常有效時間較短,維持數個小時通常就要更換,而非對稱加密技術至少都維持一年
!!金鑰產生步驟
#隨機產生兩個大的質數:p、q
#N = p * q
#找出 N 的尤拉函數
**比 N 小且與 N 互質的所有整數 __個數__
**尤拉函數(N) = (p-1)(q-1)
#第一把金鑰 e 就是與尤拉函數(N)的最大公因數
#第二把金鑰 d = e^^-1^^ 除以 尤拉函數(N) 取餘數
!橢圓曲線密碼學 Elliptic curve cryptography
*新一代密碼學演算法
*在相同的安全性強度下,ECC 的金鑰長度遠較其他演算法 (如 RSA) 的密碼系統小
**RSA 1024 位元長度,則 ECC 只需要 160 位元長度即可達到相同的安全性強度
*因其只要非常小的金鑰儲存空間,極適合用在儲存資源有限的設備例如智慧卡等
*微軟常用 ECC 來驗證用戶端是否正版
**Vista 之後也支援 IPSec
!雜湊函數
*將任何長度的訊息輸入後加以濃縮,轉換為一個長度較短且固定的輸出,此輸出訊息為雜湊值 (Hash Value) 或訊息摘要 (Message Digest)
*雜湊函數特性
**單向的映射函數 (One way transformation),無法由密文還原為明文
**抗碰撞性 (collision resistance)
***弱抗碰撞性 (Weak):已知一個明文與其產生的摘要值,但無法找到另外一個明文產生的摘要值與前面的摘要值相同
***強抗碰撞性(Strong):能證明無法找到兩個不同的明文產生相同的摘要值
***常被稱為『數位指紋 (Digital Fingerprint)』
**擴張性 (Diffusion):明文中任何一個小地方的變更,都會擴散到整個密文,使人一眼就能辨別差異
*應用
**確保資料傳送的完整性
**數位簽章
**密碼儲存
**訊息確認
!!MD4
*較快
!!MD5
*安全性較高
!!Digital Signature Standard (DSS)
*使用雜湊演算法
*輸出 160 bits
*區塊大小 512 - 1024 bits
!!SHA、SHA-1
*為了符合 DSS 要求衍生出來的演算法
*512 bits 區塊,160 bits 摘要值
*輸入有 2^^64^^-1 位元的限制
!!RIPEMD-160
*512 bits 區塊,160 bits 摘要值
!!Hashed Message Authentication Code (HMAC)
*一種訊息認證機制
*利用雜湊演算法則與一把金鑰 (secret key) 一起運算產生摘要,藉由比對摘要的值,可以檢查資料在處理或傳輸過程中是否保持完整性並做訊息驗證
*依據採用的雜湊函數可分為 HMAC-MD5、HMAC-SHA1
!!生日攻擊法 (Birthday Attack)
*雜湊的破解不是在於還原密文,而是透過破解找到另外一個明文到目的端產生一個相同的摘要值
*問題:一間教室要有多少人,才會有 50% 以上的機率其中有兩位同學生日同一天,
**答案:23 人
*衍生 MD5 破解法:
**立論基礎:128 位元的 MD5 HASH 值結果有 2^^128^^ 的變化,理論上在人的生命週期無限的情況下,取得 2^^128^^ 不同的明文即有可能找到與已知明文相同的摘要值
**根據賭徒理論:只要機率超過 50% 就算是有機會,因此只要 2^^64^^ 就有機會破解
**透過數學方法可將 2^^64^^逐步降低,目前為中國山東大學王小雲提出的 2^^42^^ 最低,且透過超級電腦可在極短的時間破解
**MD5 破解!
!PKI (Public Key Infrastructure)
*包含非對稱性加密技術、軟體、網路服務的整合技術,主要用來提供保障網路通訊和電子企業交易的安全性
**憑證服務、目錄服務
*支援數位憑證和公開金鑰各項標準或協定的整合性安全服務與架構
!Why PKI
*電子商務牽扯到的都是錢,交易需要更多層面和高度安全性的交易機制
**需要 NR (不可否認性(
*傳統的密碼系統不夠安全,易受攻擊,而維護和管理密碼的負荷高
!!公開金鑰加密
*寄件者取得收件者公開金鑰,透過收件者公開金鑰加密信件內容,收件者透過自己的私密金鑰解密信件內容
!!公開金鑰驗證
!!公開金鑰如何取得
*憑證管理中心 (Certification Authority):可信賴的第三者或機構來當公鑰授權單位,以簽發公鑰電子憑證的方式來證明公鑰的效力
**微軟翻譯憑證授權管理中心
*CA 就是一個用來提供發行、撤銷管理憑證的服務單位
**國內以內政部為 CA 發放自然人憑證
*可由政府、商業公司 (如 Verisign、Thawte Consulting) 或組織內自行架設以提供各項憑證相關的服務
!憑證 (Certificate)
*數位憑證是經由 CA 使用其私密金鑰簽署的一份電子郵件
*用來證明公開金鑰和特定的個人或單位 (擁有者) 的聯繫關係
*標準:ITU-T X.509
*憑證內容包括版本 (目前為三版)、序號、使用者名稱、公開金鑰、發證者 (Issuer)、生效和到期日、擁有者、支援的演算法......等資訊
!憑證管理中心發行憑證
#CA 接受用戶端的憑證請求
#確認請求資訊並決定是否同意發行
#CA 使用其私密金鑰對所要發行的憑證數位簽章
#發行憑證予申請的用戶端
!PKI 的基本角色
*CA 公司有責任與義務在網際網路上維護一個公開的資料庫 (Certification Repository) 提供對憑證有疑慮的用戶端查詢
*一份有效的憑證
**可信賴的 CA 發行
**沒有過期
**沒有撤銷
***查詢 CA 公司的撤銷資料庫
!Windows PKI 主要元件
*憑證服務 (Certificate Service)
**PKI 的核心服務,允許企業扮演自己的 CA,自行發行和管理憑證
*AD
**透過 PKI 的發佈服務
***不是絕對的必要,允許使用共用資料夾發布
*PKI 嵌入式應用程式支援
**IE、IIS、Outlook、Outlook express、MS money、Exchange、SQL、ISA
!Windows 憑證的應用
*伺服器驗證 (Server Authentication):例如 SQL
*用戶端驗證 (Client Authentication)
*電子郵件的安全性傳遞 (Secure E-mail)
*軟體簽署 (Code Signing)
**網頁應用程式常會出現,例如 adobe flash player 安裝時會出現
***證明軟體發行者的身分,具不可否認性
***證明軟體自發行後,沒有被做任何變更
*加密檔案系統 (EFS)
*IPSec:機器驗證
!Windows CA 類型
*企業 CA 需 AD 支援,有效範圍僅及於 AD 樹系內的使用者與機器,可依據所要求的憑證類型的原則及安全性使用權限設定,來接受或拒絕,企業 CA 並不會將憑證請求設定成擱置,使用憑證範本可自動發行,可發行使用智慧卡來登入的憑證,可使用網頁或憑證請求精靈
**AD 使用者透過憑證請求精靈申請憑證 <<toBalaFlashPlayer "flash/04.3-07-enterprise_CA_user_use_wizard_request_certification.swf" "有影片有真相" "800" "620">>
**AD 環境下檢視、管理已發行憑證 <<toBalaFlashPlayer "flash/04.3-08-CA_AD_users_and_computers.swf" "有影片有真相" "800" "620">>
*獨立 CA 較具彈性,可發行給不同樹系的協力廠商,預設會設定為擱置,等待 CA 系統管理員確認,只能使用相同的預設憑證,不能發行智慧卡登入的憑證,僅可使用網頁申請憑證
{{item1{__CA 架設前準備__}}} <<toBalaFlashPlayer "flash/04.3-01-CA_preinstall.swf" "有影片有真相" "800" "620">>
{{item1{__CA 安裝__}}} <<toBalaFlashPlayer "flash/04.3-02-CA-install.swf" "有影片有真相" "800" "620">>
#新增移除 windows 元件
#安裝後無法修改電腦名稱、加入網域或特定工作群組
#若在 AD 環境可以架設企業 CA
**大型企業:總公司架設 企業根 CA,分公司 企業次級 CA,企業根 CA 僅發行憑證給次級 CA (SCA, Subordinate CA)
***使用者收到 SCA 發行的憑證會自動信任根 CA
#選擇自訂
#CSP:選擇密碼使用的套件,雜湊演算法,預設 SHA-1 已是最新最安全的
#金鑰長度 2048 足夠
#CA 的一般名稱,在網路上的名稱
#有效期間:通常 CA 都 10 ~ 20 年
#憑證資料庫
**CA 系統管理員須定期備份憑證資料庫與記錄檔
#共用資料夾
**不在 AD 環境下只能透過共用資料夾來查詢
**AD 環境下建議取消
!!微軟憑證服務安裝元件
*憑證請求網頁
*『憑證授權單元』嵌入式管理工具
*命令列管裡工具
{{{
certutil
certreq
certsrv
}}}
!!管理憑證服務
*使用『憑證授權單位』工具
*啟動與關閉服務
*備份與還原 CA 憑證、資料庫與紀錄檔
**資料庫備份加上紀錄檔備份才可以還原到最新的狀態
*備份重要資料
!!備份與還原 CA
*備份策略
**備份整部伺服器
**備份系統狀態資料
**備份重要資料
*若備份私密金鑰,則必須妥善保管,且需額外設定一組密碼予以保護 <<toBalaFlashPlayer "flash/04.3-03-CA_backup_private_key.swf" "有影片有真相" "800" "620">>
**通常會以 .p12 副檔名格式儲存
**備份私密金鑰的密碼需符合密碼原則
**備份私密金鑰後,p12 檔案仍需要妥善保存,雖然有密碼保護,但破解密碼只是時間問題
**沒有匯出私密金鑰的檔案類型通常為 .cer 檔
!!憑證的發放管理
*發放憑證
*拒絕發放
*撤銷憑證 (revoke) <<toBalaFlashPlayer "flash/04.3-04-certificate_revoke.swf" "有影片有真相" "800" "620">>
**假如在有效日期到期之前網路上發生安全性事件,使得已發放的憑證使用上有安全性的問題
***私密金鑰洩漏
***私密金鑰遺失
***員工離職
***聯盟關係變更
**微軟的撤銷後並不是馬上在 Internet 發佈,預設一個禮拜發佈一次
***若已撤銷的憑證在未發佈前仍會被當作是有效的憑證
***若憑證撤銷後有重大安全性的疑慮,則手動發行
**CRL 發佈點:CRL Distribution Point,讓使用者察核收到的憑證是否有效的網址
{{item1{__縮短發佈撤銷憑證之時間間隔__}}} <<toBalaFlashPlayer "flash/04.3-05-enshort_distribution_time.swf" "有影片有真相" "800" "620">>
{{item1{__手動發佈已撤銷之憑證__}}} <<toBalaFlashPlayer "flash/04.3-06-manual_distribution.swf" "有影片有真相" "800" "620">>
!!教育使用者如何使用憑證
*如何使用『憑證』嵌入式管理單元
**匯出與匯入憑證
**新增信任的根憑證授權
*目的:具備法律上的不可否認性
!!數位簽章流程
*寄件者簽署
**原始訊息 → Secure Hash Algorithm → 摘要值 → 透過私密金鑰加密 → 原始訊息 + 加密後的摘要值 →傳送
*收件者檢查簽署
**原始訊息 → Secure Hash Algorithm → 摘要值 → 比對 ← 摘要值 ← 透過寄件者憑證中的公開金鑰解密 ← 加密後的摘要值
!電子商務主流——數位信封 (Digita Envelope)
*因非對性加密技術的最大缺點就是速度慢,因此採用數位信封方式
**訊息送出方系統採用對稱性加密
*ISA 安裝完成預設啟動代理快取,關閉快取服務
!!代理伺服器
*透過 ISA 作為代理快取伺服器上網有兩個好處
**安全
***隱藏內部 IP 組態
***避免內外機器直接連線
**效能
***提供快取服務
****加速網頁下載
****降低網際網路頻寬耗用
!設定直接存取與備援伺服器
*設定直接存取時機:假若有網站無法透過代理伺服器存取,或是用戶端存取網站經常會下載到過時的資料時,則允許用戶端穿越 (passthrough)
*網路 → 內部 → 內容
**位址:要符合企業內部網路網段
**網頁瀏覽器
***這個網路中的 web 伺服器不使用 Proxy:內部網路當然不使用 Proxy
***直接存取【網域】索引標籤中所指定的電腦:
***直接存取【位址】索引標籤中所指定的電腦:在位址中的 IP 都允許用戶端直接穿越存取
***直接存取這些伺服器或網域:輸入允許直接穿越的網域或網段或主機
***備援伺服器
****預設當 ISA 伺服器無法使用時,允許用戶端直接連接 Internet
****當有第二部以上 ISA Server 則可設定替代的 ISA Server
!ISA 快取功能
*啟用快取的目的
**加速網頁下載效率
**降低存取網際網路的流量
*依方向分為兩種類型
**正向快取
***快取區維護的是企業內部員工較常存取的網頁
***加速員工下載網頁的效率
**反向快取
***快取區維護的是企業外部存取公司網站的內容
***加速 Internet 使用者下載公司網頁的效率
*依架構分為:
**連鎖 (Chained):又叫階層式快取 (ISA 2004)
**分散式 (Distributed):僅企業版本支援
*支援協定:HTTP、FTP
!ISA 快取特性
*快取區包括記憶體與磁碟
*維護一個查詢的快取目錄 (cache directory)
*快取目錄具備快速還原能力:主要平常在使用的一份放在記憶體,備份的在磁碟機,若主要的毀損,則從磁碟機中取出
*採用單一快取檔案
**C:\urlcache\dir1.cdat
*提供排程自動下載功能
*自動清除能力
!啟用並設定快取大小
*預設不啟用
*快取區只能使用 NTFS 檔案系統
*越大越好,沒有例外
*避開系統磁碟機
*微軟官方建議:最低要求 100MB + 0.5MB × N
**N 最多連線人數,通常採用公司員工數量
!!建立與設定快取規則 (Cache rule)
*__快取規則__定義需要儲存在快取中的內容類型、快取內容的儲存方式及提供使用者快取服務物件的方法
*定義快取規則目的地
*快取存放與擷取方式
*HTTP 的快取設定
**設定是否啟用 HTTP 快取 TTL
*FTP 的快取設定
*限制快取物件大小
*預設兩個規則大致符合一般企業要求
**一個是微軟更新的快取服務
#規則名稱
#快取規則的目的地:一般來說是外部
#擷取內容:
##只要有有效版本的物件。... (預設):快取區有沒過期的資料,直接下載,若不存在,則直接上網際網路存取
##只要快取中存在任何版本。...:不管快取區的資料有無過期,都讓使用者下載,若不存在,則直接上網際網路存取
##如果快取中存在任何版本。...:如果快取區沒有使用者要的資料則不允許下載
#快取內容
**來源標頭提示要快取
##動態網址:只要 URL 包含 『?』與『&』就是動態內容,不建議快取
##存取的 web server 離線,送一個 302 307 訊息給反向快取,這種情況是否要快取,不建議快取
##
#快取進階設定
**不要快取大小超過以下限制的物件:免得大檔案浪費快取空間
**是否快取 SSL 回應
#HTTP 快取
**啟用 HTTP 快取 TTL
***網際網路下載的物件是否要啟用存留時間
****由 HTTP 的 Header 的 expired 欄位直接決定 TTL
****若 expired 欄位空白,則利用 HTTP 表頭內最後創造或修改日期的欄位計算其內容年齡 (content age) 在乘上組態設定的 (內容年齡的 %)
****若算出來的 TTL 小於『不少於』的時間,則 TTL 就是『不少於』的時間,若算出來的 TTL 大於『不多於』的時間,則 TTL 就是『不多於』的時間
***若勾選『也套用這些 TTL 界限到指定期限的來源』
**設定物件的 TTL:值越大,則用戶端有較大的機率從 ISA 存取到所需要的資料,也有較大的機率存取到過期的資料
#FTP 快取
**啟用後 TTL 時間固定
***國內經常出現分公司將營運資料分別在兩個 location,總公司固定在一個時間下載,則使用者會經常存取到過時的資料
****解決方案:時間縮短、或是乾脆取消 FTP 快取
!!案例說明
*若協力廠商經常有資料互換的情形,且資料變動極大,政策不允許使用代理快取功能
#目的
**選擇 URL
#快取內容:永不
#調整順序:由上而下的篩選
!快取進階設定
*快取沒有上次修改時間的物件:預設核選,若 HTTP 表頭的 expire time 與 last modify time 欄位都沒有資料,是否要快取
*快取即使沒有 HTTP 狀態碼為 200 的物件:預設核選,HTTP 狀態碼為 200 表示完全下載成功,通常是用戶端收到 4xx 或 5xx 的錯誤是否要維護在快取
**__強烈建議取消__,現在連不上不代表不存在,尤其在網路品質不佳的環境
*快取在記憶體中的 URL 大小上限 (byte):預設 12800,避免被駭客做 DoS 攻擊
*如果無法連到過期物件的網站:
**不要傳回已過期的物件 (傳回錯誤分頁):
**只有在過期日如下時才傳回已過期的物件:預設
***少於這個百分比的原始存留時間:維護在快取中物件的過期後,在過期開始算起,原始 TTL × 百分比的時間內,仍然把這個過期資料傳給用戶端
**但不多於:上述的延長期限不能超過這個值
*用於快取的可用記憶體的百分比:實體記憶體的百分比用來作為記憶體的快取區
**若這台機器沒有其他服務,可設為稍高
!ISA 鏈結 (Chained) 或 階層 (Hierarchical) 快取架構
*獨立的快取架構
|台北總公司|→|ISA|→|網際網路|
|台中分公司|→|ISA|→|網際網路|
|高雄分公司|→|ISA|→|網際網路|
*階層是架構
|台北總公司|>| → |台北ISA|→|網際網路|
|台中分公司|→|台中ISA|~|~|~|
|高雄分公司|→|高雄ISA|~|~|~|
*高雄台中的 ISA 必須指定台北 ISA 為 Upstream server (上游伺服器)
*將要求重新導向:利用於暫時禁止內部上網
*設定主要路由:指定內部網路 IP 或能解析的名稱
*備份動作 (備援):若總公司 ISA 掛點
##略過請求:回應錯誤
##直接從指定的目的地擷取要求:若不通下游自己出去
##備份路由:再指定第二台上游 ISA
!排程自動下載
*假若員工經常存取的網站,若維護在快取區中,則讓員工下載網頁的效率提高,節省網際網路頻寬
*利用公司網路無人或是少人使用的情況下,自動排程下載特定網頁內容
*內容下載工作頁籤
#輸入工作名稱
#下載頻率:多久要下載一次
#下載的時間點:上班前,與其他排程錯開
#輸入網址
**工作限制:
***不要跟隨超出所指定 URL 網域名稱的連結:例如 {{{http://tw.yahoo.com}}} 就只會下載屬於 yahoo.com 網域內的網頁內容
***每頁連結的最大深度:設定下載深度多少以內的
***限制擷取的物件數目最大:下載的物件最多數量限制
***為這個工作所建立同時 TCP 連線數目:視頻寬決定
#內容快取
**
**存留時間
***根據 expired 欄位而定
***看不懂 -.-
***覆寫物件的 TTL:(建議) 根據經驗設定一個精確的 TTL 值
!清除快取資料
#停止防火牆服務
#刪除快取檔案
#啟動防火牆服務
!ISA 防火牆功能
*建立防火牆規則
*使用應用程式與網頁篩選器
*設定入侵偵測
**功能不強
!ISA Server 2006 防火牆功能
*封包過濾
*狀態檢視
*電路閘道 - SOCKS
**僅支援 SOCKS 4,因此只支援第四層代理
**預設不啟動
*應用層過濾
*入侵偵測
**TCP/IP 弱點攻擊
**DNS、POP3 應用層攻擊
!三種規則類型
*網路規則
**定義兩個網段的傳送關係:NAT 或 路由
*系統原則規則
**定義 30 個內建的規則
*防火牆原則規則
**管理員自行定義的防火牆規則,分為存取規則與發佈規則
**存取規則
**發佈規則:透過 ISA Server 把 Internet 請求轉送到內部網路 (即 Virtual Server)
!ISA 2006 防火牆存取規則處理
#檢查是否存在來源與目的網段是否存在任何網路規則 (NAT or Route)
**若有則繼續
**若無則丟掉
#處理系統原則規則
#處理防火牆原則規則
#最後在依據網路規則來決定流兩如何處理
!網路規則
*路由不會變更來源位址,A 能路由給 B,B 也能路由給 A
*NAT 會變更來源位址,NAT 為單向關係
!ISA 2006 預設網路規則
*ISA Server 與任何網段為路由關係
*VPN 用戶端與內部網路為路由關係
*內部網域和 VPN 用戶端與外部網路為 NAT 關係
!!注意:未來加入任何網段時,必須先建立網路規則,若所屬的網段沒有任何網路規則時,則封包將會自動被丟棄
!ISA 2006 預設網路與規則
*內部:所有指定的 IP
*外部:包含所有不與任何其他網路有關係的 IP
*VPN 用戶端
*隔離的 VPN 用戶端:預設尚未啟用
*本機主機:代表所有 ISA Server 電腦,本身就是獨立的網路
*一個或一個單位以上的網路可以設定為網路組
!防火牆存取規則結構
|action|on __traffic__|from __user__|from __source__|to __destination__|with __conditions__|
|allow<br>deny|1. Protocol<br>2. IP、Port、Type|1. User<br>2. Group|1. Src network<br>2. Src IP|1. Dst network<br>2. Dst IP<br>3. Dst site|1. Schedule<br>2. Content type|
!網路物件
*通常用在存取規則中用來控制或限制來源或目的
**網路:包含內建的內部、外部等網路單位,與自訂的網路單位
**網路組:一個或一個單位以上的網路組成
***預設的網路組
****所有受保護的網路:使用排除條款,排除外部的網路,意即除了外部以外的網路都稱為受保護的網路
****所有的網路 (與本機主機):使用排除條款,什麼都沒排除,所有的網路單位都屬於所有網路
***建立網路組:可選擇以包含 (include) 或是排除 (exclude)
**電腦:針對特定 IP 位址
**位址範圍:特定 IP 範圍
**子網路:特定的網段
**電腦組:一個或一個以上的單一 IP 位址、網段、或位址範圍
**URL 組
**網域名稱組:可搭配 * 萬用字元,但僅能放在最前面,放其他位置都沒用
!通訊協定
*ISA 2006 Server 2006 以定義好 Internet 上常用到的通訊協定
*並未替其他建立通訊協定,例如 Oracle SQL
*次要連線:例如 FTP、PPTP、串流媒體流量都有次要連線
*X Window:使用 XDMCP,UDP (接收) 6000 - 6063,無次要連線
!建立內容類型
*內容僅限於 HTTP 與 FTP 流量使用
*利用 MIME type 或檔案副檔名來建立內容類型
!排程
*在規則中以時間控制存取規則,則需要建立排程時間
!使用者
*利用使用者或群組來控制存取
*可新增本機使用者和群組、LDAP、RADIUS、SecurID
**LDAP 與 RADIUS 需要額外的定義
***設定 → 一般 → 指定 LDAP 及 RADIUS 伺服器
**SecurID 需購買額外的軟硬體
!透過防火牆規則架構,事後根據現實網路環境的變更,若防火牆規則有需要變更時,不需要整條規則砍掉,而找出需要修改的部份加以變更即可
*管理員建立規則後,允許停用或移除
**內建的系統原則能做部份修改,但無法停用或刪除
*建立規則名稱,使用明確而能一目了然的名稱
*ISA 規則清單由上往下搜尋符合連線的規則來強制處理
*管理員能變更自訂規則順序,但系統原則無法變更其順序
*先處理系統原則,再處理管理員自訂規則
*三種規則元素『從』、『到』、『使用者』可做例外處理
!ISA 防火牆存取規則套用規則的順序
#通訊協定
#來源條件
#來源時段
#目的條件
#User-level
#內容類型
#允許或禁止
#應用層篩選器
*中間只要有一關不符合就丟棄
!建立存取規則的順序要點
#首先建立伺服器與網頁發佈原則
#其次建立匿名存取規則 (先拒絕再允許)
#最後建立身份驗證規則 (先拒絕再允許)
!設定允許存取網際網路
*設定 ISA Server 2006 這台電腦可以上網
*開放企業內部使用者可以存取網頁服務 —— HTTP、DNS
*設定樹系內網域控制站可以存取網際網路對時服務 (NTP、SNTP Service)
*限制企業內員工在上班時段內禁止傳輸 DVD/CD 映像檔
**排程與內容類型需在內容對話盒中設定
**這個規則必須在允許內部員工上網之前
*允許老闆在上班時間下載 DVD/CD 映像檔
**可以在『從』的標籤頁中排除
**或是新增一條規則
*禁止企業員工存取不良的網站
**簡單的方式:在允許員工存取外部網頁的目的中加入排除,加入 URL 組或是網域名稱組的網路物件
*設定只允許公司管理人員使用 MSN
**考慮以 User-level 或是以 Host-based 允許
***User-level 較安全、Hot-based 速度較快
*允許用戶端使用 Ping 查詢外部主機
!疑難排解 - 流量模擬
*ISA Server 2006 SP1 提供
!ISA 應用層篩選器
*ISA 2006 的應用層篩選器大多在處理
##複雜的協定:不是放行或攔阻特定 TCP、UDP Port 就能達到存取規則
##內容過濾
***僅 HTTP 的內容過濾,SMTP 的內容過濾被拿到 Forefront
##特殊驗證方法
!應用程式篩選器
*複雜的通訊協定
**FTP存取篩選器:
**H.323 篩選器:早期視訊會議與網路電話協定,目前新的協定為 SIP,微軟並未支援
**MMS、PNM、RTSP 篩選器:串流媒體
**RPC 篩選器:支援 RPC 動態連接埠協定
**PPTP 篩選器:經由 ISA Server 的 PPTP 通道
*--SMTP 篩選--:自 ISA 2006 移到 M$ Forefront Gateway,ISA 2006 內僅保留 SMTP 命令
*SOCKS V4 篩選器:做代理連線用途,若企業內有非微軟的作業系統,且要讓這些電腦存取 Internet 較複雜的協定,才需要開啟這個功能
**僅適合 TCP 複雜的協定
*DNS 篩選器、POP 非法入侵偵測篩選器:作 IDS 用途
**DNS:針對 DNS 伺服器偵測是否有外部入侵攻擊事件
**POP:針對 POP 伺服器偵測是否有外部入侵攻擊事件
!網頁篩選器
*處理網頁效能相關
**DiffServ 篩選器:與 QoS 相關
**壓縮篩選器
**快取壓縮內容篩選器
*提供較複雜的協定驗證功能
**驗證委派篩選器
**表單型
**RADIUS
**LDAP
*其他
**網頁發行負載平衡篩選器:負責負載平衡的篩選器
**連結轉譯篩選器:Virtual Server 相關的篩選器
*HTTP 篩選器:最重要的 HTTP 內容篩選器
!HTTP 篩選器
*可以用來過濾 (過濾其內容) 進出的 HTTP 流量
*基於每一條 『HTTP』 相關的『允許』存取規則加以設定
*設定規則的 HTTP 原則:越右邊的標籤頁越有效,但也越複雜
**一般:針對 HTTP 表頭做限制,避免遭受 DoS 的緩衝區溢位攻擊
***要求標頭:長度上限:預設 32768 位元組
***要求裝載 (Payload):避免內容過大 HTTP 封包
***URL 保護
****長度上限:
****查詢長度上限:不能超過『長度上限』
****確認正規化:將 URL 的跳脫字元『%』還原其原意兩次,以避免被駭客使用跳脫字元隱藏依些可能會被偵測的字元
*****強烈建議使用
****封鎖高位元字元:封鎖 ASCII128 以後的字元,不適合用在中文環境,不做!
***可執行檔
****正常的 EXE 檔開頭為 MZ,藉由檢查檔案內容開頭可以做到『封鎖含有 Windows 可執行內容的回應』
*****用途不大
**方法:透過 HTTP 的 10 個 Method 篩選封包內容,通常針對網站發佈功能篩選由外部連進內部的請求流量
***GET、HEAD、POST、PUT:通常放行這幾個 Method 即可
***TRACE、DELETE、CONNECT:常用來做診斷用途的 Method,卻經常被駭客拿來誤用
**副檔名:提供網頁篩選過濾或允許特定副檔名的要求
***封鎖含有不明確副檔名的要求:是否在 MIME type 內並未定義的要求要被封鎖
**標頭:針對 HTTP 標頭過濾
***允許下列以外的所有標頭 (在清單以外的是被允許的)
****可設定針對要求標頭或是回應標頭過濾
****範例:要求標頭:P2P-Agent
***伺服器標頭:移除或修改『伺服器標頭』,以避免軟體名稱與版本訊息曝露
***Via 標頭:通常是代理伺服器插入的一個標頭欄位 (通常為 Proxy 的機器名稱),藉由通知用戶端這個請求是由 Proxy 處理的,若不想讓外界得知這個欄位內容,則啟用這個選項
**簽章:針對 URL、HTTP Header 或資料段內是否有特定字串做過濾
***按照 IETF 的公告,除了網頁應用程式外其他應用程式若需要走 TCP/80 連接埠,必須在表頭內有可供辨別的標示
!!範例說明:
*2001 年 Nimda 蠕蟲,篩選以下條件
**副檔名:.exe
**簽章 (要求 URL):Directory Travelsal 『 .. 』
*2002 年 Code Red 蠕蟲,阻絕 .ida
*禁用 Windows Live/ MSN 三招
##要求表頭 User-Agent:,簽章內容:Windows Live Messenger 、MSN Messenger、webmessenger.msn.com
##使用 application/x-msn-messenger 來控制 MSN 流量
##找出國內 MSN 連線的伺服器主機位址,直接封掉
*封鎖 BitTorrent
**要求表頭 User-Agent: 簽章 BitTorrent
*FTP Filter
**阻止存取 FTP 伺服器的流量
**允許只能唯讀的存取 FTP 伺服器
**允許可下載及上傳 FTP 伺服器檔案
!!SMTP 篩選器
*僅剩下 SMTP 命令
*每個命令都有長度限制:避免被駭客做緩衝區溢位攻擊
**微軟已經預設設好了,沒有經驗使用預設值即可
*有幾個命令常被駭客攻擊:EXPN、VRFY,停用!!
**Exchange 2007 預設不收這兩個命令
!ISA 入侵偵測
*功能不多
*TCP/IP 弱點入侵偵測
**Windows out-of-band (WinNuke):利用連續對目標發送大量『URGENT (緊急)』封包,會被伺服器優先處理,造成其他正常連線的封包無法處理
**Land Attack:假造來源目標位址相同,且來源目的連接埠相容的封包傳送,以企圖當掉主機
**Ping of death:利用過大的 ICMP 請求封包造成緩衝區溢位
**IP half scan:一種隱匿性掃描技術,避免被紀錄,利用傳送 SYN 封包,當收到 SYN/ACK 封包回應後不回送 ACK 封包
**UDP bomb:
**Port scan (預設未啟用):當收到樹個不同指定的連接埠連接封包後,會視為 Port scan 中斷掉
***知名連接埠 (~2048)
***所有連接埠(2048~)
*應用層入侵偵測
**DNS
**POP
!!ISA 入侵偵測處理方式
*丟棄封包
*紀錄
*警示 (需設定)
!!設定 IP 保護
!!!提供彈性的 IDS 設計,避免正常使用網路被當作是攻擊事件
*IP 選項
**需要靠經驗來實作
*IP 片段 (Fragment)
**封鎖 IP 片段:只要 IP 封包內 Fragment ID 不為 0 的封包就丟棄,不要勾!!除非能確定 IP 封包在網路上傳送不會被切割
*IP 路由
**啟用 IP 路由:啟用後速度較快,但有可能原始的表頭會夾帶攻擊命令
***不勾選:速度較慢,但較安全,做法是將一份標準的表頭覆蓋到原始的表頭位置,以避免夾帶攻擊命令
!!DNS 攻擊入侵偵測
*DNS 主機名稱溢位:DNS 名稱長度限制 255,若有人送出名稱超過 255 個字元的正向解析請求,則過濾掉
*DNS 長度溢位:作 DNS 反解析時,送來超過 4 bytes 的逆向解析請求,則過濾掉
*DNS 區域轉送 (預設未啟用):若有人對公司內部網路攻擊前,通常會先對 DNS Server 作 Zone transfer,企圖將整個 DNS 的 zone 傾倒出來,因此極建議過濾
!ISA 2006 洪水防護功能
*ISA 2006 新增功能
*IP 例外狀況:通常用在比較特殊的連線,或是在做壓力測試的時候會將管理員的機器加入例外
**設定電腦組
!設計較複雜的防火牆架構
*微軟 ISA 2006 建議採用的防火牆架構
**邊緣防火牆 (Edge Firewall):雙介面主機防火牆、單層防火牆
**外圍三向防火牆 (3-Leg):三介面主機防火牆、提供建置 DMZ (Perimeter Network) 可能性
***DMZ 區 IP 考慮:Private 或是 Public IP
**背對背防火牆 (Back-to-Back):2-Tier 防火牆
***DMZ 區 IP 與前端防火牆內部 IP 與後端防火牆外部 IP 屬於同一個網段
**單一網路介面卡:僅能當作代理伺服器提供快取功能,而沒辦法提供防火牆功能
!透過範本精靈改變防火牆架構
*改變架構後,原先的原則規則設定將會取消
!!變更架構為外圍三向
*週邊至外部網路預設為路由關係,以國內的現況而言,應該變更為 NAT
**除非週邊能取得 Public IP
!!變更架構為前端防火牆
*外部為 Internet
*內部為周邊:即 DMZ 區
!!變更架構為後端防火牆
*內部為 Intranet
*外部為 周邊 + Internet
!防火牆的虛擬伺服器發佈問題
*透過位址轉譯的方式安全性的將內部網路伺服器提供給 Internet 使用者來加以存取
**禁止 Internet 使用者直接連接內部伺服器
**Internet 使用者不會知道內部伺服器真正的位址
*透過設定發佈原則 (虛擬伺服器原則)
**學理上常用 Port redirect
!ISA 2006 伺服器發行
*複雜,為了支援自家產品的專屬性協定
*發行網頁伺服器
**HTTP
**HTTPS
**SharePoint 網站
*發行郵件伺服器
**基本郵件協定
**Exchange Server
*發行其他伺服器
!ISA 2006 網站發行功能特性
*提供代理存取至內部網站的防火牆保護
*可以執行深度的 HTTP 應用層檢視
**對從外部連進的 HTTP 要求,應用層篩選一樣有效
*允許路徑轉送 (Path Redirect)
*提供連線前預先驗證能力 (Pre-authentication)
*支援特殊的驗證方法
**一次密碼
**RADIUS + LDAP
*提供反向快取 (reverse caching) 功能
**可提升 Internet 存取內部網站的效率
*支援單一 IP 位址發佈多部網站
**不需要換 Port
*提供連結轉譯 (Link Translation) 功能
**主要為了避免 broken link
*可支援傳送 ISA 的 IP 或原始用戶端的 IP 至內部網站
*支援利用排程時間控制允許存取的時段
*允許轉換連接埠
*憑證限制原則 (Certificate Restriction Policies)
**可限制用戶端與伺服端憑證的原則
!使用發行精靈程式
*Exchange 發行規則精靈
*郵件伺服器發行規則精靈
*網頁發行規則精靈
!網站發行類型
*發行單一網站
**內部網站名稱:給內部用戶端識別的名稱,通常避免混淆都使用 Internet 的網站名稱
**轉寄標頭:是否保留原始用戶端的表頭,Web Server 管理員在紀錄檔中才看的到原始的用戶端資訊,除非有特殊原因,不然都會核選
**公用名稱:Web Server 在網際網路註冊的正式 FQDN
**網頁接聽程式:幫 ISA Server 建立一個 Listen 80 port 的程式
***Web listener 運作在 ISA 底層,專門監聽 80/443 Port 的請求,以便轉送至內部
****要使用 443,ISA 必須要有有效的憑證
****ISA 2006 新增壓縮功能,預設啟動
****ISA 2006 表單式驗證可支援 IIS Server,2004 僅支援 Exchange Server
**ISA 2006 SP1 新增測試功能,在動作標籤頁內
*發佈大型的複雜網站伺服器
**不同的 URL 會被對應到不同的內部網站伺服器 (Path Mapping)
***通常為第一層目錄名稱
***先建立根目錄
***再建立次目錄
*發布多部獨立的網頁伺服器
**只使用一個 IP 就發佈兩部不同的網站
***利用公用名稱就可以使用相同的 Port Number
**可以一次一次使用發佈單一網站精靈,或是使用一次發佈多個網站,但使用一次發佈多個網站時,需額外在該條防火牆規則內容對話盒中加入所發佈網站的內部 IP 位址
**若有使用者會使用 IP 連入公司網站時,則必須在公用名稱標籤頁下『網站和 IP 位址』中新增『外部合法』 IP 位址
!!Port Redirect
*防火牆原則項目的內容對話盒,橋接標籤頁中
!!連結轉譯 (Link Translation)
*若公司網頁內的 URL 經常會以主機名稱為網址,例如 http:/ /mail1/owa,造成網際網路使用者收到 404 錯誤代碼時,則建議開啟該選項,透過網頁篩選器中的『連結轉譯篩選器』將 link 改寫為 http:/ /mail1.flatfish.com/owa
*設定頁面在防火牆原則項目的內容對話盒,連結轉譯標籤頁中
**另外,建議開啟萬國碼支援功能,以提供中文網址的支援,『也使用這個字元集套用連結轉譯到網頁內容』
!!SSL Bridging
*防火牆作為 SSL 的端點,針對 SSL 封包執行加密與解密,所以本身需要有 SSL 有效憑證,可以做應用層篩選過濾
##申請有效憑證:伺服器憑證,且申請的名稱與與主機名稱相符合
##Web listener 傾聽 443 port,且選擇正確的憑證
##選擇發佈網站原則
***選擇新增規則,或是修改既有規則
##選擇是否同時接收 80 與 443 連接埠的要求
***使用憑證來驗證 SSL Web 伺服器:建議不開啟,不然 ISA Server 與 Web Server 間要再次申請一個 ISA Server 的用戶端憑證做雙向驗證
##選擇是否將 80 的要求轉向 443 使用 HTTPS
!!SSL Tunneling
*防火牆只是單純的轉送封包,並不會對封包加密與解密,單純的作 Frowarding,所以不需要憑證,也因此不能使用應用層篩選過濾
**選擇發佈其他類型伺服器
!網站陣列 (Web Server Farm)
*ISA 伺服器不需要在使用昂貴的 L4 Switch 或是彆腳的 TCP/IP 負載平衡下,提高網站伺服器可用性、效能,網站負載平衡、容錯能力
*ISA Web Server Farm 自動追蹤伺服器功能
**Active
**Draining
**Removed
**Out of service
#建立網路物件:伺服器陣列
**陣列中的內部服務主機需自行維護同步功能
**選擇 ISA 伺服器追蹤伺服器使用的方式
***建議使用 GET,最適合用在 Web Server farm 追蹤
**伺服器陣列建立後,會自動新增一個防火牆原則,允許 ISA 向網站陣列抓取資料
#發佈網站伺服器
**選擇以何種方式維護負載平衡連線
**以 cookie 為主:用戶端不能停用 cookie
**以 IP 為主:用戶端若透過 NAT,可能會出問題
!發佈電子郵件
*發布基本的電子郵件服務:SMTP、POP3、IMAP
*設定 ISA SMTP 篩選器 (僅剩下命令篩選功能)
*支援特殊的 Exchange Server 非標準的用戶端連線方式
**MAPI (RPC)
**OWA
**RPC over HTTP(S):Outlook anywhere
!發佈基本的電子郵件服務:SMTP、POP3、IMAP
*通常為了安全性考量會只發佈 SMTP
!發佈微軟 Exchange OWA
*Exchange Server OWA 讓使用者經過瀏覽器可以用來收發電子郵件甚至安排行程 (Outlook 的 Web 版本)
**OWA (HTTP)
**OWA + SSL (HTTPS)
**Form-based OWA (HTTPS)
*不同版本的 Exchange,使用不同的發布方式,因此要選擇正確的 Exchange Server 版本
**僅有 bridged 模式
!使用 OWA 表單式驗證 (Form-based)
*表單式驗證只能做一次,因此要決定是由 ISA 作表單式驗證或是由 Exchange 作表單式驗證
**因為表單式驗證無法做委派
!Outlook 用戶端使用 MAPI 存取 Exchange Server 的問題
*好處
**擁有完整豐富的電子郵件及協同作業功能
*缺點
**MAPI 使用的 RPC 為一動態連接埠的設計,不易通過防火牆
**RPC 本身有不少安全性弱點,網際網路上易遭受攻擊
!支援 MAPI 用戶端 (Outlook) 的方法
*Outlook 使用 RPC 協定連線,為一動態連接埠的協定,使得防火牆的規則設定困難
*解決方法:
**非 ISA 防火牆
***需放行 high port (1024 以上)
***在 DC 與 Exchange Server 上的登錄資料庫設定使用固定的連接埠
***Exchange 使用 RPC over HTTPS
**ISA 防火牆
***啟用 RPC Filter 並設定 Outlook RPC 發佈規則與 RPC 加密
####設定發佈
####RPC 加密
*****需與用戶端搭配,Outlook 2007 預設加密,Outlook 2003 預設不勾,需在 Profile 中勾選 Outlook 與 Exchange server 之間傳輸加密
***Exchange 使用 RPC over HTTPS
!發行 Outlook Anywhere (RPC over HTTPS)
*使用『 Exchange 發行規則精靈 』
!伺服器發佈與網頁發佈不同點
*伺服器主要在發佈非 HTTP(s) 伺服器
*不作代理存取與快取
**例如 DNS、NNTP、RDP
*無法作 user-level 驗證
*無法作 port redirect
!發佈的伺服器本身一定要是 SecureNAT 用戶端
*自 ISA 2004 才開始大幅強化 VPN 功能
!VPN 連線類型
*Client-to-Site VPN
*Site-to-Site VPN
!ISA 2006 VPN 通道協定
*只比 RRAS VPN 多了一個:IPSec VPN
**只支援 Site-to-Site VPN
!Client-to-Site
*啟用 RRAS VPN,套用後 ISA 會自動完成以下動作
## 啟動 RRAS VPN
##啟用『允許到 ISA Server 的 VPN 用戶端流量』系統原則規則
##內定僅啟用 PPTP(100) 伺服器
##新增一個 RAP (遠端存取原則)『ISA 預設原則』
##只支援 MS-CHAPv2 驗證
*雖然底層仍然是 RRAS,但日後不能對 RRAS 直接變更,因為變更後 ISA 並不會知道
*為 VPN 使用者建立一個群組或是在直接允許使用者撥入或 VPN
*若非 Windows 平台,且使用 RADIUS 驗證,要開啟使用者對應功能,並補上 Domain Name,才能驗證成功
!建立 VPN 用戶端存取規則
*預設上,VPN 用戶端無法存取資源,因為尚未替他們建立一個存取規則
*建立一個適合用於 VPN 的防火牆規則 (影片中採用最寬鬆的作法,不適合用在實務環境)
!Site-to-Site VPN (Route-to-Route)
*RRAS 稱為 Demand Dial
**IPSec 通道模式
**L2TP 通道協定
**PPTP 通道協定
*網站間網路名稱:輸入的名稱在本地的 ISA 伺服器內必須要有相同的帳號
!!建立遠端網站網路預設動作
*如使用 L2TP / IPSec 或 PPTP 協定,則會在 RRAS 內建立
**一個『網站間網路名稱』的指定撥號介面
**新增連結至遠端網路的靜態路由
!VPN 用戶端隔離
*VPN 用戶端隔離功能,讓 VPN 用戶端連接至 ISA 伺服器成功後,會先被歸屬到『隔離 VPN 用戶端』,等到用戶端通過檢查後 (例如是否安裝 Service Pack),才會被歸屬為『VPN 用戶端』
**Cisco NAP (Network Access Protection)、MS NAC (Network Access Control)
*只要違反企業安全政策的電腦就不准使用企業內部網路
*例如 VPN 用戶端隔離可以幫助檢查 VPN 用戶端
**是否安裝 SP 或修補檔
**是否安裝並啟用防毒程式
**個人防火牆是否安裝並啟用
*VPN Quarantine Process
##VPN 用戶端連線驗證成功
##歸屬到 VPN 隔離區
##此時僅放行基本受限制的資源,通常為 DHCP
##檢查用戶端是否符合企業安全性規定
##通過後則歸屬到『VPN 用戶端』,並允許使用公司內部資源
***若檢查未通過,則在 Timeout 過後仍未收到檢查成功的訊息將被 ISA Server 強制斷線
!VPN 用戶端隔離元件
*伺服端必須安裝 RQS.svc 服務
**專門聆聽 TCP 7250 的流量
*用戶端電腦必須安裝 RQC.exe,以及管理員針對企業安全性政策撰寫的 Script 檔
**透過 Connection Management Administration Kit (CMAK) 將 RQC.exe 與 Script 打包
#啟用隔離控制
**通常都使用 ISA Server 原則通道協定進行 VPN 用戶端隔離
**根據網路設定『中斷連線隔離的使用前的等待時間』
**設定排除檢查人員:例如老闆、高階主管
#安裝遠端存取隔離服務
#安裝『連線管理員系統管理組件』(在 Management Monitoring Tools 中)
#到微軟網站下載 Remote Access Quarantine Tool for ISA Server 2006 (RQSutis.exe),並解壓縮出 ConfigureRQSForISA.vbs,並修改部份變數名稱,以符合使用的中文版 ISA 環境
#執行 Cscript ConfigureRQSForISA.vbs /install key,將遠端隔離服務整合至 ISA Server 中
#準備適合的 Script 檔,可自行撰寫或是上微軟網站找適合的 Script 檔
**針對要檢查的項目撰寫
#使用 CMAK (連線管理員系統管理組件) 將 Script 與 RQC.exe 打包
##新增設定檔,給予一個服務名稱與檔案名稱
##VPN 支援:填入『永遠使用相同的 VPN 伺服器』,通常為 ISA Server 的外部介面 IP
##取消自動下載電話簿更新
##自訂操作:新增,執行程式選擇要執行的程式,操作類型選擇『連線後』,若 Script 有參數要自行加入
##其他檔案:加入 RQC.exe (在 c:\program files\cmak\support 內),與其他在 Script 中需要的 batch 或是 vbs 檔)
##將 c:\program files\Cmak\profile\//服務名稱// 整個資料夾交給用戶端,並請用戶端執行自訂的檔案名稱
#建立 VPN 用戶端、隔離的 VPN 用戶端與內部網路的存取規則
!防火牆如何放行 VPN 流量
*PPTP
**TCP 1723
**IP 通訊協定 ID 47 (即 GRE - 通用路由封裝)
*L2TP
**UDP 1701
**UDP 500 (IKE)
**IP 通訊協定 ID 50 (IPSec ESP)
**IP 通訊協定 ID 51 (IPSec AH)
*使用 ISA 的 VPN 建立精靈程式會自動建立這些允許的規則
!!發佈 PPTP VPN 伺服器
*透過 PPTP 篩選器,發佈 PPTP VPN 伺服器變得簡單
!!發佈 L2TP/IPSec VPN 伺服器
#發佈 IKE 伺服器
#發佈 NAT-T 伺服器
#發佈 L2TP 伺服器
*安裝『遠端存取隔離服務』和『連線管理員系統管理組件』
*啟動遠端存取隔離服務 (remote access quarantine agent)
*遠端存取原則 設定檔內進階標籤頁 新增 MS-Quarantine-IPFilter (放行 UDP 67、68) 與 MS-Quarantine-Session-Timeout (設定等待的 Timeout 時間)
!ISA Server 效能相關功能
*HTTP 壓縮
*差異服務 (DiffServ)
!HTTP 壓縮
*HTTP 1.1 協定
*使用 GZIP 及 Deflate 演算法
!ISA 2006 的 HTTP 壓縮
*壓縮篩選器
**負責壓縮及解壓縮 HTTP 要求與回應
**高優先順序
*快取壓縮內容篩選器
**負責快取壓縮的內容
**以快取的壓縮內容來服務要求
**低優先順序
!差異服務 (DiffServ)
*透過 QoS 服務,針對特定網址可以優先傳送
*兩方傳送的端點需要支援 DiffServ 之外,中間經過的網路設備也都需要支援 DiffServ (QoS)
*可在 URL 末端或是網域名前端使用 『 * 』
*可啟用或停用特定網路的 DiffServ,來選取對哪些網路的封包指定優先順序 (disable by default)
#交換原理
#Spanning - Tree
#VLAN
#VTP
!優缺點
*優點
**彈性大
**適性強
**安裝易
*缺點
!WLAN的區域
*PAL
*HotSpot
*HotZone
*Wi-Fi Building
*Wi-Fi City
*Wi-Fi State
!無線網路類型
!!依範圍分類
WPAN
WLAN
WMAN
WWAN
IBSS
BSS
ESS
DS
BSSID
SSID
Ad-hoc Mode
Infrastructure Mode
!ISA 伺服器一般性管理功能與工作
*警示 (Alerts)
*日誌報表 (Logging and Reports)
*線上即時監控 (Sessions)
*連線能力監控 (Connectivity)
*備份與還原 (Backup and Restore)
**匯入與匯出 (Export and Import)
!儀表板
!!設定重新整理頻率
*低:120 秒
*中: 60 秒
*高: 30 秒
!警示 (Alert)
*認可 (Acknowledge)
**當管理員看到了解後,有保留必要,在儀表板會看不到
*重設 (Reset)
**刪除
*ISA 電腦重新啟動,或防火牆服務重新啟動後,會重設所有警示
!!自訂警示事件
*提供五種處理方式
**傳送電子郵件
**執行程式
**紀錄到事件檢視器
**停止所選的 ISA
**啟動所選的 ISA
*設定觸發方式
**不設代表發生一次就觸發
!管理工作階段
*可看到 ISA Server 目前線上連線狀況
*有使用 User-level 驗證的使用者名稱匯出現在用戶端使用者名稱
*即時的資料
**可暫停、停止監視工作階段
***暫停:暫時停止 Refresh
***停止:停用監視工作階段功能,將看不到任何工作階段
!服務
*顯示目前與 ISA Server 緊密相關的服務
*僅能停止與啟動
!設定連線能力檢查器
!ISA 紀錄檔
*防火牆紀錄
*網頁 Proxy 紀錄
*紀錄類型
***Miicrosoft SQL Server 2000 Desktop Engine (MSDE 2000) 資料庫
****資料庫大小限制 2 GB
****只有這個紀錄檔類型可以在 ISA 紀錄中查詢
****要匯入至其他程式 (例如 Excel),可透過複製貼上轉換為純文字
***檔案
****全球資訊網協會 W3C 格式
*****以 tab 分隔
*****日期時間為 UTC
****ISA Server 格式
*****使用逗號分隔
*****日期時間為當地時間
***SQL 資料庫
****按照規定,防火牆的紀錄應該存在遠端
*****網際網路標準:Syslog、SNMP Trap
******微軟作業系統支援 SNMP 用戶端 (Agent),伺服端需額外購買 MOM、SMS
*******用戶端主動將網管訊息傳送給伺服端:Trap
*******伺服端主動向用戶端要求網管訊息:Get
*******透過 OID 傳送
********OID 為樹狀結構,SNMP MIB Tree,例如 .1.2.3.4.131.......
****僅有存在 SQL Server 能存放在遠端機器
****原版光碟 \FPC\Program Files\Microsoft ISA Server\fwsrv.sql 與 w3proxy.sql 定義好了表格中各項欄位
****強制資料加密需 MS SQL 2005 以後 (非 MS 也不行)
****驗證詳細資料必須要對於該資料庫有寫入以上權限
!ISA 2006 五大報表
*摘要
**最有用
*網頁使用方法
*應用程式使用方法
*流量和使用量
*安全性
!產生 ISA 報表
*立即產生
*排定時間定期產生
**強烈建議每天定期執行,一天產生一份報表
*發佈報表:將報表存放置一個共用資料夾,並設定適當權限給要開放的對象
*每天產生的報表無法指定產生報表的時間,預設 00:30 產生 log summary,01:00 產生報表
!變更追蹤
*ISA Server 2006 SP1 新增功能
*讓管理員變更組態設定時,紀錄下來並輸入描述,以便日後追蹤是否有不當或未經授權的變更
*需先啟動該功能
!鎖定模式 (Lockdown mode)
*類似硬體防火牆的緊急鍵:在企業招受攻擊時,按下緊急鍵可攔阻任何連入的流量
*當發生造成 Microsoft Firewall 服務關閉時 (不是管理員正常關閉的情況下,不管是意外造成或有心人士造成),即會進入鎖定模式
**此時只有遠端管理電腦組內的電腦流量會被放行
**自己本身連外的流量會通
*CSS (ADAM) 伺服器:儲存所有 ISA 伺服器組態
*DC 提供 ISA 伺服器驗證
*陣列方式集中儲存管理伺服器組態
*支援 Internet 新的快取標準:CARP
*支援微軟的 Network Load Balance
陣列方式集中儲存管理伺服器組態
!設定存放區伺服器 (Configuration Storage Server)
*用來儲存企業內所有 ISA Server 的組態、規則 (包括防火牆規則、快取伺服器規則、VPN 規則)
*設定存放區伺服器使用 ADAM (Active Directory Application Mode)
*從 ISA Server 2006 Enterprise Edition 光碟安裝時,會將 ADAM 自動安裝
**CSS 會維護一個與 LDAP 相容的資料庫,但又不需要加入 Domain
***LAB 環境中,有可能讓 ISA Server 加入網域,但不是絕對必要,主要是為了讓機器能互相驗證
*可與 ISA 伺服器共存在同一部伺服器下或不同伺服器,不建議裝在同一部
*支援備份或還原
!建置與管理 ISA 企業版
*安裝 ISA 2006 企業版
**安裝 ISA 設定存放區 (CSS)
**建立 ISA 陣列 (Array)
**安裝 ISA 伺服器服務
!考慮 CSS 驗證方法
*當 ISA Server 每 15 秒向 CSS 抓取資料時,都需要機器驗證
*驗證方式
**Windows 驗證
**需架 AD
**將 ISA Server 加入『同一個』網域
**使用 SSL
***需先申請伺服器憑證,並將其匯出為 pfx 檔 (含私密金鑰),安裝的時候載入
***此憑證需具備嚴謹條件:
****伺服器類型憑證
****由可信任的 CA 發行
****可匯出私密金鑰的格式
****申請名稱必須要是電腦名稱
!安裝設定存放區伺服器
*使用 Domain Admins 群組內的使用者帳號
!建立 ISA 陣列
*使用 Domain Admins 群組內的使用者帳號
*安裝 ISA 伺服器前,先創造 ISA 陣列
*直接在安裝第一部 ISA 伺服器時,同時建立 ISA 陣列
*建立 ISA Array 時,微軟預設使用機器名稱當作陣列名稱,極不適合,強烈建議要修改,以避免主機名稱與陣列名稱混淆
!根據實驗環境設定規則
#網路物件 - 電腦組 - 陣列伺服器:新增第二部 ISA Server
#(OPT) 建立防火牆原則:若 ISA 裝在 CSS 與 DC 同一部,需建立允許『來源:陣列伺服器』到『目的:本機』所有流量,(DC 流量複雜)
!ISA 2006 陣列與陣列成員
*包含以下電腦的群組
##一或多部儲存 ISA Server 資訊的 CSS
##一或多個陣列成員,安裝 ISA Server 的電腦
##一或多部安裝 ISA Server 管理的遠端管理電腦
!相同陣列成員條件
*所有成員必須擁有以下相同的設定
**相同的網路卡數目
**相同的時區
**相容的應用程式及網頁篩選器 (包含設定與類型,例如 Third party 篩選器,全部都要有)
**安裝相同的憑證
**相同的 AD 網域『與』站台或相同的工作群組
*ISA 陣列成員無法日後搬移
!手動建立 ISA 陣列
!修改 ISA 陣列內容
*只有 Enterprise 系統管理員層級能修改
*原則設定值
**陣列防火牆原則規則類型
***拒絕
****[禁止 / 允許] 陣列管裡員創造拒絕的存取規則
***允許
****[禁止 / 允許] 陣列管裡員創造允許的存取規則
***發佈規則 (拒絕及允許)
****[禁止 / 允許] 陣列管裡員發佈公司內部伺服器
**當陣列管理員已建立拒絕或允許的存取規則時,無法再將其建立拒絕或允許規則的權限移除,除非陣列內的拒絕或允許規則移除
*設定存放區
**可設定備援 CSS
**檢查設定存放區伺服器更新的間隔:預設 陣列中每部 ISA 伺服器每隔 15 秒會向 CSS 抓取設定
**選擇 ISA Server 與 CSS 間的驗證方式
!有效陣列原則的運作方式
*防火牆原則會依下面的順序處理
##陣列系統原則規則 (預設隱藏,且無法更動)
##陣列之前企業原則規則 (企業管理員管理)
##陣列防火牆原則規則 (陣列管裡員僅管理這個規則)
##陣列之後企業原則規則 (企業管理員管理)
!
!快取陣列路由通訊協定
*目的:透過分散式快取功能,將網頁快取內容分散在多部 ISA Server 中
*方式:用戶端向 Proxy Server 送出網頁 URL 請求,Proxy Server 收到請求時,將 URL 以雜湊函數計算出摘要值,將摘要值除以 Proxy 伺服器數目取餘數以得知該 URL 的快取該由哪一部伺服器負責,而由接到請求的 Proxy Server 向計算出負責該 URL 的伺服器查詢網頁內容,負責該 URL 的伺服器則檢查自己的快取區有無資料,若無,則上網際網路下載,並傳回網頁內容給接收請求的 Proxy Server,再由該 Proxy Server 將網頁內容傳回給用戶端
!CARP 例外
*某些特殊網站可能下載的網頁內容會分散在不同的 URL,以致於下載過程會被中斷,類似的網站就需要加入到例外,最常見的例子就是微軟更新網站
!CARP 載入因子 (CARP Load Factor)
*每部快取伺服器的相對可用性,0 ~ 100
!ISA Server 2006 企業版整合負載平衡功能
!!網路負載平衡 (NLB)
##一部用戶端對 NLB 叢集起始請求
***NLB 叢集:藉由硬體的 NLB 閘道器、軟體的 NLB 所串聯提供相同服務,具備相同對外的的叢集 IP、主機名稱
##NLB 閘道器對進來的請求進行溢送 (floods) 給叢集內的所有提供服務的伺服器
##收到溢送通知的伺服器透過內部網路溝通,由其中負載最低的伺服器回應請求,其他的伺服器則丟棄請求通知
!!ISA Server 2006 負載平衡功能
*提供負載平衡的 ISA 伺服器必須有三個以上的介面
**一個對內、一個對外、一個連接 DMZ 區
*外部負載平衡:VPN 連線負載平衡
*內部負載平衡:快取伺服器負載平衡
*設定虛擬 IP:叢集統一對外的 IP,必須與現有的 IP 不相衝突
*一旦啟用 NLB 後,防火牆用戶端或 Web Proxy 應設為 虛擬 IP,而非原來的 IP
!ISA Server 2006 負載平衡功能的條件
*ISA Server 2006 Enterprise Edition
*Windows Server 2003 Enterprise Edition
#路由原理
#路由協定種類
#RIP
#OSPF
#EIGRP
#PPP & 專線
#Frame Relay
#NAT、PAT
#IPv6
Chap 1 - 交換技術 (Switching)
Chap 2 - 路由技術 (Routing)
Chap 3 - 存取控制清單 (ACL)
Chap 4 - 廣域網路 (WAN)
Background: #fff
Foreground: #000
PrimaryPale: #fc8
PrimaryLight: #ccf
PrimaryMid: #03c
PrimaryDark: #55f
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/***
|''Name:''|CryptoFunctionsPlugin|
|''Description:''|Support for cryptographic functions|
***/
//{{{
if(!version.extensions.CryptoFunctionsPlugin) {
version.extensions.CryptoFunctionsPlugin = {installed:true};
//--
//-- Crypto functions and associated conversion routines
//--
// Crypto "namespace"
function Crypto() {}
// Convert a string to an array of big-endian 32-bit words
Crypto.strToBe32s = function(str)
{
var be = Array();
var len = Math.floor(str.length/4);
var i, j;
for(i=0, j=0; i<len; i++, j+=4) {
be[i] = ((str.charCodeAt(j)&0xff) << 24)|((str.charCodeAt(j+1)&0xff) << 16)|((str.charCodeAt(j+2)&0xff) << 8)|(str.charCodeAt(j+3)&0xff);
}
while (j<str.length) {
be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
j++;
}
return be;
};
// Convert an array of big-endian 32-bit words to a string
Crypto.be32sToStr = function(be)
{
var str = "";
for(var i=0;i<be.length*32;i+=8)
str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
return str;
};
// Convert an array of big-endian 32-bit words to a hex string
Crypto.be32sToHex = function(be)
{
var hex = "0123456789ABCDEF";
var str = "";
for(var i=0;i<be.length*4;i++)
str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
return str;
};
// Return, in hex, the SHA-1 hash of a string
Crypto.hexSha1Str = function(str)
{
return Crypto.be32sToHex(Crypto.sha1Str(str));
};
// Return the SHA-1 hash of a string
Crypto.sha1Str = function(str)
{
return Crypto.sha1(Crypto.strToBe32s(str),str.length);
};
// Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
Crypto.sha1 = function(x,blen)
{
// Add 32-bit integers, wrapping at 32 bits
add32 = function(a,b)
{
var lsw = (a&0xFFFF)+(b&0xFFFF);
var msw = (a>>16)+(b>>16)+(lsw>>16);
return (msw<<16)|(lsw&0xFFFF);
};
// Add five 32-bit integers, wrapping at 32 bits
add32x5 = function(a,b,c,d,e)
{
var lsw = (a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
var msw = (a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
return (msw<<16)|(lsw&0xFFFF);
};
// Bitwise rotate left a 32-bit integer by 1 bit
rol32 = function(n)
{
return (n>>>31)|(n<<1);
};
var len = blen*8;
// Append padding so length in bits is 448 mod 512
x[len>>5] |= 0x80 << (24-len%32);
// Append length
x[((len+64>>9)<<4)+15] = len;
var w = Array(80);
var k1 = 0x5A827999;
var k2 = 0x6ED9EBA1;
var k3 = 0x8F1BBCDC;
var k4 = 0xCA62C1D6;
var h0 = 0x67452301;
var h1 = 0xEFCDAB89;
var h2 = 0x98BADCFE;
var h3 = 0x10325476;
var h4 = 0xC3D2E1F0;
for(var i=0;i<x.length;i+=16) {
var j,t;
var a = h0;
var b = h1;
var c = h2;
var d = h3;
var e = h4;
for(j = 0;j<16;j++) {
w[j] = x[i+j];
t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
}
for(j=16;j<20;j++) {
w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
}
for(j=20;j<40;j++) {
w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k2);
e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
}
for(j=40;j<60;j++) {
w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
t = add32x5(e,(a>>>27)|(a<<5),(b&c)|(d&(b|c)),w[j],k3);
e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
}
for(j=60;j<80;j++) {
w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k4);
e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
}
h0 = add32(h0,a);
h1 = add32(h1,b);
h2 = add32(h2,c);
h3 = add32(h3,d);
h4 = add32(h4,e);
}
return Array(h0,h1,h2,h3,h4);
};
}
//}}}
/***
|''Name:''|DeprecatedFunctionsPlugin|
|''Description:''|Support for deprecated functions removed from core|
***/
//{{{
if(!version.extensions.DeprecatedFunctionsPlugin) {
version.extensions.DeprecatedFunctionsPlugin = {installed:true};
//--
//-- Deprecated code
//--
// @Deprecated: Use createElementAndWikify and this.termRegExp instead
config.formatterHelpers.charFormatHelper = function(w)
{
w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
};
// @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
config.formatterHelpers.monospacedByLineHelper = function(w)
{
var lookaheadRegExp = new RegExp(this.lookahead,"mg");
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var text = lookaheadMatch[1];
if(config.browser.isIE)
text = text.replace(/\n/g,"\r");
createTiddlyElement(w.output,"pre",null,null,text);
w.nextMatch = lookaheadRegExp.lastIndex;
}
};
// @Deprecated: Use <br> or <br /> instead of <<br>>
config.macros.br = {};
config.macros.br.handler = function(place)
{
createTiddlyElement(place,"br");
};
// Find an entry in an array. Returns the array index or null
// @Deprecated: Use indexOf instead
Array.prototype.find = function(item)
{
var i = this.indexOf(item);
return i == -1 ? null : i;
};
// Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
// @Deprecated: Use store.getLoader().internalizeTiddler instead
Tiddler.prototype.loadFromDiv = function(divRef,title)
{
return store.getLoader().internalizeTiddler(store,this,title,divRef);
};
// Format the text for storage in an HTML DIV
// @Deprecated Use store.getSaver().externalizeTiddler instead.
Tiddler.prototype.saveToDiv = function()
{
return store.getSaver().externalizeTiddler(store,this);
};
// @Deprecated: Use store.allTiddlersAsHtml() instead
function allTiddlersAsHtml()
{
return store.allTiddlersAsHtml();
}
// @Deprecated: Use refreshPageTemplate instead
function applyPageTemplate(title)
{
refreshPageTemplate(title);
}
// @Deprecated: Use story.displayTiddlers instead
function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,unused3)
{
story.displayTiddlers(srcElement,titles,template,animate);
}
// @Deprecated: Use story.displayTiddler instead
function displayTiddler(srcElement,title,template,unused1,unused2,animate,unused3)
{
story.displayTiddler(srcElement,title,template,animate);
}
// @Deprecated: Use functions on right hand side directly instead
var createTiddlerPopup = Popup.create;
var scrollToTiddlerPopup = Popup.show;
var hideTiddlerPopup = Popup.remove;
// @Deprecated: Use right hand side directly instead
var regexpBackSlashEn = new RegExp("\\\\n","mg");
var regexpBackSlash = new RegExp("\\\\","mg");
var regexpBackSlashEss = new RegExp("\\\\s","mg");
var regexpNewLine = new RegExp("\n","mg");
var regexpCarriageReturn = new RegExp("\r","mg");
}
//}}}
/***
|Name|DisableWikiLinksPlugin|
|Source|http://www.TiddlyTools.com/#DisableWikiLinksPlugin|
|Version|1.5.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|Tiddler.prototype.autoLinkWikiWords, 'wikiLink' formatter|
|Options|##Configuration|
|Description|selectively disable TiddlyWiki's automatic ~WikiWord linking behavior|
This plugin allows you to disable TiddlyWiki's automatic ~WikiWord linking behavior, so that WikiWords embedded in tiddler content will be rendered as regular text, instead of being automatically converted to tiddler links. To create a tiddler link when automatic linking is disabled, you must enclose the link text within {{{[[...]]}}}.
!!!!!Usage
<<<
You can block automatic WikiWord linking behavior for any specific tiddler by ''tagging it with<<tag excludeWikiWords>>'' (see configuration below) or, check a plugin option to disable automatic WikiWord links to non-existing tiddler titles, while still linking WikiWords that correspond to existing tiddlers titles or shadow tiddler titles. You can also block specific selected WikiWords from being automatically linked by listing them in [[DisableWikiLinksList]] (see configuration below), separated by whitespace. This tiddler is optional and, when present, causes the listed words to always be excluded, even if automatic linking of other WikiWords is being permitted.
Note: WikiWords contained in default ''shadow'' tiddlers will be automatically linked unless you select an additional checkbox option lets you disable these automatic links as well, though this is not recommended, since it can make it more difficult to access some TiddlyWiki standard default content (such as AdvancedOptions or SideBarTabs)
<<<
!!!!!Configuration
<<<
<<option chkDisableWikiLinks>> Disable ALL automatic WikiWord tiddler links
<<option chkAllowLinksFromShadowTiddlers>> ... except for WikiWords //contained in// shadow tiddlers
<<option chkDisableNonExistingWikiLinks>> Disable automatic WikiWord links for non-existing tiddlers
Disable automatic WikiWord links for words listed in: <<option txtDisableWikiLinksList>>
Disable automatic WikiWord links for tiddlers tagged with: <<option txtDisableWikiLinksTag>>
<<<
!!!!!Revisions
<<<
2006.06.09 [1.5.0] added configurable txtDisableWikiLinksTag (default value: "excludeWikiWords") to allows selective disabling of automatic WikiWord links for any tiddler tagged with that value.
2006.12.31 [1.4.0] in formatter, test for chkDisableNonExistingWikiLinks
2006.12.09 [1.3.0] in formatter, test for excluded wiki words specified in DisableWikiLinksList
2006.12.09 [1.2.2] fix logic in autoLinkWikiWords() (was allowing links TO shadow tiddlers, even when chkDisableWikiLinks is TRUE).
2006.12.09 [1.2.1] revised logic for handling links in shadow content
2006.12.08 [1.2.0] added hijack of Tiddler.prototype.autoLinkWikiWords so regular (non-bracketed) WikiWords won't be added to the missing list
2006.05.24 [1.1.0] added option to NOT bypass automatic wikiword links when displaying default shadow content (default is to auto-link shadow content)
2006.02.05 [1.0.1] wrapped wikifier hijack in init function to eliminate globals and avoid FireFox 1.5.0.1 crash bug when referencing globals
2005.12.09 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.disableWikiLinks= {major: 1, minor: 5, revision: 0, date: new Date(2007,6,9)};
if (config.options.chkDisableNonExistingWikiLinks==undefined) config.options.chkDisableNonExistingWikiLinks= false;
if (config.options.chkDisableWikiLinks==undefined) config.options.chkDisableWikiLinks=false;
if (config.options.txtDisableWikiLinksList==undefined) config.options.txtDisableWikiLinksList="DisableWikiLinksList";
if (config.options.chkAllowLinksFromShadowTiddlers==undefined) config.options.chkAllowLinksFromShadowTiddlers=true;
if (config.options.txtDisableWikiLinksTag==undefined) config.options.txtDisableWikiLinksTag="excludeWikiWords";
// find the formatter for wikiLink and replace handler with 'pass-thru' rendering
initDisableWikiLinksFormatter();
function initDisableWikiLinksFormatter() {
for (var i=0; i<config.formatters.length && config.formatters[i].name!="wikiLink"; i++);
config.formatters[i].coreHandler=config.formatters[i].handler;
config.formatters[i].handler=function(w) {
// supress any leading "~" (if present)
var skip=(w.matchText.substr(0,1)==config.textPrimitives.unWikiLink)?1:0;
var title=w.matchText.substr(skip);
var exists=store.tiddlerExists(title);
var inShadow=w.tiddler && store.isShadowTiddler(w.tiddler.title);
// check for excluded Tiddler
if (w.tiddler && w.tiddler.isTagged(config.options.txtDisableWikiLinksTag))
{ w.outputText(w.output,w.matchStart+skip,w.nextMatch); return; }
// check for specific excluded wiki words
var t=store.getTiddlerText(config.options.txtDisableWikiLinksList)
if (t && t.length && t.indexOf(w.matchText)!=-1)
{ w.outputText(w.output,w.matchStart+skip,w.nextMatch); return; }
// if not disabling links from shadows (default setting)
if (config.options.chkAllowLinksFromShadowTiddlers && inShadow)
return this.coreHandler(w);
// check for non-existing non-shadow tiddler
if (config.options.chkDisableNonExistingWikiLinks && !exists)
{ w.outputText(w.output,w.matchStart+skip,w.nextMatch); return; }
// if not enabled, just do standard WikiWord link formatting
if (!config.options.chkDisableWikiLinks)
return this.coreHandler(w);
// just return text without linking
w.outputText(w.output,w.matchStart+skip,w.nextMatch)
}
}
Tiddler.prototype.coreAutoLinkWikiWords = Tiddler.prototype.autoLinkWikiWords;
Tiddler.prototype.autoLinkWikiWords = function()
{
// DEBUG alert("processing: "+this.title);
// if all automatic links are not disabled, just return results from core function
if (!config.options.chkDisableWikiLinks)
return this.coreAutoLinkWikiWords.apply(this,arguments);
return false;
}
//}}}
/*{{{*/
/*}}}*/
/*{{{*/
a {color:#0044BB;font-weight:bold}
/*}}}*/
<!--{{{-->
<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='easyEdit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
/***
|''Name:''|FieldsEditorPlugin|
|''Description:''|//create//, //edit//, //view// and //delete// commands in toolbar <<toolbar fields>>.|
|''Version:''|1.0.2|
|''Date:''|Dec 21,2007|
|''Source:''|http://visualtw.ouvaton.org/VisualTW.html|
|''Author:''|Pascal Collin|
|''License:''|[[BSD open source license|License]]|
|''~CoreVersion:''|2.2.0|
|''Browser:''|Firefox 2.0; InternetExplorer 6.0, others|
!Demo:
On [[homepage|http://visualtw.ouvaton.org/VisualTW.html]], see [[FieldEditor example]]
!Installation:
*import this tiddler from [[homepage|http://visualtw.ouvaton.org/VisualTW.html]] (tagged as systemConfig)
*save and reload
*optionnaly : add the following css text in your StyleSheet : {{{#popup tr.fieldTableRow td {padding:1px 3px 1px 3px;}}}}
!Code
***/
//{{{
config.commands.fields.handlePopup = function(popup,title) {
var tiddler = store.fetchTiddler(title);
if(!tiddler)
return;
var fields = {};
store.forEachField(tiddler,function(tiddler,fieldName,value) {fields[fieldName] = value;},true);
var items = [];
for(var t in fields) {
var editCommand = "<<untiddledCall editFieldDialog "+escape(title)+" "+escape(t)+">>";
var deleteCommand = "<<untiddledCall deleteField "+escape(title)+" "+escape(t)+">>";
var renameCommand = "<<untiddledCall renameField "+escape(title)+" "+escape(t)+">>";
items.push({field: t,value: fields[t], actions: editCommand+renameCommand+deleteCommand});
}
items.sort(function(a,b) {return a.field < b.field ? -1 : (a.field == b.field ? 0 : +1);});
var createNewCommand = "<<untiddledCall createField "+escape(title)+">>";
items.push({field : "", value : "", actions:createNewCommand });
if(items.length > 0)
ListView.create(popup,items,this.listViewTemplate);
else
createTiddlyElement(popup,"div",null,null,this.emptyText);
}
config.commands.fields.listViewTemplate = {
columns: [
{name: 'Field', field: 'field', title: "Field", type: 'String'},
{name: 'Actions', field: 'actions', title: "Actions", type: 'WikiText'},
{name: 'Value', field: 'value', title: "Value", type: 'WikiText'}
],
rowClasses: [
{className: 'fieldTableRow', field: 'actions'}
],
buttons: [ //can't use button for selected then delete, because click on checkbox will hide the popup
]
}
config.macros.untiddledCall = { // when called from listview, tiddler is unset, so we need to pass tiddler as parameter
handler : function(place,macroName,params,wikifier,paramString) {
var macroName = params.shift();
if (macroName) var macro = config.macros[macroName];
var title = params.shift();
if (title) var tiddler = store.getTiddler(unescape(title));
if (macro) macro.handler(place,macroName,params,wikifier,paramString,tiddler);
}
}
config.macros.deleteField = {
handler : function(place,macroName,params,wikifier,paramString,tiddler) {
if(!readOnly && params[0]) {
fieldName = unescape(params[0]);
var btn = createTiddlyButton(place,"delete", "delete "+fieldName,this.onClickDeleteField);
btn.setAttribute("title",tiddler.title);
btn.setAttribute("fieldName", fieldName);
}
},
onClickDeleteField : function() {
var title=this.getAttribute("title");
var fieldName=this.getAttribute("fieldName");
var tiddler = store.getTiddler(title);
if (tiddler && fieldName && confirm("delete field " + fieldName+" from " + title +" tiddler ?")) {
delete tiddler.fields[fieldName];
store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields);
story.refreshTiddler(title,"ViewTemplate",true);
}
return false;
}
}
config.macros.createField = {
handler : function(place,macroName,params,wikifier,paramString,tiddler) {
if(!readOnly) {
var btn = createTiddlyButton(place,"create new", "create a new field",this.onClickCreateField);
btn.setAttribute("title",tiddler.title);
}
},
onClickCreateField : function() {
var title=this.getAttribute("title");
var tiddler = store.getTiddler(title);
if (tiddler) {
var fieldName = prompt("Field name","");
if (store.getValue(tiddler,fieldName)) {
window.alert("This field already exists.");
}
else if (fieldName) {
var v = prompt("Field value","");
tiddler.fields[fieldName]=v;
store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields);
story.refreshTiddler(title,"ViewTemplate",true);
}
}
return false;
}
}
config.macros.editFieldDialog = {
handler : function(place,macroName,params,wikifier,paramString,tiddler) {
if(!readOnly && params[0]) {
fieldName = unescape(params[0]);
var btn = createTiddlyButton(place,"edit", "edit this field",this.onClickEditFieldDialog);
btn.setAttribute("title",tiddler.title);
btn.setAttribute("fieldName", fieldName);
}
},
onClickEditFieldDialog : function() {
var title=this.getAttribute("title");
var tiddler = store.getTiddler(title);
var fieldName=this.getAttribute("fieldName");
if (tiddler && fieldName) {
var value = tiddler.fields[fieldName];
value = value ? value : "";
var lines = value.match(/\n/mg);
lines = lines ? true : false;
if (!lines || confirm("This field contains more than one line. Only the first line will be kept if you edit it here. Proceed ?")) {
var v = prompt("Field value",value);
tiddler.fields[fieldName]=v;
store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields);
story.refreshTiddler(title,"ViewTemplate",true);
}
}
return false;
}
}
config.macros.renameField = {
handler : function(place,macroName,params,wikifier,paramString,tiddler) {
if(!readOnly && params[0]) {
fieldName = unescape(params[0]);
var btn = createTiddlyButton(place,"rename", "rename "+fieldName,this.onClickRenameField);
btn.setAttribute("title",tiddler.title);
btn.setAttribute("fieldName", fieldName);
}
},
onClickRenameField : function() {
var title=this.getAttribute("title");
var fieldName=this.getAttribute("fieldName");
var tiddler = store.getTiddler(title);
if (tiddler && fieldName) {
var newName = prompt("Rename " + fieldName + " as ?", fieldName);
if (newName) {
tiddler.fields[newName]=tiddler.fields[fieldName];
delete tiddler.fields[fieldName];
store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields);
story.refreshTiddler(title,"ViewTemplate",true);
}
}
return false;
}
}
config.shadowTiddlers.StyleSheetFieldsEditor = "/*{{{*/\n";
config.shadowTiddlers.StyleSheetFieldsEditor += ".fieldTableRow td {padding : 1px 3px}\n";
config.shadowTiddlers.StyleSheetFieldsEditor += ".fieldTableRow .button {border:0; padding : 0 0.2em}\n";
config.shadowTiddlers.StyleSheetFieldsEditor +="/*}}}*/";
store.addNotification("StyleSheetFieldsEditor", refreshStyles);
//}}}
/***
|''Name:''|ForEachTiddlerPlugin|
|''Version:''|1.0.8 (2007-04-12)|
|''Source:''|http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|© 2005-2007 [[abego Software|http://www.abego-software.de]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
!Description
Create customizable lists, tables etc. for your selections of tiddlers. Specify the tiddlers to include and their order through a powerful language.
''Syntax:''
|>|{{{<<}}}''forEachTiddler'' [''in'' //tiddlyWikiPath//] [''where'' //whereCondition//] [''sortBy'' //sortExpression// [''ascending'' //or// ''descending'']] [''script'' //scriptText//] [//action// [//actionParameters//]]{{{>>}}}|
|//tiddlyWikiPath//|The filepath to the TiddlyWiki the macro should work on. When missing the current TiddlyWiki is used.|
|//whereCondition//|(quoted) JavaScript boolean expression. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//sortExpression//|(quoted) JavaScript expression returning "comparable" objects (using '{{{<}}}','{{{>}}}','{{{==}}}'. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//scriptText//|(quoted) JavaScript text. Typically defines JavaScript functions that are called by the various JavaScript expressions (whereClause, sortClause, action arguments,...)|
|//action//|The action that should be performed on every selected tiddler, in the given order. By default the actions [[addToList|AddToListAction]] and [[write|WriteAction]] are supported. When no action is specified [[addToList|AddToListAction]] is used.|
|//actionParameters//|(action specific) parameters the action may refer while processing the tiddlers (see action descriptions for details). <<tiddler [[JavaScript in actionParameters]]>>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
See details see [[ForEachTiddlerMacro]] and [[ForEachTiddlerExamples]].
!Revision history
* v1.0.8 (2007-04-12)
** Adapted to latest TiddlyWiki 2.2 Beta importTiddlyWiki API (introduced with changeset 2004). TiddlyWiki 2.2 Beta builds prior to changeset 2004 are no longer supported (but TiddlyWiki 2.1 and earlier, of cause)
* v1.0.7 (2007-03-28)
** Also support "pre" formatted TiddlyWikis (introduced with TW 2.2) (when using "in" clause to work on external tiddlers)
* v1.0.6 (2006-09-16)
** Context provides "viewerTiddler", i.e. the tiddler used to view the macro. Most times this is equal to the "inTiddler", but when using the "tiddler" macro both may be different.
** Support "begin", "end" and "none" expressions in "write" action
* v1.0.5 (2006-02-05)
** Pass tiddler containing the macro with wikify, context object also holds reference to tiddler containing the macro ("inTiddler"). Thanks to SimonBaird.
** Support Firefox 1.5.0.1
** Internal
*** Make "JSLint" conform
*** "Only install once"
* v1.0.4 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.3 (2005-12-22)
** Features:
*** Write output to a file supports multi-byte environments (Thanks to Bram Chen)
*** Provide API to access the forEachTiddler functionality directly through JavaScript (see getTiddlers and performMacro)
** Enhancements:
*** Improved error messages on InternetExplorer.
* v1.0.2 (2005-12-10)
** Features:
*** context object also holds reference to store (TiddlyWiki)
** Fixed Bugs:
*** ForEachTiddler 1.0.1 has broken support on win32 Opera 8.51 (Thanks to BrunoSabin for reporting)
* v1.0.1 (2005-12-08)
** Features:
*** Access tiddlers stored in separated TiddlyWikis through the "in" option. I.e. you are no longer limited to only work on the "current TiddlyWiki".
*** Write output to an external file using the "toFile" option of the "write" action. With this option you may write your customized tiddler exports.
*** Use the "script" section to define "helper" JavaScript functions etc. to be used in the various JavaScript expressions (whereClause, sortClause, action arguments,...).
*** Access and store context information for the current forEachTiddler invocation (through the build-in "context" object) .
*** Improved script evaluation (for where/sort clause and write scripts).
* v1.0.0 (2005-11-20)
** initial version
!Code
***/
//{{{
//============================================================================
//============================================================================
// ForEachTiddlerPlugin
//============================================================================
//============================================================================
// Only install once
if (!version.extensions.ForEachTiddlerPlugin) {
if (!window.abego) window.abego = {};
version.extensions.ForEachTiddlerPlugin = {
major: 1, minor: 0, revision: 8,
date: new Date(2007,3,12),
source: "http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin",
licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",
copyright: "Copyright (c) abego Software GmbH, 2005-2007 (www.abego-software.de)"
};
// For backward compatibility with TW 1.2.x
//
if (!TiddlyWiki.prototype.forEachTiddler) {
TiddlyWiki.prototype.forEachTiddler = function(callback) {
for(var t in this.tiddlers) {
callback.call(this,t,this.tiddlers[t]);
}
};
}
//============================================================================
// forEachTiddler Macro
//============================================================================
version.extensions.forEachTiddler = {
major: 1, minor: 0, revision: 8, date: new Date(2007,3,12), provider: "http://tiddlywiki.abego-software.de"};
// ---------------------------------------------------------------------------
// Configurations and constants
// ---------------------------------------------------------------------------
config.macros.forEachTiddler = {
// Standard Properties
label: "forEachTiddler",
prompt: "Perform actions on a (sorted) selection of tiddlers",
// actions
actions: {
addToList: {},
write: {}
}
};
// ---------------------------------------------------------------------------
// The forEachTiddler Macro Handler
// ---------------------------------------------------------------------------
config.macros.forEachTiddler.getContainingTiddler = function(e) {
while(e && !hasClass(e,"tiddler"))
e = e.parentNode;
var title = e ? e.getAttribute("tiddler") : null;
return title ? store.getTiddler(title) : null;
};
config.macros.forEachTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
// config.macros.forEachTiddler.traceMacroCall(place,macroName,params,wikifier,paramString,tiddler);
if (!tiddler) tiddler = config.macros.forEachTiddler.getContainingTiddler(place);
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// Parse the "in" clause
var tiddlyWikiPath = undefined;
if ((i < params.length) && params[i] == "in") {
i++;
if (i >= params.length) {
this.handleError(place, "TiddlyWiki path expected behind 'in'.");
return;
}
tiddlyWikiPath = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the where clause
var whereClause ="true";
if ((i < params.length) && params[i] == "where") {
i++;
whereClause = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the sort stuff
var sortClause = null;
var sortAscending = true;
if ((i < params.length) && params[i] == "sortBy") {
i++;
if (i >= params.length) {
this.handleError(place, "sortClause missing behind 'sortBy'.");
return;
}
sortClause = this.paramEncode(params[i]);
i++;
if ((i < params.length) && (params[i] == "ascending" || params[i] == "descending")) {
sortAscending = params[i] == "ascending";
i++;
}
}
// Parse the script
var scriptText = null;
if ((i < params.length) && params[i] == "script") {
i++;
scriptText = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the action.
// When we are already at the end use the default action
var actionName = "addToList";
if (i < params.length) {
if (!config.macros.forEachTiddler.actions[params[i]]) {
this.handleError(place, "Unknown action '"+params[i]+"'.");
return;
} else {
actionName = params[i];
i++;
}
}
// Get the action parameter
// (the parsing is done inside the individual action implementation.)
var actionParameter = params.slice(i);
// --- Processing ------------------------------------------
try {
this.performMacro({
place: place,
inTiddler: tiddler,
whereClause: whereClause,
sortClause: sortClause,
sortAscending: sortAscending,
actionName: actionName,
actionParameter: actionParameter,
scriptText: scriptText,
tiddlyWikiPath: tiddlyWikiPath});
} catch (e) {
this.handleError(place, e);
}
};
// Returns an object with properties "tiddlers" and "context".
// tiddlers holds the (sorted) tiddlers selected by the parameter,
// context the context of the execution of the macro.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlersAndContext = function(parameter) {
var context = config.macros.forEachTiddler.createContext(parameter.place, parameter.whereClause, parameter.sortClause, parameter.sortAscending, parameter.actionName, parameter.actionParameter, parameter.scriptText, parameter.tiddlyWikiPath, parameter.inTiddler);
var tiddlyWiki = parameter.tiddlyWikiPath ? this.loadTiddlyWiki(parameter.tiddlyWikiPath) : store;
context["tiddlyWiki"] = tiddlyWiki;
// Get the tiddlers, as defined by the whereClause
var tiddlers = this.findTiddlers(parameter.whereClause, context, tiddlyWiki);
context["tiddlers"] = tiddlers;
// Sort the tiddlers, when sorting is required.
if (parameter.sortClause) {
this.sortTiddlers(tiddlers, parameter.sortClause, parameter.sortAscending, context);
}
return {tiddlers: tiddlers, context: context};
};
// Returns the (sorted) tiddlers selected by the parameter.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlers = function(parameter) {
return this.getTiddlersAndContext(parameter).tiddlers;
};
// Performs the macros with the given parameter.
//
// @param parameter holds the parameter of the macro as separate properties.
// The following properties are supported:
//
// place
// whereClause
// sortClause
// sortAscending
// actionName
// actionParameter
// scriptText
// tiddlyWikiPath
//
// All properties are optional.
// For most actions the place property must be defined.
//
config.macros.forEachTiddler.performMacro = function(parameter) {
var tiddlersAndContext = this.getTiddlersAndContext(parameter);
// Perform the action
var actionName = parameter.actionName ? parameter.actionName : "addToList";
var action = config.macros.forEachTiddler.actions[actionName];
if (!action) {
this.handleError(parameter.place, "Unknown action '"+actionName+"'.");
return;
}
var actionHandler = action.handler;
actionHandler(parameter.place, tiddlersAndContext.tiddlers, parameter.actionParameter, tiddlersAndContext.context);
};
// ---------------------------------------------------------------------------
// The actions
// ---------------------------------------------------------------------------
// Internal.
//
// --- The addToList Action -----------------------------------------------
//
config.macros.forEachTiddler.actions.addToList.handler = function(place, tiddlers, parameter, context) {
// Parse the parameter
var p = 0;
// Check for extra parameters
if (parameter.length > p) {
config.macros.forEachTiddler.createExtraParameterErrorElement(place, "addToList", parameter, p);
return;
}
// Perform the action.
var list = document.createElement("ul");
place.appendChild(list);
for (var i = 0; i < tiddlers.length; i++) {
var tiddler = tiddlers[i];
var listItem = document.createElement("li");
list.appendChild(listItem);
createTiddlyLink(listItem, tiddler.title, true);
}
};
abego.parseNamedParameter = function(name, parameter, i) {
var beginExpression = null;
if ((i < parameter.length) && parameter[i] == name) {
i++;
if (i >= parameter.length) {
throw "Missing text behind '%0'".format([name]);
}
return config.macros.forEachTiddler.paramEncode(parameter[i]);
}
return null;
}
// Internal.
//
// --- The write Action ---------------------------------------------------
//
config.macros.forEachTiddler.actions.write.handler = function(place, tiddlers, parameter, context) {
// Parse the parameter
var p = 0;
if (p >= parameter.length) {
this.handleError(place, "Missing expression behind 'write'.");
return;
}
var textExpression = config.macros.forEachTiddler.paramEncode(parameter[p]);
p++;
// Parse the "begin" option
var beginExpression = abego.parseNamedParameter("begin", parameter, p);
if (beginExpression !== null)
p += 2;
var endExpression = abego.parseNamedParameter("end", parameter, p);
if (endExpression !== null)
p += 2;
var noneExpression = abego.parseNamedParameter("none", parameter, p);
if (noneExpression !== null)
p += 2;
// Parse the "toFile" option
var filename = null;
var lineSeparator = undefined;
if ((p < parameter.length) && parameter[p] == "toFile") {
p++;
if (p >= parameter.length) {
this.handleError(place, "Filename expected behind 'toFile' of 'write' action.");
return;
}
filename = config.macros.forEachTiddler.getLocalPath(config.macros.forEachTiddler.paramEncode(parameter[p]));
p++;
if ((p < parameter.length) && parameter[p] == "withLineSeparator") {
p++;
if (p >= parameter.length) {
this.handleError(place, "Line separator text expected behind 'withLineSeparator' of 'write' action.");
return;
}
lineSeparator = config.macros.forEachTiddler.paramEncode(parameter[p]);
p++;
}
}
// Check for extra parameters
if (parameter.length > p) {
config.macros.forEachTiddler.createExtraParameterErrorElement(place, "write", parameter, p);
return;
}
// Perform the action.
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(textExpression, context);
var count = tiddlers.length;
var text = "";
if (count > 0 && beginExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(beginExpression, context)(undefined, context, count, undefined);
for (var i = 0; i < count; i++) {
var tiddler = tiddlers[i];
text += func(tiddler, context, count, i);
}
if (count > 0 && endExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(endExpression, context)(undefined, context, count, undefined);
if (count == 0 && noneExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(noneExpression, context)(undefined, context, count, undefined);
if (filename) {
if (lineSeparator !== undefined) {
lineSeparator = lineSeparator.replace(/\\n/mg, "\n").replace(/\\r/mg, "\r");
text = text.replace(/\n/mg,lineSeparator);
}
saveFile(filename, convertUnicodeToUTF8(text));
} else {
var wrapper = createTiddlyElement(place, "span");
wikify(text, wrapper, null/* highlightRegExp */, context.inTiddler);
}
};
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
// Internal.
//
config.macros.forEachTiddler.createContext = function(placeParam, whereClauseParam, sortClauseParam, sortAscendingParam, actionNameParam, actionParameterParam, scriptText, tiddlyWikiPathParam, inTiddlerParam) {
return {
place : placeParam,
whereClause : whereClauseParam,
sortClause : sortClauseParam,
sortAscending : sortAscendingParam,
script : scriptText,
actionName : actionNameParam,
actionParameter : actionParameterParam,
tiddlyWikiPath : tiddlyWikiPathParam,
inTiddler : inTiddlerParam, // the tiddler containing the <<forEachTiddler ...>> macro call.
viewerTiddler : config.macros.forEachTiddler.getContainingTiddler(placeParam) // the tiddler showing the forEachTiddler result
};
};
// Internal.
//
// Returns a TiddlyWiki with the tiddlers loaded from the TiddlyWiki of
// the given path.
//
config.macros.forEachTiddler.loadTiddlyWiki = function(path, idPrefix) {
if (!idPrefix) {
idPrefix = "store";
}
var lenPrefix = idPrefix.length;
// Read the content of the given file
var content = loadFile(this.getLocalPath(path));
if(content === null) {
throw "TiddlyWiki '"+path+"' not found.";
}
var tiddlyWiki = new TiddlyWiki();
// Starting with TW 2.2 there is a helper function to import the tiddlers
if (tiddlyWiki.importTiddlyWiki) {
if (!tiddlyWiki.importTiddlyWiki(content))
throw "File '"+path+"' is not a TiddlyWiki.";
tiddlyWiki.dirty = false;
return tiddlyWiki;
}
// The legacy code, for TW < 2.2
// Locate the storeArea div's
var posOpeningDiv = content.indexOf(startSaveArea);
var posClosingDiv = content.lastIndexOf(endSaveArea);
if((posOpeningDiv == -1) || (posClosingDiv == -1)) {
throw "File '"+path+"' is not a TiddlyWiki.";
}
var storageText = content.substr(posOpeningDiv + startSaveArea.length, posClosingDiv);
// Create a "div" element that contains the storage text
var myStorageDiv = document.createElement("div");
myStorageDiv.innerHTML = storageText;
myStorageDiv.normalize();
// Create all tiddlers in a new TiddlyWiki
// (following code is modified copy of TiddlyWiki.prototype.loadFromDiv)
var store = myStorageDiv.childNodes;
for(var t = 0; t < store.length; t++) {
var e = store[t];
var title = null;
if(e.getAttribute)
title = e.getAttribute("tiddler");
if(!title && e.id && e.id.substr(0,lenPrefix) == idPrefix)
title = e.id.substr(lenPrefix);
if(title && title !== "") {
var tiddler = tiddlyWiki.createTiddler(title);
tiddler.loadFromDiv(e,title);
}
}
tiddlyWiki.dirty = false;
return tiddlyWiki;
};
// Internal.
//
// Returns a function that has a function body returning the given javaScriptExpression.
// The function has the parameters:
//
// (tiddler, context, count, index)
//
config.macros.forEachTiddler.getEvalTiddlerFunction = function (javaScriptExpression, context) {
var script = context["script"];
var functionText = "var theFunction = function(tiddler, context, count, index) { return "+javaScriptExpression+"}";
var fullText = (script ? script+";" : "")+functionText+";theFunction;";
return eval(fullText);
};
// Internal.
//
config.macros.forEachTiddler.findTiddlers = function(whereClause, context, tiddlyWiki) {
var result = [];
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(whereClause, context);
tiddlyWiki.forEachTiddler(function(title,tiddler) {
if (func(tiddler, context, undefined, undefined)) {
result.push(tiddler);
}
});
return result;
};
// Internal.
//
config.macros.forEachTiddler.createExtraParameterErrorElement = function(place, actionName, parameter, firstUnusedIndex) {
var message = "Extra parameter behind '"+actionName+"':";
for (var i = firstUnusedIndex; i < parameter.length; i++) {
message += " "+parameter[i];
}
this.handleError(place, message);
};
// Internal.
//
config.macros.forEachTiddler.sortAscending = function(tiddlerA, tiddlerB) {
var result =
(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
? 0
: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
? -1
: +1;
return result;
};
// Internal.
//
config.macros.forEachTiddler.sortDescending = function(tiddlerA, tiddlerB) {
var result =
(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
? 0
: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
? +1
: -1;
return result;
};
// Internal.
//
config.macros.forEachTiddler.sortTiddlers = function(tiddlers, sortClause, ascending, context) {
// To avoid evaluating the sortClause whenever two items are compared
// we pre-calculate the sortValue for every item in the array and store it in a
// temporary property ("forEachTiddlerSortValue") of the tiddlers.
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(sortClause, context);
var count = tiddlers.length;
var i;
for (i = 0; i < count; i++) {
var tiddler = tiddlers[i];
tiddler.forEachTiddlerSortValue = func(tiddler,context, undefined, undefined);
}
// Do the sorting
tiddlers.sort(ascending ? this.sortAscending : this.sortDescending);
// Delete the temporary property that holds the sortValue.
for (i = 0; i < tiddlers.length; i++) {
delete tiddlers[i].forEachTiddlerSortValue;
}
};
// Internal.
//
config.macros.forEachTiddler.trace = function(message) {
displayMessage(message);
};
// Internal.
//
config.macros.forEachTiddler.traceMacroCall = function(place,macroName,params) {
var message ="<<"+macroName;
for (var i = 0; i < params.length; i++) {
message += " "+params[i];
}
message += ">>";
displayMessage(message);
};
// Internal.
//
// Creates an element that holds an error message
//
config.macros.forEachTiddler.createErrorElement = function(place, exception) {
var message = (exception.description) ? exception.description : exception.toString();
return createTiddlyElement(place,"span",null,"forEachTiddlerError","<<forEachTiddler ...>>: "+message);
};
// Internal.
//
// @param place [may be null]
//
config.macros.forEachTiddler.handleError = function(place, exception) {
if (place) {
this.createErrorElement(place, exception);
} else {
throw exception;
}
};
// Internal.
//
// Encodes the given string.
//
// Replaces
// "$))" to ">>"
// "$)" to ">"
//
config.macros.forEachTiddler.paramEncode = function(s) {
var reGTGT = new RegExp("\\$\\)\\)","mg");
var reGT = new RegExp("\\$\\)","mg");
return s.replace(reGTGT, ">>").replace(reGT, ">");
};
// Internal.
//
// Returns the given original path (that is a file path, starting with "file:")
// as a path to a local file, in the systems native file format.
//
// Location information in the originalPath (i.e. the "#" and stuff following)
// is stripped.
//
config.macros.forEachTiddler.getLocalPath = function(originalPath) {
// Remove any location part of the URL
var hashPos = originalPath.indexOf("#");
if(hashPos != -1)
originalPath = originalPath.substr(0,hashPos);
// Convert to a native file format assuming
// "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
// "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
// "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
// "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
var localPath;
if(originalPath.charAt(9) == ":") // pc local file
localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file://///") === 0) // FireFox pc network file
localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file:///") === 0) // mac/unix local file
localPath = unescape(originalPath.substr(7));
else if(originalPath.indexOf("file:/") === 0) // mac/unix local file
localPath = unescape(originalPath.substr(5));
else // pc network file
localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
return localPath;
};
// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
".forEachTiddlerError{color: #ffffff;background-color: #880000;}",
"forEachTiddler");
//============================================================================
// End of forEachTiddler Macro
//============================================================================
//============================================================================
// String.startsWith Function
//============================================================================
//
// Returns true if the string starts with the given prefix, false otherwise.
//
version.extensions["String.startsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.startsWith = function(prefix) {
var n = prefix.length;
return (this.length >= n) && (this.slice(0, n) == prefix);
};
//============================================================================
// String.endsWith Function
//============================================================================
//
// Returns true if the string ends with the given suffix, false otherwise.
//
version.extensions["String.endsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.endsWith = function(suffix) {
var n = suffix.length;
return (this.length >= n) && (this.right(n) == suffix);
};
//============================================================================
// String.contains Function
//============================================================================
//
// Returns true when the string contains the given substring, false otherwise.
//
version.extensions["String.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.contains = function(substring) {
return this.indexOf(substring) >= 0;
};
//============================================================================
// Array.indexOf Function
//============================================================================
//
// Returns the index of the first occurance of the given item in the array or
// -1 when no such item exists.
//
// @param item [may be null]
//
version.extensions["Array.indexOf"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.indexOf = function(item) {
for (var i = 0; i < this.length; i++) {
if (this[i] == item) {
return i;
}
}
return -1;
};
//============================================================================
// Array.contains Function
//============================================================================
//
// Returns true when the array contains the given item, otherwise false.
//
// @param item [may be null]
//
version.extensions["Array.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.contains = function(item) {
return (this.indexOf(item) >= 0);
};
//============================================================================
// Array.containsAny Function
//============================================================================
//
// Returns true when the array contains at least one of the elements
// of the item. Otherwise (or when items contains no elements) false is returned.
//
version.extensions["Array.containsAny"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAny = function(items) {
for(var i = 0; i < items.length; i++) {
if (this.contains(items[i])) {
return true;
}
}
return false;
};
//============================================================================
// Array.containsAll Function
//============================================================================
//
// Returns true when the array contains all the items, otherwise false.
//
// When items is null false is returned (even if the array contains a null).
//
// @param items [may be null]
//
version.extensions["Array.containsAll"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAll = function(items) {
for(var i = 0; i < items.length; i++) {
if (!this.contains(items[i])) {
return false;
}
}
return true;
};
} // of "install only once"
// Used Globals (for JSLint) ==============
// ... DOM
/*global document */
// ... TiddlyWiki Core
/*global convertUnicodeToUTF8, createTiddlyElement, createTiddlyLink,
displayMessage, endSaveArea, hasClass, loadFile, saveFile,
startSaveArea, store, wikify */
//}}}
/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/
使用此 TiddlyWiki 的空白範本之前,請先修改以下預設文章:
* SiteTitle 及 SiteSubtitle:網站的標題和副標題,顯示於頁面上方<br />(在儲存變更後,將顯示於瀏覽器視窗的標題列)。
* MainMenu:主選單(通常在頁面左側)。
* DefaultTiddlers:內含一些文章的標題,可於載入TiddlyWiki 後的預設開啟。
請輸入您的大名,作為所建立/ 編輯的文章署名:<<option txtUserName>>
<!--{{{-->
<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='easyEdit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
/***
|Name|HTMLFormattingPlugin|
|Source|http://www.TiddlyTools.com/#HTMLFormattingPlugin|
|Documentation|http://www.TiddlyTools.com/#HTMLFormattingPluginInfo|
|Version|2.1.5|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|'HTML' formatter|
|Description|embed wiki syntax formatting inside of HTML content|
The shorthand Wiki-style formatting syntax of ~TiddlyWiki is very convenient and enables most content to be reasonably well presented. However, there are times when tried-and-true HTML formatting syntax allows more more precise control of the content display. When HTML formatting syntax is embedded within a tiddler (in between {{{<}}}{{{html>}}} and {{{<}}}{{{/html>}}} markers) TiddlyWiki passes this content to the browser for processing as 'native' HTML. However, TiddlyWiki does not also process the HTML source content for any embedded wiki-formatting syntax it may contain. This means that while you can use HTML formatted content, you cannot mix wiki-formatted content within the HTML formatting.
The ~HTMLFormatting plugin allows you to freely ''mix wiki-style formatting syntax within HTML formatted content'' by extending the action of the standard TiddlyWiki formatting handler.
!!!!!Documentation
>see [[HTMLFormattingPluginInfo]]
!!!!!Revisions
<<<
2008.01.08 [*.*.*] plugin size reduction: documentation moved to HTMLFormattingInfo
2007.12.04 [*.*.*] update for TW2.3.0: replaced deprecated core functions, regexps, and macros
2007.06.14 [2.1.5] in formatter, removed call to e.normalize(). Creates an INFINITE RECURSION error in Safari!!!!
2006.09.10 [2.1.4] update formatter for 2.1 compatibility (use this.lookaheadRegExp instead of temp variable)
2006.05.28 [2.1.3] in wikifyTextNodes(), decode the *value* of TEXTAREA nodes, but don't wikify() its children. (thanks to "ayj" for bug report)
2006.02.19 [2.1.2] in wikifyTextNodes(), put SPAN element into tiddler DOM (replacing text node), BEFORE wikifying the text content. This ensures that the 'place' passed to any macros is correctly defined when the macro is evaluated, so that calls to story.findContainingTiddler(place) will work as expected. (Thanks for bug report from GeoffSlocock)
2006.02.05 [2.1.1] wrapped wikifier hijack in init function to eliminate globals and avoid FireFox 1.5.0.1 crash bug when referencing globals
2005.12.01 [2.1.0] don't wikify #TEXT nodes inside SELECT and TEXTAREA elements
2005.11.06 [2.0.1] code cleanup
2005.10.31 [2.0.0] replaced hijack wikify() with hijack config.formatters["html"] and simplified recursive WikifyTextNodes() code
2005.10.09 [1.0.2] combined documentation and code into a single tiddler
2005.08.05 [1.0.1] moved HTML and CSS definitions into plugin code instead of using separate tiddlers
2005.07.26 [1.0.1] Re-released as a plugin. Added <{{{html}}}>...</{{{nohtml}}}> and <{{{hide newlines}}}> handling
2005.06.26 [1.0.0] Initial Release (as code adaptation - pre-dates TiddlyWiki plugin architecture!!)
<<<
!!!!!Code
***/
//{{{
version.extensions.HTMLFormatting = {major: 2, minor: 1, revision: 5, date: new Date(2007,6,14)};
// find the formatter for HTML and replace the handler
initHTMLFormatter();
function initHTMLFormatter()
{
for (var i=0; i<config.formatters.length && config.formatters[i].name!="html"; i++);
if (i<config.formatters.length) config.formatters[i].handler=function(w) {
if (!this.lookaheadRegExp) // fixup for TW2.0.x
this.lookaheadRegExp = new RegExp(this.lookahead,"mg");
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var html=lookaheadMatch[1];
// optionally suppress wiki-style literal handling of newlines
// strip any carriage returns added by Internet Explorer's textarea edit field
// encode newlines as \n so Internet Explorer's HTML parser won't eat them
// encode macro brackets (<< and >>) so HTML parser won't eat them
if (html.indexOf('<hide linebreaks>')!=-1) html=html.replace(/\n/g,' ');
html=html.replace(/\r/g,'');
html=html.replace(/\n/g,'\\n');
html=html.replace(/<</g,'%%(').replace(/>>/g,')%%');
// create span to hold HTML
// parse HTML and normalize the results
// walk node tree and call wikify() on each text node
var e = createTiddlyElement(w.output,"span");
e.innerHTML=html;
// REMOVED: e.normalize(); // THIS CAUSED INFINITE RECURSION IN SAFARI
wikifyTextNodes(e);
// advance to next parse position
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
}
// wikify text nodes remaining after HTML content is processed (pre-order recursion)
function wikifyTextNodes(theNode)
{
// textarea node doesn't get wikified, just decoded...
if (theNode.nodeName.toLowerCase()=='textarea')
theNode.value=theNode.value.replace(/\%%\(/g,'<<').replace(/\)\%%/g,'>>').replace(/\\n/g,'\n');
else for (var i=0;i<theNode.childNodes.length;i++) {
var theChild=theNode.childNodes.item(i);
if (theChild.nodeName.toLowerCase()=='option') continue;
if (theChild.nodeName.toLowerCase()=='select') continue;
wikifyTextNodes(theChild);
if (theChild.nodeName=='#text') {
var txt=theChild.nodeValue;
// decode macro brackets and newlines
txt=txt.replace(/\%%\(/g,'<<').replace(/\)\%%/g,'>>').replace(/\\n/g,'\n');
// replace text node with wikified() span
var newNode=createTiddlyElement(null,"span");
theNode.replaceChild(newNode,theChild);
wikify(txt,newNode);
}
}
}
//}}}
<!--{{{-->
<div class='toolbar' macro='toolbar closeTiddler closeOthers +easyEdit > fields syncing permalink references jump'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<!--
<div class='tagging' macro='tagging'></div>
-->
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<<importTiddlers inline>>
/***
|Name|ImportTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#ImportTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#ImportTiddlersPluginInfo|
|Version|4.3.1|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|config.macros.importTiddlers.handler|
|Options|##Configuration|
|Description|interactive controls for import/export with filtering.|
This plugin lets you selectively combine tiddlers from any two TiddlyWiki documents. An interactive control panel lets you pick a document to import from, and then select which tiddlers to import, with prompting for skip, rename, merge or replace actions when importing tiddlers that match existing titles. Automatically add tags to imported tiddlers so they are easy to find later on. Generates a detailed report of import 'history' in ImportedTiddlers.
!!!!!Documentation
<<<
see [[ImportTiddlersPluginInfo]] for details
<<<
!!!!!interactive control panel:
<<<
<<tiddler ImportTiddlers>>
^^(see [[ImportTiddlers]] shadow tiddler)^^
<<<
!!!!!Installation Notes
<<<
* As of 6/27/2007, "patch" functions that provide backward-compatibility with TW2.1.x and earlier have been split into a separate [[ImportTiddlersPluginPatch]] tiddler to reduce installation overhead for //this// plugin. You only need to install the additional plugin tiddler when using ImportTiddlersPlugin in documents using TW2.1.x or earlier.
* As of 3/21/2007, the interactive {{{<<importTiddlers>>}}} and non-interactive {{{<<loadTiddlers>>}}} macro definitions and related code have been split into separate [[ImportTiddlersPlugin]] and [[LoadTiddlersPlugin]] to permit selective installation of either the interactive and/or non-interactive macro functions.
* Quick Installation Tip: If you are using an unmodified version of TiddlyWiki (core release version <<version>>), you can get a new, empty TiddlyWiki with the Import Tiddlers plugin pre-installed (''[[download from here|TW+ImportExport.html]]''), and then simply import all your content from your old document into this new, empty document.
<<<
!!!!!Revisions
<<<
2008.06.29 [4.3.1] More layout/animation work for simpler sequential interaction. Code reduction/cleanup
|please see [[ImportTiddlersPluginInfo]] for additional revision details|
2005.07.20 [1.0.0] Initial Release
<<<
!!!!!Code
***/
// // ''MACRO DEFINITION''
//{{{
// Version
version.extensions.importTiddlers = {major: 4, minor: 3, revision: 0, date: new Date(2008,6,28)};
// add ImportTiddlerPlugin controls to built-in backstage import task
if (config.tasks) { // TW2.2 or above
config.tasks.importTask.content="Use ~TiddlyWiki built-in importer (below) or, ";
config.tasks.importTask.content+="<<importTiddlers link 'Use ImportTiddlersPlugin control panel...'>>\n";
config.tasks.importTask.content+="<<importTiddlers core>>"
}
// IE needs explicit global scoping for functions/vars called from browser events
window.onClickImportButton=onClickImportButton;
window.refreshImportList=refreshImportList;
// default cookie/option values
if (!config.options.chkImportReport) config.options.chkImportReport=true;
// default shadow definition
config.shadowTiddlers.ImportTiddlers="<<importTiddlers inline>>";
merge(config.macros.importTiddlers,{
label: "import tiddlers",
prompt: "Copy tiddlers from another document",
openMsg: "Opening %0",
openErrMsg: "Could not open %0 - error=%1",
readMsg: "Read %0 bytes from %1",
foundMsg: "Found %0 tiddlers in %1",
filterMsg: "Filtered %0 tiddlers matching '%1'",
summaryMsg: "%0 tiddler%1 in the list",
summaryFilteredMsg: "%0 of %1 tiddler%2 in the list",
plural: "s are",
single: " is",
countMsg: "%0 tiddlers selected for import",
processedMsg: "Processed %0 tiddlers",
importedMsg: "Imported %0 of %1 tiddlers from %2",
loadText: "please load a document...",
closeText: "close", // text for close button when file is loaded
doneText: "done", // text for close button when file is not loaded
startText: "import", // text for import button
stopText: "stop", // text for import button while importing
local: true, // default to import from local file
src: "", // path/filename or URL of document to import (retrieved from SiteUrl tiddler)
proxy: "", // URL for remote proxy script (retrieved from SiteProxy tiddler)
useProxy: false, // use specific proxy script in front of remote URL
inbound: null, // hash-indexed array of tiddlers from other document
newTags: "", // text of tags added to imported tiddlers
addTags: true, // add new tags to imported tiddlers
listsize: 10, // # of lines to show in imported tiddler list
importTags: true, // include tags from remote source document when importing a tiddler
keepTags: true, // retain existing tags when replacing a tiddler
sync: false, // add 'server' fields to imported tiddlers (for sync function)
lastFilter: "", // most recent filter (URL hash) applied
lastAction: null, // most recent collision button performed
index: 0, // current processing index in import list
sort: "" // sort order for imported tiddler listbox
});
if (config.macros.importTiddlers.coreHandler==undefined)
config.macros.importTiddlers.coreHandler=config.macros.importTiddlers.handler; // save built-in handler
config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
if (!params[0] || params[0].toLowerCase()=='core') { // default to built in
if (config.macros.importTiddlers.coreHandler)
config.macros.importTiddlers.coreHandler.apply(this,arguments);
else
createTiddlyButton(place,this.label,this.prompt,onClickImportMenu);
} else if (params[0]=='link') { // show link to floating panel
createTiddlyButton(place,params[1]||this.label,params[2]||this.prompt,onClickImportMenu);
} else if (params[0]=='inline') {// show panel as INLINE tiddler content
createImportPanel(place);
document.getElementById("importPanel").style.position="static";
document.getElementById("importPanel").style.display="block";
} else if (config.macros.loadTiddlers)
config.macros.loadTiddlers.handler(place,macroName,params); // any other params: loadtiddlers
}
// // ''INTERFACE DEFINITION''
// // Handle link click to create/show/hide control panel
//{{{
function onClickImportMenu(e)
{
if (!e) var e = window.event;
var parent=resolveTarget(e).parentNode;
var panel = document.getElementById("importPanel");
if (panel==undefined || panel.parentNode!=parent)
panel=createImportPanel(parent);
var isOpen = panel.style.display=="block";
if(config.options.chkAnimate)
anim.startAnimating(new Slider(panel,!isOpen,e.shiftKey || e.altKey,"none"));
else
panel.style.display = isOpen ? "none" : "block" ;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
return(false);
}
//}}}
// // Create control panel: HTML, CSS
//{{{
function createImportPanel(place) {
var cmi=config.macros.importTiddlers; // abbreviation
var panel=document.getElementById("importPanel");
if (panel) { panel.parentNode.removeChild(panel); }
setStylesheet(cmi.css,"importTiddlers");
panel=createTiddlyElement(place,"span","importPanel",null,null)
panel.innerHTML=cmi.html;
refreshImportList();
var siteURL=store.getTiddlerText("SiteUrl"); if (!siteURL) siteURL="";
document.getElementById("importSourceURL").value=siteURL;
cmi.src=siteURL;
var siteProxy=store.getTiddlerText("SiteProxy"); if (!siteProxy) siteProxy="SiteProxy";
document.getElementById("importSiteProxy").value=siteProxy;
cmi.proxy=siteProxy;
if (config.browser.isGecko) { // FF3 FIXUP
document.getElementById("fileImportSource").style.display="none";
document.getElementById("importLocalPanelFix").style.display="block";
}
return panel;
}
//}}}
// // CSS
//{{{
config.macros.importTiddlers.css = '\
#importPanel {\
display: none; position:absolute; z-index:11; width:35em; right:105%; top:3em;\
background-color: #eee; color:#000; font-size: 8pt; line-height:110%;\
border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;\
padding: 0.5em; margin:0em; -moz-border-radius:1em;\
}\
#importPanel a, #importPanel td a { color:#009; display:inline; margin:0px; padding:1px; }\
#importPanel table { width:100%; border:0px; padding:0px; margin:0px; font-size:8pt; line-height:110%; background:transparent; }\
#importPanel tr { border:0px;padding:0px;margin:0px; background:transparent; }\
#importPanel td { color:#000; border:0px;padding:0px;margin:0px; background:transparent; }\
#importPanel select { width:100%;margin:0px;font-size:8pt;line-height:110%;}\
#importPanel input { width:98%;padding:0px;margin:0px;font-size:8pt;line-height:110%}\
#importPanel .box { border:1px solid #000; background-color:#eee; padding:3px 5px; margin-bottom:5px; -moz-border-radius:5px;}\
#importPanel .topline { border-top:1px solid #999; padding-top:2px; margin-top:2px; }\
#importPanel .rad { width:auto; }\
#importPanel .chk { width:auto; margin:1px;border:0; }\
#importPanel .btn { width:auto; }\
#importPanel .btn1 { width:98%; }\
#importPanel .btn2 { width:48%; }\
#importPanel .btn3 { width:32%; }\
#importPanel .btn4 { width:23%; }\
#importPanel .btn5 { width:19%; }\
#importPanel .importButton { padding: 0em; margin: 0px; font-size:8pt; }\
#importPanel .importListButton { padding:0em 0.25em 0em 0.25em; color: #000000; display:inline }\
#importCollisionPanel { display:none; margin:0.5em 0em 0em 0em; }\
#backstagePanel #importPanel { left:10%; right:auto; }\
';
//}}}
// // HTML
//{{{
config.macros.importTiddlers.html = '\
<!-- source and report -->\
<table><tr><td align=left>\
import from\
<input type="radio" class="rad" name="importFrom" id="importFromFile" value="file" CHECKED\
onclick="onClickImportButton(this,event)" title="show file controls"> local file\
<input type="radio" class="rad" name="importFrom" id="importFromWeb" value="http"\
onclick="onClickImportButton(this,event)" title="show web controls"> web server\
</td><td align=right>\
<input type=checkbox class="chk" id="chkImportReport" checked\
onClick="config.options[\'chkImportReport\']=this.checked;"> create report\
</td></tr></table>\
\
<div class="box" id="importSourcePanel" style="margin:.5em">\
<div id="importLocalPanel" style="display:block;margin-bottom:2px;"><!-- import from local file -->\
enter or browse for source path/filename<br>\
<input type="file" id="fileImportSource" size=57 style="width:100%"\
onKeyUp="config.macros.importTiddlers.src=this.value"\
onChange="config.macros.importTiddlers.src=this.value;document.getElementById(\'importLoad\').onclick()">\
<div id="importLocalPanelFix" style="display:none"><!-- FF3 FIXUP -->\
<input type="text" id="fileImportSourceFix" style="width:90%"\
title="Enter a path/file to import"\
onKeyUp="config.macros.importTiddlers.src=this.value"\
onChange="config.macros.importTiddlers.src=this.value; document.getElementById(\'importLoad\').onclick()">\
<input type="button" id="fileImportSourceFixButton" style="width:7%" value="..."\
title="Select a path/file to import"\
onClick="var r=config.macros.importTiddlers.askForFilename(this); if (!r||!r.length) return;\
document.getElementById(\'fileImportSourceFix\').value=r;\
config.macros.importTiddlers.src=r;\
document.getElementById(\'importLoad\').onclick()">\
</div><!--end FF3 FIXUP-->\
</div><!--end local-->\
<div id="importHTTPPanel" style="display:none;margin-bottom:2px;"><!-- import from http server -->\
<table><tr><td align=left>\
enter a URL or <a href="javascript:;" id="importSelectFeed"\
onclick="onClickImportButton(this,event)" title="select a pre-defined \'systemServer\' URL">\
select a server</a><br>\
</td><td align=right>\
<input type="checkbox" class="chk" id="importUsePassword"\
onClick="config.macros.importTiddlers.usePassword=this.checked;\
config.macros.importTiddlers.showPanel(\'importIDPWPanel\',this.checked);">password\
<input type="checkbox" class="chk" id="importUseProxy"\
onClick="config.macros.importTiddlers.useProxy=this.checked;\
config.macros.importTiddlers.showPanel(\'importSiteProxy\',this.checked);">proxy\
</td></tr></table>\
<input type="text" id="importSiteProxy" style="display:none;margin-bottom:1px" onfocus="this.select()" value="SiteProxy"\
onKeyUp="config.macros.importTiddlers.proxy=this.value"\
onChange="config.macros.importTiddlers.proxy=this.value;">\
<input type="text" id="importSourceURL" onfocus="this.select()" value="SiteUrl"\
onKeyUp="config.macros.importTiddlers.src=this.value"\
onChange="config.macros.importTiddlers.src=this.value;">\
<div id="importIDPWPanel" style="text-align:center;margin-top:2px;display:none";>\
username: <input type=text id="txtImportID" style="width:25%" \
onChange="config.options.txtRemoteUsername=this.value;">\
password: <input type=password id="txtImportPW" style="width:25%" \
onChange="config.options.txtRemotePassword=this.value;">\
</div><!--end idpw-->\
</div><!--end http-->\
</div><!--end source-->\
\
<div class="box" id="importSelectPanel" style="display:none;margin:.5em;">\
<table><tr><td align=left>\
select:\
<a href="javascript:;" id="importSelectAll"\
onclick="onClickImportButton(this);return false;" title="SELECT all tiddlers">\
all</a>\
<a href="javascript:;" id="importSelectNew"\
onclick="onClickImportButton(this);return false;" title="SELECT tiddlers not already in destination document">\
added</a>\
<a href="javascript:;" id="importSelectChanges"\
onclick="onClickImportButton(this);return false;" title="SELECT tiddlers that have been updated in source document">\
changes</a>\
<a href="javascript:;" id="importSelectDifferences"\
onclick="onClickImportButton(this);return false;" title="SELECT tiddlers that have been added or are different from existing tiddlers">\
differences</a>\
</td><td align=right>\
<a href="javascript:;" id="importListSmaller"\
onclick="onClickImportButton(this);return false;" title="SHRINK list size">\
– </a>\
<a href="javascript:;" id="importListLarger"\
onclick="onClickImportButton(this);return false;" title="GROW list size">\
+ </a>\
<a href="javascript:;" id="importListMaximize"\
onclick="onClickImportButton(this);return false;" title="MAXIMIZE/RESTORE list size">\
= </a>\
</td></tr></table>\
<select id="importList" size=8 multiple\
onchange="setTimeout(\'refreshImportList(\'+this.selectedIndex+\')\',1)">\
<!-- NOTE: delay refresh so list is updated AFTER onchange event is handled -->\
</select>\
<div style="text-align:center">\
<a href="javascript:;"\
title="click for help using filters..."\
onclick="alert(\'A filter consists of one or more space-separated combinations of:\\n\\ntiddler titles\\ntag:[[tagvalue]]\\ntag:[[tag expression]] (requires MatchTagsPlugin)\\nstory:[[TiddlerName]]\\nsearch:[[searchtext]]\\n\\nUse a blank filter for all tiddlers.\')"\
>filter</a>\
<input type="text" id="importLastFilter" style="margin-bottom:1px; width:65%"\
title="Enter a combination of one or more filters. Use a blank filter for all tiddlers."\
onfocus="this.select()" value=""\
onKeyUp="config.macros.importTiddlers.lastFilter=this.value"\
onChange="config.macros.importTiddlers.lastFilter=this.value;">\
<input type="button" id="importApplyFilter" style="width:20%" value="apply"\
title="filter list of tiddlers to include only those that match certain criteria"\
onclick="onClickImportButton(this)">\
</div>\
</div><!--end select-->\
\
<div class="box" id="importOptionsPanel" style="text-align:center;margin:.5em;display:none;">\
apply tags: <input type=checkbox class="chk" id="chkImportTags" checked\
onClick="config.macros.importTiddlers.importTags=this.checked;">from source \
<input type=checkbox class="chk" id="chkKeepTags" checked\
onClick="config.macros.importTiddlers.keepTags=this.checked;">keep existing \
<input type=checkbox class="chk" id="chkAddTags" \
onClick="config.macros.importTiddlers.addTags=this.checked;\
if (this.checked) document.getElementById(\'txtNewTags\').focus();">add new<br>\
<input type=text id="txtNewTags" style="margin-top:4px;" size=15\ onfocus="this.select()" \
title="enter tags to be added to imported tiddlers" \
onKeyUp="config.macros.importTiddlers.newTags=this.value;\
document.getElementById(\'chkAddTags\').checked=this.value.length>0;" autocomplete=off>\
<nobr><input type=checkbox class="chk" id="chkSync" \
onClick="config.macros.importTiddlers.sync=this.checked;">\
link imported tiddlers to source document (for sync later)</nobr>\
</div><!--end options-->\
\
<div id="importButtonPanel" style="text-align:center">\
<input type=button id="importLoad" class="importButton btn3" value="open"\
title="load listbox with tiddlers from source document"\
onclick="onClickImportButton(this)">\
<input type=button id="importOptions" class="importButton btn3" value="options..."\
title="set options for tags, sync, etc."\
onclick="onClickImportButton(this)">\
<input type=button id="importStart" class="importButton btn3" value="import"\
title="start/stop import of selected source tiddlers into current document"\
onclick="onClickImportButton(this)">\
<input type=button id="importClose" class="importButton btn3" value="done"\
title="clear listbox or hide control panel"\
onclick="onClickImportButton(this)">\
</div>\
\
<div class="none" id="importCollisionPanel" style="text-align:left;margin:0 0 0 .5em">\
<table><tr><td style="width:65%" align="left">\
<table><tr>\
<td align=left>\
tiddler already exists:\
</td><td align=right>\
<input type=checkbox class="chk" id="importApplyToAll" \
onclick="document.getElementById(\'importRename\').disabled=this.checked;"\
checked>apply to all\
</td></tr></table>\
<input type=text id="importNewTitle" size=15 autocomplete=off">\
</td><td style="width:34%" align="center">\
<input type=button id="importMerge"\
class="importButton" style="width:47%" value="merge"\
title="append the incoming tiddler to the existing tiddler"\
onclick="onClickImportButton(this)"><!--\
--><input type=button id="importSkip"\
class="importButton" style="width:47%" value="skip"\
title="do not import this tiddler"\
onclick="onClickImportButton(this)"><!--\
--><br><input type=button id="importRename"\
class="importButton" style="width:47%" value="rename"\
title="rename the incoming tiddler"\
onclick="onClickImportButton(this)"><!--\
--><input type=button id="importReplace"\
class="importButton" style="width:47%" value="replace"\
title="discard the existing tiddler"\
onclick="onClickImportButton(this)">\
</td></tr></table>\
</div><!--end collision-->\
';
//}}}
// // Control interactions
//{{{
function onClickImportButton(which,event)
{
var cmi=config.macros.importTiddlers; // abbreviation
var list = document.getElementById('importList');
if (!list) return;
var thePanel = document.getElementById('importPanel');
var theCollisionPanel = document.getElementById('importCollisionPanel');
var theNewTitle = document.getElementById('importNewTitle');
var count=0;
switch (which.id)
{
case 'importFromFile': // show local panel
case 'importFromWeb': // show HTTP panel
cmi.local=(which.id=='importFromFile');
cmi.showPanel('importLocalPanel',cmi.local);
cmi.showPanel('importHTTPPanel',!cmi.local);
break;
case 'importOptions': // show/hide options panel
cmi.showPanel('importOptionsPanel',document.getElementById('importOptionsPanel').style.display=='none');
break;
case 'fileImportSource':
case 'importLoad': // load import source into hidden frame
importReport(); // if an import was in progress, generate a report
cmi.inbound=null; // clear the imported tiddler buffer
refreshImportList(); // reset/resize the listbox
if (cmi.src=="") break;
// Load document, read it's DOM and fill the list
cmi.loadRemoteFile(cmi.src,cmi.filterTiddlerList);
break;
case 'importSelectFeed': // select a pre-defined systemServer feed URL
var p=Popup.create(which); if (!p) return;
var tids=store.getTaggedTiddlers('systemServer');
if (!tids.length)
createTiddlyText(createTiddlyElement(p,'li'),'no pre-defined server feeds');
for (var t=0; t<tids.length; t++) {
var u=store.getTiddlerSlice(tids[t].title,"URL");
var d=store.getTiddlerSlice(tids[t].title,"Description");
if (!d||!d.length) d=store.getTiddlerSlice(tids[t].title,"description");
if (!d||!d.length) d=u;
createTiddlyButton(createTiddlyElement(p,'li'),tids[t].title,d,
function(){
var u=this.getAttribute('url');
document.getElementById('importSourceURL').value=u;
config.macros.importTiddlers.src=u;
document.getElementById('importLoad').onclick();
},
null,null,null,{url:u});
}
Popup.show(p,false);
event.cancelBubble = true;
if (event.stopPropagation) event.stopPropagation();
return(false);
// create popup with feed list
// onselect, insert feed URL into input field.
break;
case 'importSelectAll': // select all tiddler list items (i.e., not headings)
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < list.options.length; t++) {
if (list.options[t].value=="") continue;
list.options[t].selected=true;
count++;
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
document.getElementById('importStart').disabled=!count;
document.getElementById('importStart').style.visibility=count?"visible":"hidden";
break;
case 'importSelectNew': // select tiddlers not in current document
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < list.options.length; t++) {
list.options[t].selected=false;
if (list.options[t].value=="") continue;
list.options[t].selected=!store.tiddlerExists(list.options[t].value);
count+=list.options[t].selected?1:0;
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
document.getElementById('importStart').disabled=!count;
document.getElementById('importStart').style.visibility=count?"visible":"hidden";
break;
case 'importSelectChanges': // select tiddlers that are updated from existing tiddlers
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < list.options.length; t++) {
list.options[t].selected=false;
if (list.options[t].value==""||!store.tiddlerExists(list.options[t].value)) continue;
for (var i=0; i<cmi.inbound.length; i++) // find matching inbound tiddler
{ var inbound=cmi.inbound[i]; if (inbound.title==list.options[t].value) break; }
list.options[t].selected=(inbound.modified-store.getTiddler(list.options[t].value).modified>0); // updated tiddler
count+=list.options[t].selected?1:0;
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
document.getElementById('importStart').disabled=!count;
document.getElementById('importStart').style.visibility=count?"visible":"hidden";
break;
case 'importSelectDifferences': // select tiddlers that are new or different from existing tiddlers
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < list.options.length; t++) {
list.options[t].selected=false;
if (list.options[t].value=="") continue;
if (!store.tiddlerExists(list.options[t].value)) { list.options[t].selected=true; count++; continue; }
for (var i=0; i<cmi.inbound.length; i++) // find matching inbound tiddler
{ var inbound=cmi.inbound[i]; if (inbound.title==list.options[t].value) break; }
list.options[t].selected=(inbound.modified-store.getTiddler(list.options[t].value).modified!=0); // changed tiddler
count+=list.options[t].selected?1:0;
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
document.getElementById('importStart').disabled=!count;
document.getElementById('importStart').style.visibility=count?"visible":"hidden";
break;
case 'importApplyFilter': // filter list to include only matching tiddlers
importReport(); // if an import was in progress, generate a report
clearMessage();
if (!cmi.all) // no tiddlers loaded = "0 selected"
{ displayMessage(cmi.countMsg.format([0])); return false; }
var hash=document.getElementById('importLastFilter').value;
cmi.inbound=cmi.filterByHash("#"+hash,cmi.all);
refreshImportList(); // reset/resize the listbox
break;
case 'importStart': // initiate the import processing
importReport(); // if an import was in progress, generate a report
document.getElementById('importApplyToAll').checked=false;
document.getElementById('importStart').value=cmi.stopText;
if (cmi.index>0) cmi.index=-1; // stop processing
else cmi.index=importTiddlers(0); // or begin processing
importStopped();
break;
case 'importClose': // unload imported tiddlers or hide the import control panel
// if imported tiddlers not loaded, close the import control panel
if (!cmi.inbound) { thePanel.style.display='none'; break; }
importReport(); // if an import was in progress, generate a report
cmi.inbound=null; // clear the imported tiddler buffer
refreshImportList(); // reset/resize the listbox
break;
case 'importSkip': // don't import the tiddler
cmi.lastAction=which;
var theItem = list.options[cmi.index];
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==theItem.value) break;
var theImported = cmi.inbound[j];
theImported.status='skipped after asking'; // mark item as skipped
theCollisionPanel.style.display='none';
cmi.index=importTiddlers(cmi.index+1); // resume with NEXT item
importStopped();
break;
case 'importRename': // change name of imported tiddler
cmi.lastAction=which;
var theItem = list.options[cmi.index];
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==theItem.value) break;
var theImported = cmi.inbound[j];
theImported.status = 'renamed from '+theImported.title; // mark item as renamed
theImported.set(theNewTitle.value,null,null,null,null); // change the tiddler title
theItem.value = theNewTitle.value; // change the listbox item text
theItem.text = theNewTitle.value; // change the listbox item text
theCollisionPanel.style.display='none';
cmi.index=importTiddlers(cmi.index); // resume with THIS item
importStopped();
break;
case 'importMerge': // join existing and imported tiddler content
cmi.lastAction=which;
var theItem = list.options[cmi.index];
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==theItem.value) break;
var theImported = cmi.inbound[j];
var theExisting = store.getTiddler(theItem.value);
var theText = theExisting.text+'\n----\n^^merged from: ';
theText +='[['+cmi.src+'#'+theItem.value+'|'+cmi.src+'#'+theItem.value+']]^^\n';
theText +='^^'+theImported.modified.toLocaleString()+' by '+theImported.modifier+'^^\n'+theImported.text;
var theDate = new Date();
var theTags = theExisting.getTags()+' '+theImported.getTags();
theImported.set(null,theText,null,theDate,theTags);
theImported.status = 'merged with '+theExisting.title; // mark item as merged
theImported.status += ' - '+theExisting.modified.formatString("MM/DD/YYYY 0hh:0mm:0ss");
theImported.status += ' by '+theExisting.modifier;
theCollisionPanel.style.display='none';
cmi.index=importTiddlers(cmi.index); // resume with this item
importStopped();
break;
case 'importReplace': // substitute imported tiddler for existing tiddler
cmi.lastAction=which;
var theItem = list.options[cmi.index];
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==theItem.value) break;
var theImported = cmi.inbound[j];
var theExisting = store.getTiddler(theItem.value);
theImported.status = 'replaces '+theExisting.title; // mark item for replace
theImported.status += ' - '+theExisting.modified.formatString("MM/DD/YYYY 0hh:0mm:0ss");
theImported.status += ' by '+theExisting.modifier;
theCollisionPanel.style.display='none';
cmi.index=importTiddlers(cmi.index); // resume with THIS item
importStopped();
break;
case 'importListSmaller': // decrease current listbox size, minimum=5
if (list.options.length==1) break;
list.size-=(list.size>5)?1:0;
cmi.listsize=list.size;
break;
case 'importListLarger': // increase current listbox size, maximum=number of items in list
if (list.options.length==1) break;
list.size+=(list.size<list.options.length)?1:0;
cmi.listsize=list.size;
break;
case 'importListMaximize': // toggle listbox size between current and maximum
if (list.options.length==1) break;
list.size=(list.size==list.options.length)?cmi.listsize:list.options.length;
break;
}
}
//}}}
// // toggle panel
//{{{
config.macros.importTiddlers.showPanel=function(place,show) {
if (typeof place == "string") var place=document.getElementById(place);
if (!place||!place.style) return;
if(anim && config.options.chkAnimate) anim.startAnimating(new Slider(place,show,false,"none"));
else place.style.display=show?"block":"none";
}
//}}}
// // refresh listbox
//{{{
function refreshImportList(selectedIndex)
{
var cmi=config.macros.importTiddlers; // abbreviation
var list = document.getElementById("importList");
if (!list) return;
// if nothing to show, reset list content and size
if (!cmi.inbound)
{
while (list.length > 0) { list.options[0] = null; }
list.options[0]=new Option(cmi.loadText,"",false,false);
list.size=cmi.listsize;
// toggle buttons and panels
document.getElementById('importLoad').disabled=false;
document.getElementById('importLoad').style.display='inline';
document.getElementById('importStart').disabled=true;
document.getElementById('importStart').style.visibility="hidden";
document.getElementById('importOptions').disabled=true;
document.getElementById('importOptions').style.display='none';
document.getElementById('fileImportSource').disabled=false;
document.getElementById('importFromFile').disabled=false;
document.getElementById('importFromWeb').disabled=false;
document.getElementById('importStart').value=cmi.startText;
document.getElementById('importClose').value=cmi.doneText;
document.getElementById('importSelectPanel').style.display='none';
document.getElementById('importOptionsPanel').style.display='none';
return;
}
// there are inbound tiddlers loaded...
// toggle buttons and panels
document.getElementById('importLoad').disabled=true;
document.getElementById('importLoad').style.display='none';
document.getElementById('importStart').style.visibility='visible';
document.getElementById('importOptions').style.display='inline';
document.getElementById('importOptions').disabled=false;
document.getElementById('fileImportSource').disabled=true;
document.getElementById('importFromFile').disabled=true;
document.getElementById('importFromWeb').disabled=true;
document.getElementById('importClose').value=cmi.closeText;
if (document.getElementById('importSelectPanel').style.display=='none')
cmi.showPanel('importSelectPanel',true);
// get the sort order
if (!selectedIndex) selectedIndex=0;
if (selectedIndex==0) cmi.sort='title'; // heading
if (selectedIndex==1) cmi.sort='title';
if (selectedIndex==2) cmi.sort='modified';
if (selectedIndex==3) cmi.sort='tags';
if (selectedIndex>3) {
// display selected tiddler count
for (var t=0,count=0; t < list.options.length; t++) {
if (!list.options[t].selected) continue;
if (list.options[t].value!="")
count+=1;
else { // if heading is selected, deselect it, and then select and count all in section
list.options[t].selected=false;
for ( t++; t<list.options.length && list.options[t].value!=""; t++) {
list.options[t].selected=true;
count++;
}
}
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
}
document.getElementById('importStart').disabled=!count;
if (selectedIndex>3) return; // no refresh needed
// get the alphasorted list of tiddlers
var tiddlers=cmi.inbound;
tiddlers.sort(function (a,b) {if(a['title'] == b['title']) return(0); else return (a['title'] < b['title']) ? -1 : +1; });
// clear current list contents
while (list.length > 0) { list.options[0] = null; }
// add heading and control items to list
var i=0;
var indent=String.fromCharCode(160)+String.fromCharCode(160);
if (cmi.all.length==tiddlers.length)
var summary=cmi.summaryMsg.format([tiddlers.length,(tiddlers.length!=1)?cmi.plural:cmi.single]);
else
var summary=cmi.summaryFilteredMsg.format([tiddlers.length,cmi.all.length,(cmi.all.length!=1)?cmi.plural:cmi.single]);
list.options[i++]=new Option(summary,"",false,false);
list.options[i++]=new Option(((cmi.sort=="title" )?">":indent)+' [by title]',"",false,false);
list.options[i++]=new Option(((cmi.sort=="modified")?">":indent)+' [by date]',"",false,false);
list.options[i++]=new Option(((cmi.sort=="tags")?">":indent)+' [by tags]',"",false,false);
// output the tiddler list
switch(cmi.sort) {
case "title":
for(var t = 0; t < tiddlers.length; t++)
list.options[i++] = new Option(tiddlers[t].title,tiddlers[t].title,false,false);
break;
case "modified":
// sort descending for newest date first
tiddlers.sort(function (a,b) {if(a['modified'] == b['modified']) return(0); else return (a['modified'] > b['modified']) ? -1 : +1; });
var lastSection = "";
for(var t = 0; t < tiddlers.length; t++) {
var tiddler = tiddlers[t];
var theSection = tiddler.modified.toLocaleDateString();
if (theSection != lastSection) {
list.options[i++] = new Option(theSection,"",false,false);
lastSection = theSection;
}
list.options[i++] = new Option(indent+indent+tiddler.title,tiddler.title,false,false);
}
break;
case "tags":
var theTitles = {}; // all tiddler titles, hash indexed by tag value
var theTags = new Array();
for(var t=0; t<tiddlers.length; t++) {
var title=tiddlers[t].title;
var tags=tiddlers[t].tags;
if (!tags || !tags.length) {
if (theTitles["untagged"]==undefined) { theTags.push("untagged"); theTitles["untagged"]=new Array(); }
theTitles["untagged"].push(title);
}
else for(var s=0; s<tags.length; s++) {
if (theTitles[tags[s]]==undefined) { theTags.push(tags[s]); theTitles[tags[s]]=new Array(); }
theTitles[tags[s]].push(title);
}
}
theTags.sort();
for(var tagindex=0; tagindex<theTags.length; tagindex++) {
var theTag=theTags[tagindex];
list.options[i++]=new Option(theTag,"",false,false);
for(var t=0; t<theTitles[theTag].length; t++)
list.options[i++]=new Option(indent+indent+theTitles[theTag][t],theTitles[theTag][t],false,false);
}
break;
}
list.selectedIndex=selectedIndex; // select current control item
if (list.size<cmi.listsize) list.size=cmi.listsize;
if (list.size>list.options.length) list.size=list.options.length;
}
config.macros.importTiddlers.filterTiddlerList=function(success,params,txt,src,xhr) {
var cmi=config.macros.importTiddlers; // abbreviation
var src=src.replace(/%20/g," ");
if (!success) { displayMessage(cmi.openErrMsg.format([src,xhr.status])); return; }
cmi.all = cmi.readTiddlersFromHTML(txt);
var count=cmi.all?cmi.all.length:0;
var querypos=src.lastIndexOf("?"); if (querypos!=-1) src=src.substr(0,querypos);
displayMessage(cmi.foundMsg.format([count,src]));
cmi.inbound=cmi.filterByHash(params,cmi.all); // use full URL including hash (if any)
document.getElementById("importLastFilter").value=cmi.lastFilter;
window.refreshImportList(0);
}
config.macros.importTiddlers.filterByHash=function(src,tiddlers)
{
var hashpos=src.lastIndexOf("#"); if (hashpos==-1) return tiddlers;
var hash=src.substr(hashpos+1); if (!hash.length) return tiddlers;
var tids=[];
var params=hash.parseParams("anon",null,true,false,false);
for (var p=1; p<params.length; p++) {
switch (params[p].name) {
case "anon":
case "open":
tids.pushUnique(params[p].value);
break;
case "tag":
if (store.getMatchingTiddlers) { // for boolean expressions - see MatchTagsPlugin
var r=store.getMatchingTiddlers(params[p].value,null,tiddlers);
for (var t=0; t<r.length; t++) tids.pushUnique(r[t].title);
} else for (var t=0; t<tiddlers.length; t++)
if (tiddlers[t].isTagged(params[p].value))
tids.pushUnique(tiddlers[t].title);
break;
case "story":
for (var t=0; t<tiddlers.length; t++)
if (tiddlers[t].title==params[p].value) {
tiddlers[t].changed();
for (var s=0; s<tiddlers[t].links.length; s++)
tids.pushUnique(tiddlers[t].links[s]);
break;
}
break;
case "search":
for (var t=0; t<tiddlers.length; t++)
if (tiddlers[t].text.indexOf(params[p].value)!=-1)
tids.pushUnique(tiddlers[t].title);
break;
}
}
var matches=[];
for (var t=0; t<tiddlers.length; t++)
if (tids.contains(tiddlers[t].title))
matches.push(tiddlers[t]);
displayMessage(config.macros.importTiddlers.filterMsg.format([matches.length,hash]));
config.macros.importTiddlers.lastFilter=hash;
return matches;
}
//}}}
// // re-entrant processing for handling import with interactive collision prompting
//{{{
function importTiddlers(startIndex)
{
var cmi=config.macros.importTiddlers; // abbreviation
if (!cmi.inbound) return -1;
var list = document.getElementById('importList');
if (!list) return;
var t;
// if starting new import, reset import status flags
if (startIndex==0)
for (var t=0;t<cmi.inbound.length;t++)
cmi.inbound[t].status="";
for (var i=startIndex; i<list.options.length; i++)
{
// if list item is not selected or is a heading (i.e., has no value), skip it
if ((!list.options[i].selected) || ((t=list.options[i].value)==""))
continue;
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==t) break;
var inbound = cmi.inbound[j];
var theExisting = store.getTiddler(inbound.title);
// avoid redundant import for tiddlers that are listed multiple times (when 'by tags')
if (inbound.status=="added")
continue;
// don't import the "ImportedTiddlers" history from the other document...
if (inbound.title=='ImportedTiddlers')
continue;
// if tiddler exists and import not marked for replace or merge, stop importing
if (theExisting && (inbound.status.substr(0,7)!="replace") && (inbound.status.substr(0,5)!="merge"))
return i;
// assemble tags (remote + existing + added)
var newTags = "";
if (cmi.importTags)
newTags+=inbound.getTags() // import remote tags
if (cmi.keepTags && theExisting)
newTags+=" "+theExisting.getTags(); // keep existing tags
if (cmi.addTags && cmi.newTags.trim().length)
newTags+=" "+cmi.newTags; // add new tags
inbound.set(null,null,null,null,newTags.trim());
// set the status to 'added' (if not already set by the 'ask the user' UI)
inbound.status=(inbound.status=="")?'added':inbound.status;
// set sync fields
if (cmi.sync) {
if (!inbound.fields) inbound.fields={}; // for TW2.1.x backward-compatibility
inbound.fields["server.page.revision"]=inbound.modified.convertToYYYYMMDDHHMM();
inbound.fields["server.type"]="file";
inbound.fields["server.host"]=(cmi.local?"file://":"")+cmi.src;
}
// do the import!
store.suspendNotifications();
store.saveTiddler(inbound.title, inbound.title, inbound.text, inbound.modifier, inbound.modified, inbound.tags, inbound.fields, true, inbound.created);
store.fetchTiddler(inbound.title).created = inbound.created; // force creation date to imported value (needed for TW2.1.x and earlier)
store.resumeNotifications();
}
return(-1); // signals that we really finished the entire list
}
//}}}
//{{{
function importStopped()
{
var cmi=config.macros.importTiddlers; // abbreviation
var list = document.getElementById('importList');
var theNewTitle = document.getElementById('importNewTitle');
if (!list) return;
if (cmi.index==-1){
document.getElementById('importStart').value=cmi.startText;
importReport(); // import finished... generate the report
} else {
// import collision...
// show the collision panel and set the title edit field
document.getElementById('importStart').value=cmi.stopText;
cmi.showPanel('importCollisionPanel',true);
theNewTitle.value=list.options[cmi.index].value;
if (document.getElementById('importApplyToAll').checked
&& cmi.lastAction
&& cmi.lastAction.id!="importRename") {
onClickImportButton(cmi.lastAction);
}
}
}
//}}}
// // ''REPORT GENERATOR''
//{{{
function importReport()
{
var cmi=config.macros.importTiddlers; // abbreviation
if (!cmi.inbound) return;
// if import was not completed, the collision panel will still be open... close it now.
var panel=document.getElementById('importCollisionPanel'); if (panel) panel.style.display='none';
// get the alphasorted list of tiddlers
var tiddlers = cmi.inbound;
// gather the statistics
var count=0; var total=0;
for (var t=0; t<tiddlers.length; t++) {
if (!tiddlers[t].status || !tiddlers[t].status.trim().length) continue;
if (tiddlers[t].status.substr(0,7)!="skipped") count++;
total++;
}
// generate a report
if (total) displayMessage(cmi.processedMsg.format([total]));
if (count && config.options.chkImportReport) {
// get/create the report tiddler
var theReport = store.getTiddler('ImportedTiddlers');
if (!theReport) { theReport= new Tiddler(); theReport.title = 'ImportedTiddlers'; theReport.text = ""; }
// format the report content
var now = new Date();
var newText = "On "+now.toLocaleString()+", "+config.options.txtUserName
newText +=" imported "+count+" tiddler"+(count==1?"":"s")+" from\n[["+cmi.src+"|"+cmi.src+"]]:\n";
if (cmi.addTags && cmi.newTags.trim().length)
newText += "imported tiddlers were tagged with: \""+cmi.newTags+"\"\n";
newText += "<<<\n";
for (var t=0; t<tiddlers.length; t++) if (tiddlers[t].status) newText += "#[["+tiddlers[t].title+"]] - "+tiddlers[t].status+"\n";
newText += "<<<\n";
// update the ImportedTiddlers content and show the tiddler
theReport.text = newText+((theReport.text!="")?'\n----\n':"")+theReport.text;
theReport.modifier = config.options.txtUserName;
theReport.modified = new Date();
store.saveTiddler(theReport.title, theReport.title, theReport.text, theReport.modifier, theReport.modified, theReport.tags, theReport.fields);
story.displayTiddler(null,theReport.title,1,null,null,false);
story.refreshTiddler(theReport.title,1,true);
}
// reset status flags
for (var t=0; t<cmi.inbound.length; t++) cmi.inbound[t].status="";
// mark document as dirty and let display update as needed
if (count) { store.setDirty(true); store.notifyAll(); }
// always show final message when tiddlers were actually loaded
if (count) displayMessage(cmi.importedMsg.format([count,tiddlers.length,cmi.src.replace(/%20/g," ")]));
}
//}}}
// // File and XMLHttpRequest I/O
//{{{
config.macros.importTiddlers.askForFilename=function(here) {
var msg=here.title; // use tooltip as dialog box message
var path=getLocalPath(document.location.href);
var slashpos=path.lastIndexOf("/"); if (slashpos==-1) slashpos=path.lastIndexOf("\\");
if (slashpos!=-1) path = path.substr(0,slashpos+1); // remove filename from path, leave the trailing slash
var file="";
var result="";
if(window.Components) { // moz
try {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
picker.init(window, msg, nsIFilePicker.modeOpen);
var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
thispath.initWithPath(path);
picker.displayDirectory=thispath;
picker.defaultExtension='html';
picker.defaultString=file;
picker.appendFilters(nsIFilePicker.filterAll|nsIFilePicker.filterText|nsIFilePicker.filterHTML);
if (picker.show()!=nsIFilePicker.returnCancel) var result=picker.file.persistentDescriptor;
}
catch(e) { alert('error during local file access: '+e.toString()) }
}
else { // IE
try { // XPSP2 IE only
var s = new ActiveXObject('UserAccounts.CommonDialog');
s.Filter='All files|*.*|Text files|*.txt|HTML files|*.htm;*.html|';
s.FilterIndex=3; // default to HTML files;
s.InitialDir=path;
s.FileName=file;
if (s.showOpen()) var result=s.FileName;
}
catch(e) { // fallback
var result=prompt(msg,path+file);
}
}
return result;
}
config.macros.importTiddlers.fileExists=function(theFile) {
var found=false;
if(window.Components) {
try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); }
catch(e) { return false; } // security access denied
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
try { file.initWithPath(theFile); }
catch(e) { return false; } // invalid directory
found = file.exists();
}
else { // use ActiveX FSO object for MSIE
var fso = new ActiveXObject("Scripting.FileSystemObject");
found = fso.FileExists(theFile)
}
return found;
}
config.macros.importTiddlers.loadRemoteFile = function(src,callback) {
if (src==undefined || !src.length) return null; // filename is required
var original=src; // URL as specified
var hashpos=src.indexOf("#"); if (hashpos!=-1) src=src.substr(0,hashpos); // URL with #... suffix removed (needed for IE)
clearMessage();
displayMessage(this.openMsg.format([src.replace(/%20/g," ")]));
if (src.substr(0,5)!="http:" && src.substr(0,5)!="file:") { // if src is relative (i.e., not a URL)
if (!this.fileExists(src)) { // if file cannot be found, might be relative path.. try fixup
var pathPrefix=document.location.href; // get current document path and trim off filename
var slashpos=pathPrefix.lastIndexOf("/"); if (slashpos==-1) slashpos=pathPrefix.lastIndexOf("\\");
if (slashpos!=-1 && slashpos!=pathPrefix.length-1) pathPrefix=pathPrefix.substr(0,slashpos+1);
src=pathPrefix+src;
if (pathPrefix.substr(0,5)!="http:") src=getLocalPath(src);
}
}
if (src.substr(0,5)!="http:" && src.substr(0,5)!="file:") { // if not remote URL, read from local filesystem
var txt=loadFile(src);
if ((txt==null)||(txt==false)) // file didn't load
{ displayMessage(config.macros.importTiddlers.openErrMsg.format([src.replace(/%20/g," "),"(filesystem error)"])); }
else {
displayMessage(config.macros.importTiddlers.readMsg.format([txt.length,src.replace(/%20/g," ")]));
if (callback) callback(true,original,convertUTF8ToUnicode(txt),src,null);
}
}
else {
var name=config.options.txtRemoteUsername; var pass=config.options.txtRemotePassword;
var xhr=doHttp("GET",src,null,null,name,pass,callback,original,null)
if (!xhr) displayMessage(config.macros.importTiddlers.openErrMsg.format([src,"(XMLHTTPRequest error)"]));
}
}
config.macros.importTiddlers.readTiddlersFromHTML=function(html)
{
var remoteStore=new TiddlyWiki();
remoteStore.importTiddlyWiki(html);
return remoteStore.getTiddlers("title");
}
//}}}
On 2008年11月26日 上午 10:02:27, 比目魚 imported 1 tiddler from
[[G:\MCSE2008\network\network.html|G:\MCSE2008\network\network.html]]:
<<<
#[[00 - QoS]] - added
<<<
/***
|''Name:''|IntelliTaggerPlugin|
|''Version:''|1.0.2 (2007-07-25)|
|''Type:''|plugin|
|''Source:''|http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Documentation:''|[[IntelliTaggerPlugin Documentation]]|
|''~SourceCode:''|[[IntelliTaggerPlugin SourceCode]]|
|''Licence:''|[[BSD open source license (abego Software)]]|
|''~CoreVersion:''|2.0.8|
|''Browser:''|Firefox 1.5.0.2 or better|
***/
/***
!Version History
* 1.0.2 (2007-07-25):
** Feature: "Return" key may be used to accept first tag suggestion (beside "Alt-1")
** Bugfix: Keyboard shortcuts (Alt+3 etc.) shifted
* 1.0.1 (2007-05-18): Improvement: Speedup when using TiddlyWikis with many tags
* 1.0.0 (2006-04-26): Initial release
***/
// /%
if(!version.extensions.IntelliTaggerPlugin){if(!window.abego){window.abego={};}if(!abego.internal){abego.internal={};}abego.alertAndThrow=function(s){alert(s);throw s;};if(version.major<2){abego.alertAndThrow("Use TiddlyWiki 2.0.8 or better to run the IntelliTagger Plugin.");}version.extensions.IntelliTaggerPlugin={major:1,minor:0,revision:2,date:new Date(2007,6,25),type:"plugin",source:"http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin",documentation:"[[IntelliTaggerPlugin Documentation]]",sourcecode:"[[IntelliTaggerPlugin SourceCode]]",author:"Udo Borkowski (ub [at] abego-software [dot] de)",licence:"[[BSD open source license (abego Software)]]",tiddlywiki:"Version 2.0.8 or better",browser:"Firefox 1.5.0.2 or better"};abego.createEllipsis=function(_2){var e=createTiddlyElement(_2,"span");e.innerHTML="…";};abego.isPopupOpen=function(_4){return _4&&_4.parentNode==document.body;};abego.openAsPopup=function(_5){if(_5.parentNode!=document.body){document.body.appendChild(_5);}};abego.closePopup=function(_6){if(abego.isPopupOpen(_6)){document.body.removeChild(_6);}};abego.getWindowRect=function(){return {left:findScrollX(),top:findScrollY(),height:findWindowHeight(),width:findWindowWidth()};};abego.moveElement=function(_7,_8,_9){_7.style.left=_8+"px";_7.style.top=_9+"px";};abego.centerOnWindow=function(_a){if(_a.style.position!="absolute"){throw "abego.centerOnWindow: element must have absolute position";}var _b=abego.getWindowRect();abego.moveElement(_a,_b.left+(_b.width-_a.offsetWidth)/2,_b.top+(_b.height-_a.offsetHeight)/2);};abego.isDescendantOrSelf=function(_c,e){while(e){if(_c==e){return true;}e=e.parentNode;}return false;};abego.toSet=function(_e){var _f={};for(var i=0;i<_e.length;i++){_f[_e[i]]=true;}return _f;};abego.filterStrings=function(_11,_12,_13){var _14=[];for(var i=0;i<_11.length&&(_13===undefined||_14.length<_13);i++){var s=_11[i];if(s.match(_12)){_14.push(s);}}return _14;};abego.arraysAreEqual=function(a,b){if(!a){return !b;}if(!b){return false;}var n=a.length;if(n!=b.length){return false;}for(var i=0;i<n;i++){if(a[i]!=b[i]){return false;}}return true;};abego.moveBelowAndClip=function(_1b,_1c){if(!_1c){return;}var _1d=findPosX(_1c);var _1e=findPosY(_1c);var _1f=_1c.offsetHeight;var _20=_1d;var _21=_1e+_1f;var _22=findWindowWidth();if(_22<_1b.offsetWidth){_1b.style.width=(_22-100)+"px";}var _23=_1b.offsetWidth;if(_20+_23>_22){_20=_22-_23-30;}if(_20<0){_20=0;}_1b.style.left=_20+"px";_1b.style.top=_21+"px";_1b.style.display="block";};abego.compareStrings=function(a,b){return (a==b)?0:(a<b)?-1:1;};abego.sortIgnoreCase=function(arr){var _27=[];var n=arr.length;for(var i=0;i<n;i++){var s=arr[i];_27.push([s.toString().toLowerCase(),s]);}_27.sort(function(a,b){return (a[0]==b[0])?0:(a[0]<b[0])?-1:1;});for(i=0;i<n;i++){arr[i]=_27[i][1];}};abego.getTiddlerField=function(_2d,_2e,_2f){var _30=document.getElementById(_2d.idPrefix+_2e);var e=null;if(_30!=null){var _32=_30.getElementsByTagName("*");for(var t=0;t<_32.length;t++){var c=_32[t];if(c.tagName.toLowerCase()=="input"||c.tagName.toLowerCase()=="textarea"){if(!e){e=c;}if(c.getAttribute("edit")==_2f){e=c;}}}}return e;};abego.setRange=function(_35,_36,end){if(_35.setSelectionRange){_35.setSelectionRange(_36,end);var max=0+_35.scrollHeight;var len=_35.textLength;var top=max*_36/len,bot=max*end/len;_35.scrollTop=Math.min(top,(bot+top-_35.clientHeight)/2);}else{if(_35.createTextRange!=undefined){var _3b=_35.createTextRange();_3b.collapse();_3b.moveEnd("character",end);_3b.moveStart("character",_36);_3b.select();}else{_35.select();}}};abego.internal.TagManager=function(){var _3c=null;var _3d=function(){if(_3c){return;}_3c={};store.forEachTiddler(function(_3e,_3f){for(var i=0;i<_3f.tags.length;i++){var tag=_3f.tags[i];var _42=_3c[tag];if(!_42){_42=_3c[tag]={count:0,tiddlers:{}};}_42.tiddlers[_3f.title]=true;_42.count+=1;}});};var _43=TiddlyWiki.prototype.saveTiddler;TiddlyWiki.prototype.saveTiddler=function(_44,_45,_46,_47,_48,_49){var _4a=this.fetchTiddler(_44);var _4b=_4a?_4a.tags:[];var _4c=(typeof _49=="string")?_49.readBracketedList():_49;_43.apply(this,arguments);if(!abego.arraysAreEqual(_4b,_4c)){abego.internal.getTagManager().reset();}};var _4d=TiddlyWiki.prototype.removeTiddler;TiddlyWiki.prototype.removeTiddler=function(_4e){var _4f=this.fetchTiddler(_4e);var _50=_4f&&_4f.tags.length>0;_4d.apply(this,arguments);if(_50){abego.internal.getTagManager().reset();}};this.reset=function(){_3c=null;};this.getTiddlersWithTag=function(tag){_3d();var _52=_3c[tag];return _52?_52.tiddlers:null;};this.getAllTags=function(_53){_3d();var _54=[];for(var i in _3c){_54.push(i);}for(i=0;_53&&i<_53.length;i++){_54.pushUnique(_53[i],true);}abego.sortIgnoreCase(_54);return _54;};this.getTagInfos=function(){_3d();var _56=[];for(var _57 in _3c){_56.push([_57,_3c[_57]]);}return _56;};var _58=function(a,b){var a1=a[1];var b1=b[1];var d=b[1].count-a[1].count;return d!=0?d:abego.compareStrings(a[0].toLowerCase(),b[0].toLowerCase());};this.getSortedTagInfos=function(){_3d();var _5e=this.getTagInfos();_5e.sort(_58);return _5e;};this.getPartnerRankedTags=function(_5f){var _60={};for(var i=0;i<_5f.length;i++){var _62=this.getTiddlersWithTag(_5f[i]);for(var _63 in _62){var _64=store.getTiddler(_63);if(!(_64 instanceof Tiddler)){continue;}for(var j=0;j<_64.tags.length;j++){var tag=_64.tags[j];var c=_60[tag];_60[tag]=c?c+1:1;}}}var _68=abego.toSet(_5f);var _69=[];for(var n in _60){if(!_68[n]){_69.push(n);}}_69.sort(function(a,b){var d=_60[b]-_60[a];return d!=0?d:abego.compareStrings(a.toLowerCase(),b.toLowerCase());});return _69;};};abego.internal.getTagManager=function(){if(!abego.internal.gTagManager){abego.internal.gTagManager=new abego.internal.TagManager();}return abego.internal.gTagManager;};(function(){var _6e=2;var _6f=1;var _70=30;var _71;var _72;var _73;var _74;var _75;var _76;if(!abego.IntelliTagger){abego.IntelliTagger={};}var _77=function(){return _72;};var _78=function(tag){return _75[tag];};var _7a=function(s){var i=s.lastIndexOf(" ");return (i>=0)?s.substr(0,i):"";};var _7d=function(_7e){var s=_7e.value;var len=s.length;return (len>0&&s[len-1]!=" ");};var _81=function(_82){var s=_82.value;var len=s.length;if(len>0&&s[len-1]!=" "){_82.value+=" ";}};var _85=function(tag,_87,_88){if(_7d(_87)){_87.value=_7a(_87.value);}story.setTiddlerTag(_88.title,tag,0);_81(_87);abego.IntelliTagger.assistTagging(_87,_88);};var _89=function(n){if(_76&&_76.length>n){return _76[n];}return (_74&&_74.length>n)?_74[n]:null;};var _8b=function(n,_8d,_8e){var _8f=_89(n);if(_8f){_85(_8f,_8d,_8e);}};var _90=function(_91){var pos=_91.value.lastIndexOf(" ");var _93=(pos>=0)?_91.value.substr(++pos,_91.value.length):_91.value;return new RegExp(_93.escapeRegExp(),"i");};var _94=function(_95,_96){var _97=0;for(var i=0;i<_95.length;i++){if(_96[_95[i]]){_97++;}}return _97;};var _99=function(_9a,_9b,_9c){var _9d=1;var c=_9a[_9b];for(var i=_9b+1;i<_9a.length;i++){if(_9a[i][1].count==c){if(_9a[i][0].match(_9c)){_9d++;}}else{break;}}return _9d;};var _a0=function(_a1,_a2){var _a3=abego.internal.getTagManager().getSortedTagInfos();var _a4=[];var _a5=0;for(var i=0;i<_a3.length;i++){var c=_a3[i][1].count;if(c!=_a5){if(_a2&&(_a4.length+_99(_a3,i,_a1)>_a2)){break;}_a5=c;}if(c==1){break;}var s=_a3[i][0];if(s.match(_a1)){_a4.push(s);}}return _a4;};var _a9=function(_aa,_ab){return abego.filterStrings(abego.internal.getTagManager().getAllTags(_ab),_aa);};var _ac=function(){if(!_71){return;}var _ad=store.getTiddlerText("IntelliTaggerMainTemplate");if(!_ad){_ad="<b>Tiddler IntelliTaggerMainTemplate not found</b>";}_71.innerHTML=_ad;applyHtmlMacros(_71,null);refreshElements(_71,null);};var _ae=function(e){if(!e){var e=window.event;}var tag=this.getAttribute("tag");if(_73){_73.call(this,tag,e);}return false;};var _b2=function(_b3){createTiddlyElement(_b3,"span",null,"tagSeparator"," | ");};var _b4=function(_b5,_b6,_b7,_b8,_b9){if(!_b6){return;}var _ba=_b8?abego.toSet(_b8):{};var n=_b6.length;var c=0;for(var i=0;i<n;i++){var tag=_b6[i];if(_ba[tag]){continue;}if(c>0){_b2(_b5);}if(_b9&&c>=_b9){abego.createEllipsis(_b5);break;}c++;var _bf="";var _c0=_b5;if(_b7<10){_c0=createTiddlyElement(_b5,"span",null,"numberedSuggestion");_b7++;var key=_b7<10?""+(_b7):"0";createTiddlyElement(_c0,"span",null,"suggestionNumber",key+") ");var _c2=_b7==1?"Return or ":"";_bf=" (Shortcut: %1Alt-%0)".format([key,_c2]);}var _c3=config.views.wikified.tag.tooltip.format([tag]);var _c4=(_78(tag)?"Remove tag '%0'%1":"Add tag '%0'%1").format([tag,_bf]);var _c5="%0; Shift-Click: %1".format([_c4,_c3]);var btn=createTiddlyButton(_c0,tag,_c5,_ae,_78(tag)?"currentTag":null);btn.setAttribute("tag",tag);}};var _c7=function(){if(_71){window.scrollTo(0,ensureVisible(_71));}if(_77()){window.scrollTo(0,ensureVisible(_77()));}};var _c8=function(e){if(!e){var e=window.event;}if(!_71){return;}var _cb=resolveTarget(e);if(_cb==_77()){return;}if(abego.isDescendantOrSelf(_71,_cb)){return;}abego.IntelliTagger.close();};addEvent(document,"click",_c8);var _cc=Story.prototype.gatherSaveFields;Story.prototype.gatherSaveFields=function(e,_ce){_cc.apply(this,arguments);var _cf=_ce.tags;if(_cf){_ce.tags=_cf.trim();}};var _d0=function(_d1){story.focusTiddler(_d1,"tags");var _d2=abego.getTiddlerField(story,_d1,"tags");if(_d2){var len=_d2.value.length;abego.setRange(_d2,len,len);window.scrollTo(0,ensureVisible(_d2));}};var _d4=config.macros.edit.handler;config.macros.edit.handler=function(_d5,_d6,_d7,_d8,_d9,_da){_d4.apply(this,arguments);var _db=_d7[0];if((_da instanceof Tiddler)&&_db=="tags"){var _dc=_d5.lastChild;_dc.onfocus=function(e){abego.IntelliTagger.assistTagging(_dc,_da);setTimeout(function(){_d0(_da.title);},100);};_dc.onkeyup=function(e){if(!e){var e=window.event;}if(e.altKey&&!e.ctrlKey&&!e.metaKey&&(e.keyCode>=48&&e.keyCode<=57)){_8b(e.keyCode==48?9:e.keyCode-49,_dc,_da);}else{if(e.ctrlKey&&e.keyCode==32){_8b(0,_dc,_da);}}if(!e.ctrlKey&&(e.keyCode==13||e.keyCode==10)){_8b(0,_dc,_da);}setTimeout(function(){abego.IntelliTagger.assistTagging(_dc,_da);},100);return false;};_81(_dc);}};var _e0=function(e){if(!e){var e=window.event;}var _e3=resolveTarget(e);var _e4=_e3.getAttribute("tiddler");if(_e4){story.displayTiddler(_e3,_e4,"IntelliTaggerEditTagsTemplate",false);_d0(_e4);}return false;};var _e5=config.macros.tags.handler;config.macros.tags.handler=function(_e6,_e7,_e8,_e9,_ea,_eb){_e5.apply(this,arguments);abego.IntelliTagger.createEditTagsButton(_eb,createTiddlyElement(_e6.lastChild,"li"));};var _ec=function(){if(_71&&_72&&!abego.isDescendantOrSelf(document,_72)){abego.IntelliTagger.close();}};setInterval(_ec,100);abego.IntelliTagger.displayTagSuggestions=function(_ed,_ee,_ef,_f0,_f1){_74=_ed;_75=abego.toSet(_ee);_76=_ef;_72=_f0;_73=_f1;if(!_71){_71=createTiddlyElement(document.body,"div",null,"intelliTaggerSuggestions");_71.style.position="absolute";}_ac();abego.openAsPopup(_71);if(_77()){var w=_77().offsetWidth;if(_71.offsetWidth<w){_71.style.width=(w-2*(_6e+_6f))+"px";}abego.moveBelowAndClip(_71,_77());}else{abego.centerOnWindow(_71);}_c7();};abego.IntelliTagger.assistTagging=function(_f3,_f4){var _f5=_90(_f3);var s=_f3.value;if(_7d(_f3)){s=_7a(s);}var _f7=s.readBracketedList();var _f8=_f7.length>0?abego.filterStrings(abego.internal.getTagManager().getPartnerRankedTags(_f7),_f5,_70):_a0(_f5,_70);abego.IntelliTagger.displayTagSuggestions(_a9(_f5,_f7),_f7,_f8,_f3,function(tag,e){if(e.shiftKey){onClickTag.call(this,e);}else{_85(tag,_f3,_f4);}});};abego.IntelliTagger.close=function(){abego.closePopup(_71);_71=null;return false;};abego.IntelliTagger.createEditTagsButton=function(_fb,_fc,_fd,_fe,_ff,id,_101){if(!_fd){_fd="[edit]";}if(!_fe){_fe="Edit the tags";}if(!_ff){_ff="editTags";}var _102=createTiddlyButton(_fc,_fd,_fe,_e0,_ff,id,_101);_102.setAttribute("tiddler",(_fb instanceof Tiddler)?_fb.title:String(_fb));return _102;};abego.IntelliTagger.getSuggestionTagsMaxCount=function(){return 100;};config.macros.intelliTagger={label:"intelliTagger",handler:function(_103,_104,_105,_106,_107,_108){var _109=_107.parseParams("list",null,true);var _10a=_109[0]["action"];for(var i=0;_10a&&i<_10a.length;i++){var _10c=_10a[i];var _10d=config.macros.intelliTagger.subhandlers[_10c];if(!_10d){abego.alertAndThrow("Unsupported action '%0'".format([_10c]));}_10d(_103,_104,_105,_106,_107,_108);}},subhandlers:{showTags:function(_10e,_10f,_110,_111,_112,_113){_b4(_10e,_74,_76?_76.length:0,_76,abego.IntelliTagger.getSuggestionTagsMaxCount());},showFavorites:function(_114,_115,_116,_117,_118,_119){_b4(_114,_76,0);},closeButton:function(_11a,_11b,_11c,_11d,_11e,_11f){var _120=createTiddlyButton(_11a,"close","Close the suggestions",abego.IntelliTagger.close);},version:function(_121){var t="IntelliTagger %0.%1.%2".format([version.extensions.IntelliTaggerPlugin.major,version.extensions.IntelliTaggerPlugin.minor,version.extensions.IntelliTaggerPlugin.revision]);var e=createTiddlyElement(_121,"a");e.setAttribute("href","http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin");e.innerHTML="<font color=\"black\" face=\"Arial, Helvetica, sans-serif\">"+t+"<font>";},copyright:function(_124){var e=createTiddlyElement(_124,"a");e.setAttribute("href","http://tiddlywiki.abego-software.de");e.innerHTML="<font color=\"black\" face=\"Arial, Helvetica, sans-serif\">© 2006-2007 <b><font color=\"red\">abego</font></b> Software<font>";}}};})();config.shadowTiddlers["IntelliTaggerStyleSheet"]="/***\n"+"!~IntelliTagger Stylesheet\n"+"***/\n"+"/*{{{*/\n"+".intelliTaggerSuggestions {\n"+"\tposition: absolute;\n"+"\twidth: 600px;\n"+"\n"+"\tpadding: 2px;\n"+"\tlist-style: none;\n"+"\tmargin: 0;\n"+"\n"+"\tbackground: #eeeeee;\n"+"\tborder: 1px solid DarkGray;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .currentTag {\n"+"\tfont-weight: bold;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .suggestionNumber {\n"+"\tcolor: #808080;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .numberedSuggestion{\n"+"\twhite-space: nowrap;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .intelliTaggerFooter {\n"+"\tmargin-top: 4px;\n"+"\tborder-top-width: thin;\n"+"\tborder-top-style: solid;\n"+"\tborder-top-color: #999999;\n"+"}\n"+".intelliTaggerSuggestions .favorites {\n"+"\tborder-bottom-width: thin;\n"+"\tborder-bottom-style: solid;\n"+"\tborder-bottom-color: #999999;\n"+"\tpadding-bottom: 2px;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .normalTags {\n"+"\tpadding-top: 2px;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .intelliTaggerFooter .button {\n"+"\tfont-size: 10px;\n"+"\n"+"\tpadding-left: 0.3em;\n"+"\tpadding-right: 0.3em;\n"+"}\n"+"\n"+"/*}}}*/\n";config.shadowTiddlers["IntelliTaggerMainTemplate"]="<!--\n"+"{{{\n"+"-->\n"+"<div class=\"favorites\" macro=\"intelliTagger action: showFavorites\"></div>\n"+"<div class=\"normalTags\" macro=\"intelliTagger action: showTags\"></div>\n"+"<!-- The Footer (with the Navigation) ============================================ -->\n"+"<table class=\"intelliTaggerFooter\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tbody>\n"+" <tr>\n"+"\t<td align=\"left\">\n"+"\t\t<span macro=\"intelliTagger action: closeButton\"></span>\n"+"\t</td>\n"+"\t<td align=\"right\">\n"+"\t\t<span macro=\"intelliTagger action: version\"></span>, <span macro=\"intelliTagger action: copyright \"></span>\n"+"\t</td>\n"+" </tr>\n"+"</tbody></table>\n"+"<!--\n"+"}}}\n"+"-->\n";config.shadowTiddlers["IntelliTaggerEditTagsTemplate"]="<!--\n"+"{{{\n"+"-->\n"+"<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler'></div>\n"+"<div class='title' macro='view title'></div>\n"+"<div class='tagged' macro='tags'></div>\n"+"<div class='viewer' macro='view text wikified'></div>\n"+"<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler'></div>\n"+"<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>\n"+"<!--\n"+"}}}\n"+"-->\n";config.shadowTiddlers["BSD open source license (abego Software)"]="See [[Licence|http://tiddlywiki.abego-software.de/#%5B%5BBSD%20open%20source%20license%5D%5D]].";config.shadowTiddlers["IntelliTaggerPlugin Documentation"]="[[Documentation on abego Software website|http://tiddlywiki.abego-software.de/doc/IntelliTagger.pdf]].";config.shadowTiddlers["IntelliTaggerPlugin SourceCode"]="[[Plugin source code on abego Software website|http://tiddlywiki.abego-software.de/archive/IntelliTaggerPlugin/Plugin-IntelliTagger-src.1.0.2.js]]\n";(function(){var _126=restart;restart=function(){setStylesheet(store.getTiddlerText("IntelliTaggerStyleSheet"),"IntelliTaggerStyleSheet");_126.apply(this,arguments);};})();}
// %/
/***
|''Name:''|IntelliTagsEditCommandPlugin|
|''Version:''|1.0.0 (2007-10-03)|
|''Type:''|plugin|
|''Description:''|A command for your tiddler's toolbar to directly edit the tiddler's tags using the IntelliTaggerPlugin, without switching to "edit mode".|
|''Source:''|http://tiddlywiki.abego-software.de/#IntelliTagsEditCommandPlugin|
|''Requires:''|IntelliTaggerPlugin http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)]]|
|''~CoreVersion:''|2.0.8|
|''Browser:''|Firefox 1.5.0.2 or better|
***/
/***
!Using the "IntelliTagsEditCommandPlugin"
Add the command {{{intelliTagsEdit}}} into the 'macro' attribute of the 'toolbar' {{{<div...>}}} in your ViewTemplate.
''Example:''
{{{
<div class='toolbar'
macro='toolbar -closeTiddler closeOthers +editTiddler intelliTagsEdit permalink references jump'>
</div>
}}}
This adds a "tags" button to the toolbar of the tiddlers (next to the ''edit'' button). Pressing the "tags" button will open the input field for the tiddler's tags and let you edit the tags with all the [[IntelliTaggerPlugin|http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin]] features.
***/
/***
!Source Code
***/
//{{{
(function(){
if (!version.extensions.IntelliTaggerPlugin)
throw Error("IntelliTagsEditCommandPlugin requires the IntelliTaggerPlugin (http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin)");
if (config.commands.intelliTagsEdit)
return;
config.commands.intelliTagsEdit = {
text: "tags",
tooltip: "edit the tags"
};
config.commands.intelliTagsEdit.handler = function(event,src,title) {
var button = abego.IntelliTagger.createEditTagsButton(title, null, "tags", "edit the tags");
button.onclick(event);
return false;
};
})();
//}}}
<<tagsTree network "" 1 4 index label>>
<<tagsTree ICND "" 1 4 index label>>
<<tagsTree wireless "" 1 4 index label>>
<<tagsTree security "" 1 4 index label>>
<<tagsTree isa "" 1 4 index label>>
<<tagsTree supplement "" 1 4 index label>>
<<tagsTree twcms "" 1 4 index label>>
<<tagsTree KMKConfig "" 1 4 index label>>
<<tabs txtMainTab "最近更新" "依更新日期排序" SideBarTabs/Timeline "分類" "所有標籤" TabTags "設定文章" "說所有設定文章" TabMoreShadowed>>
<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
<!--}}}-->
<!--{{{-->
<style type="text/css">
#contentWrapper {display:none;}
</style>
<div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;">
<b>Sky of flatfish</b><br><br><img src="loader.gif"><br>
<span style="font-size: 14px; color:red;">Requires Javascript.</span>
</div>
<!--}}}-->
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryDark]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='toolBar' refresh='content' force='true' tiddler='ToolBar'></div>
<div id='mainMenu' refresh='content' force='true' tiddler='MainMenu'></div>
<!--
<div id='sidebar'>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
-->
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlersBar' refresh='none' ondblclick='config.macros.tiddlersBar.onTiddlersBarAction(event)'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
//{{{
//============================================================================
// PartTiddlerPlugin
// Ensure that the PartTiddler Plugin is only installed once.
//
if (!version.extensions.PartTiddlerPlugin) {
version.extensions.PartTiddlerPlugin = {
major: 1, minor: 0, revision: 9,
date: new Date(2007, 6, 14),
type: 'plugin',
source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
};
if (!window.abego) window.abego = {};
if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");
//============================================================================
// Common Helpers
// Looks for the next newline, starting at the index-th char of text.
//
// If there are only whitespaces between index and the newline
// the index behind the newline is returned,
// otherwise (or when no newline is found) index is returned.
//
var skipEmptyEndOfLine = function(text, index) {
var re = /(\n|[^\s])/g;
re.lastIndex = index;
var result = re.exec(text);
return (result && text.charAt(result.index) == '\n')
? result.index+1
: index;
}
//============================================================================
// Constants
var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
var partEndTagREString = "<\\/part>";
var partEndTagString = "</part>";
//============================================================================
// Plugin Specific Helpers
// Parse the parameters inside a <part ...> tag and return the result.
//
// @return [may be null] {partName: ..., isHidden: ...}
//
var parseStartTagParams = function(paramText) {
var params = paramText.readMacroParams();
if (params.length == 0 || params[0].length == 0) return null;
var name = params[0];
var paramsIndex = 1;
var hidden = false;
if (paramsIndex < params.length) {
hidden = params[paramsIndex] == "hidden";
paramsIndex++;
}
return {
partName: name,
isHidden: hidden
};
}
// Returns the match to the next (end or start) part tag in the text,
// starting the search at startIndex.
//
// When no such tag is found null is returned, otherwise a "Match" is returned:
// [0]: full match
// [1]: matched "end" tag (or null when no end tag match)
// [2]: matched "start" tag (or null when no start tag match)
// [3]: content of start tag (or null if no start tag match)
//
var findNextPartEndOrStartTagMatch = function(text, startIndex) {
var re = new RegExp(partEndOrStartTagRE);
re.lastIndex = startIndex;
var match = re.exec(text);
return match;
}
//============================================================================
// Formatter
// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
//
// @return true if a complete part section (including the end tag) could be processed, false otherwise.
//
var handlePartSection = function(w) {
var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
if (!tagMatch) return false;
if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
// Parse the start tag parameters
var arguments = parseStartTagParams(tagMatch[3]);
if (!arguments) return false;
// Continue processing
var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
if (endMatch && endMatch[1]) {
if (!arguments.isHidden) {
w.nextMatch = startTagEndIndex;
w.subWikify(w.output,partEndTagREString);
}
w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
return true;
}
return false;
}
config.formatters.push( {
name: "part",
match: "<part\\s+[^>]+>",
handler: function(w) {
if (!handlePartSection(w)) {
w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
}
}
} )
//============================================================================
// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers
// as tiddlers.
var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)
// Return the match to the first <part ...> tag of the text that has the
// requrest partName.
//
// @return [may be null]
//
var findPartStartTagByName = function(text, partName) {
var i = 0;
while (true) {
var tagMatch = findNextPartEndOrStartTagMatch(text, i);
if (!tagMatch) return null;
if (tagMatch[2]) {
// Is start tag
// Check the name
var arguments = parseStartTagParams(tagMatch[3]);
if (arguments && arguments.partName == partName) {
return tagMatch;
}
}
i = tagMatch.index+tagMatch[0].length;
}
}
// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler
// object, using fullName as the Tiddler's title.
//
// All remaining properties of the new Tiddler (tags etc.) are inherited from
// the parentTiddler.
//
// @return [may be null]
//
var getPart = function(parentTiddler, partName, fullName) {
var text = parentTiddler.text;
var startTag = findPartStartTagByName(text, partName);
if (!startTag) return null;
var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
if (indexOfEndTag >= 0) {
var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
var partTiddler = new Tiddler();
partTiddler.set(
fullName,
partTiddlerText,
parentTiddler.modifier,
parentTiddler.modified,
parentTiddler.tags,
parentTiddler.created);
partTiddler.abegoIsPartTiddler = true;
return partTiddler;
}
return null;
}
// Hijack the store.fetchTiddler to recognize the "part" addresses.
//
var hijackFetchTiddler = function() {
var oldFetchTiddler = store.fetchTiddler ;
store.fetchTiddler = function(title) {
var result = oldFetchTiddler.apply(this, arguments);
if (!result && title) {
var i = title.lastIndexOf('/');
if (i > 0) {
var parentName = title.substring(0, i);
var partName = title.substring(i+1);
var parent = (parentName == ".")
? store.resolveTiddler(currentParent)
: oldFetchTiddler.apply(this, [parentName]);
if (parent) {
return getPart(parent, partName, parent.title+"/"+partName);
}
}
}
return result;
};
};
// for debugging the plugin is not loaded through the systemConfig mechanism but via a script tag.
// At that point in the "store" is not yet defined. In that case hijackFetchTiddler through the restart function.
// Otherwise hijack now.
if (!store) {
var oldRestartFunc = restart;
window.restart = function() {
hijackFetchTiddler();
oldRestartFunc.apply(this,arguments);
};
} else
hijackFetchTiddler();
// The user must not edit a readOnly/partTiddler
//
config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
Tiddler.prototype.isReadOnly = function() {
// Tiddler.isReadOnly was introduced with TW 2.0.6.
// For older version we explicitly check the global readOnly flag
if (config.commands.editTiddler.oldIsReadOnlyFunction) {
if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
} else {
if (readOnly) return true;
}
return this.abegoIsPartTiddler;
}
config.commands.editTiddler.handler = function(event,src,title)
{
var t = store.getTiddler(title);
// Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
// or the tiddler is not readOnly
if(!t || !t.abegoIsPartTiddler)
{
clearMessage();
story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
story.focusTiddler(title,"text");
return false;
}
}
// To allow the "./partName" syntax in macros we need to hijack
// the invokeMacro to define the "currentParent" while it is running.
//
var oldInvokeMacro = window.invokeMacro;
function myInvokeMacro(place,macro,params,wikifier,tiddler) {
var oldCurrentParent = currentParent;
if (tiddler) currentParent = tiddler;
try {
oldInvokeMacro.apply(this, arguments);
} finally {
currentParent = oldCurrentParent;
}
}
window.invokeMacro = myInvokeMacro;
// To correctly support the "./partName" syntax while refreshing we need to hijack
// the config.refreshers.tiddlers to define the "currentParent" while it is running.
//
(function() {
var oldTiddlerRefresher= config.refreshers.tiddler;
config.refreshers.tiddler = function(e,changeList) {
var oldCurrentParent = currentParent;
try {
currentParent = e.getAttribute("tiddler");
return oldTiddlerRefresher.apply(this,arguments);
} finally {
currentParent = oldCurrentParent;
}
};
})();
// Support "./partName" syntax inside <<tabs ...>> macro
(function() {
var extendRelativeNames = function(e, title) {
var nodes = e.getElementsByTagName("a");
for(var i=0; i<nodes.length; i++) {
var node = nodes[i];
var s = node.getAttribute("content");
if (s && s.indexOf("./") == 0)
node.setAttribute("content",title+s.substr(1));
}
};
var oldHandler = config.macros.tabs.handler;
config.macros.tabs.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var result = oldHandler.apply(this,arguments);
if (tiddler)
extendRelativeNames(place, tiddler.title);
return result;
};
})();
// Scroll the anchor anchorName in the viewer of the given tiddler visible.
// When no tiddler is defined use the tiddler of the target given event is used.
window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
var tiddlerElem = null;
if (tiddler) {
tiddlerElem = document.getElementById(story.idPrefix + tiddler);
}
if (!tiddlerElem && evt) {
var target = resolveTarget(evt);
tiddlerElem = story.findContainingTiddler(target);
}
if (!tiddlerElem) return;
var children = tiddlerElem.getElementsByTagName("a");
for (var i = 0; i < children.length; i++) {
var child = children[i];
var name = child.getAttribute("name");
if (name == anchorName) {
var y = findPosY(child);
window.scrollTo(0,y);
return;
}
}
}
} // of "install only once"
//}}}
<<tabs txtMainTab "最近更新" "依更新日期排序" TabTimeline "分類" "所有標籤" TabTags "設定文章" "說所有設定文章" TabMoreShadowed>>
<<showUpdates excludeTag:excludeLists write:'(index < 10) ? ""+""+ tiddler.modified.formatString("YYYY年0MM月0DD日")+ "\n [[" +tiddler.title+"]]"+"\n" : ""'>>
''flatfish''@2008 powered by TWtBala - TiddlyWiki 2.3.0
/***
|''Name:''|SparklinePlugin|
|''Description:''|Sparklines macro|
***/
//{{{
if(!version.extensions.SparklinePlugin) {
version.extensions.SparklinePlugin = {installed:true};
//--
//-- Sparklines
//--
config.macros.sparkline = {};
config.macros.sparkline.handler = function(place,macroName,params)
{
var data = [];
var min = 0;
var max = 0;
var v;
for(var t=0; t<params.length; t++) {
v = parseInt(params[t]);
if(v < min)
min = v;
if(v > max)
max = v;
data.push(v);
}
if(data.length < 1)
return;
var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
box.title = data.join(",");
var w = box.offsetWidth;
var h = box.offsetHeight;
box.style.paddingRight = (data.length * 2 - w) + "px";
box.style.position = "relative";
for(var d=0; d<data.length; d++) {
var tick = document.createElement("img");
tick.border = 0;
tick.className = "sparktick";
tick.style.position = "absolute";
tick.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";
tick.style.left = d*2 + "px";
tick.style.width = "2px";
v = Math.floor(((data[d] - min)/(max-min)) * h);
tick.style.top = (h-v) + "px";
tick.style.height = v + "px";
box.appendChild(tick);
}
};
}
//}}}
/***
''Inspired by [[TiddlyPom|http://www.warwick.ac.uk/~tuspam/tiddlypom.html]]''
|Name|SplashScreenPlugin|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#SplashScreenPlugin|
|Version|0.21 |
|Requires|~TW2.08+|
!Description:
Provides a simple splash screen that is visible while the TW is loading.
!Installation
Copy the source text of this tiddler to your TW in a new tiddler, tag it with systemConfig and save and reload. The SplashScreen will now be installed and will be visible the next time you reload your TW.
!Customizing
Once the SplashScreen has been installed and you have reloaded your TW, the splash screen html will be present in the MarkupPreHead tiddler. You can edit it and customize to your needs.
!History
* 20-07-06 : version 0.21, modified to hide contentWrapper while SplashScreen is displayed.
* 26-06-06 : version 0.2, first release
!Code
***/
//{{{
var old_lewcid_splash_restart=restart;
restart = function()
{ if (document.getElementById("SplashScreen"))
document.getElementById("SplashScreen").style.display = "none";
if (document.getElementById("contentWrapper"))
document.getElementById("contentWrapper").style.display = "block";
old_lewcid_splash_restart();
if (splashScreenInstall)
{if(config.options.chkAutoSave)
{saveChanges();}
displayMessage("TW SplashScreen has been installed, please save and refresh your TW.");
}
}
var oldText = store.getTiddlerText("MarkupPreHead");
if (oldText.indexOf("SplashScreen")==-1)
{var siteTitle = store.getTiddlerText("SiteTitle");
var splasher='\n\n<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>'+siteTitle +'</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>';
if (! store.tiddlerExists("MarkupPreHead"))
{var myTiddler = store.createTiddler("MarkupPreHead");}
else
{var myTiddler = store.getTiddler("MarkupPreHead");}
myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
store.setDirty(true);
var splashScreenInstall = true;
}
//}}}
/*{{{*/
/* 主要項目樣式
在 Tiddler 中的使用格式如下 :
{{item1{Item description}}}
*/
.item1{
line-height:28px;
font-weight:bold;
font-size:14px;
border-left: 10px solid blue;
margin:0px 0px 0px 0px;
padding: 0px 0px 0px 3px;
background-color:transparent;
}
/* 實作項目樣式
在 Tiddler 中的使用格式如下 :
{{op1{operation description}}}
*/
.op1{
line-height:28px;
font-weight:bold;
font-size:14px;
margin:0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
background-color:transparent;
}
/*
Making preformated <pre> text wrap in CSS3, Mozilla, Opera and IE
word-wrap: break-word; - 視窗邊界換行,僅 IE 支援。也可以用 word-brak: break-all; 但不會保持英文單字的完整性。
white-space: pre; - 對某標籤作預先格式化,所有標準瀏覽器皆支援。
white-space: -moz-pre-wrap; - 預先格式化,但在元素邊界換行,僅 Mozilla (Firefox) 支援。
white-space: pre-wrap; - 預先格式化,但在元素邊界換行,僅 Opera 支援。
*/
.viewer pre {
font-size:12px;
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
/*
無框線表格樣式設定, 以下是使用範例:
|!Col1|!Col2|!Col3|
|r1c1| r1r2c2 |r1c3|
|r2c1|~|r2c3|
|borderless|k // 使用格式
*/
.borderless, .borderless table, .borderless td, .borderless tr, .borderless th, .borderless tbody {
border:0 !important; margin:0 !important; padding:2px !important;
td.vertical-align:top !important;margin-left: auto !important;
margin-right: auto !important;
}
.externalLink {text-decoration:none}
#toolBar {
background-color: #eee;
color:#ccc;
width: auto;
text-align:left;
padding: 0.4em 0.6em 0.4em 0.6em;
}
h1,h2,h3 {padding-bottom:1px; margin-top:0.1em;margin-bottom:0.1em;}
h4,h5,h6 {margin-top:0.1em;}
h1 {font-size:1.2em;}
h2 {font-size:1.0em;}
h3 {font-size:0.8em;}
h4 {font-size:0.6em;}
h5 {font-size:.0.4em;}
.headerShadow {position:relative; padding:2.0em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:2.0em 0em 1em 1em; left:0px; top:0px;}
#mainMenu {
position:absolute; left:0; width:15.5em; text-align:left; line-height:1.2em;
padding:1.0em 0.5em 0.2em 0.5em; font-size:1em;
}
#displayArea {margin:1em 1em 0em 17em;}
/*
toBalaFile 巨集的 Style 設定
*/
.toBalaFile {background:#fff; border:0px solid #ccc; padding:1px 7px 1px 3px; margin:2px;}
.toBalaFileEdit {border:1px solid #841;padding:2px;margin:2px 5px 2px 5px;}
.toBalaFileCancel {border:1px solid #841;padding:2px;margin:2px 5px 2px 5px;}
.toBalaFileToolBar {background:#fff;border-bottom: 3px solid #841;padding:3px 0px 5px 0px;margin:5px 0px 0px 3px;}
.toBalaFileSave {border:1px solid #841;padding:2px;margin:2px 5px 2px 5px;}
/*}}}*/
/*{{{*/
/***** LAYOUT STYLES - DO NOT EDIT! *****/
ul.suckerfish, ul.suckerfish ul {
margin: 0;
padding: 0;
list-style: none;
line-height:1.4em;
}
ul.suckerfish li {
display: inline-block;
display: block;
float: left;
}
ul.suckerfish li ul {
position: absolute;
left: -999em;
}
ul.suckerfish li:hover ul, ul.suckerfish li.sfhover ul {
left: auto;
}
ul.suckerfish ul li {
float: none;
border-right: 0;
border-left:0;
}
ul.suckerfish a, ul.suckerfish a:hover {
display: block;
}
ul.suckerfish li a.tiddlyLink, ul.suckerfish li a, #mainMenu ul.suckerfish li a {font-weight:bold;}
/**** END LAYOUT STYLES *****/
/**** COLORS AND APPEARANCE - DEFAULT *****/
ul.suckerfish li a {
padding: 0.5em 1.5em;
color: #FFF;
background: #0066aa;
border-bottom: 0;
font-weight:bold;
}
ul.suckerfish li:hover a, ul.suckerfish li.sfhover a{
background: #00558F;
}
ul.suckerfish li:hover ul a, ul.suckerfish li.sfhover ul a{
color: #000;
background: #eff3fa;
border-top:1px solid #FFF;
}
ul.suckerfish ul li a:hover {
background: #e0e8f5;
}
ul.suckerfish li a{
width:9em;
}
ul.suckerfish ul li a, ul.suckerfish ul li a:hover{
display:inline-block;
width:9em;
}
ul.suckerfish li {
border-left: 1px solid #00558F;
}
/***** END COLORS AND APPEARANCE - DEFAULT *****/
/***** LAYOUT AND APPEARANCE: VERTICAL *****/
ul.suckerfish.vertical li{
width:10em;
border-left: 0px solid #00558f;
}
ul.suckerfish.vertical ul li, ul.suckerfish.vertical li a, ul.suckerfish.vertical li:hover a, ul.suckerfish.vertical li.sfhover a {
border-left: 0.8em solid #00558f;
}
ul.suckerfish.vertical li a, ul.suckerfish.vertical li:hover a, ul.suckerfish.vertical li.sfhover a, ul.suckerfish.vertical li.sfhover a:hover{
width:8em;
}
ul.suckerfish.vertical {
width:10em; text-align:left;
float:left;
}
ul.suckerfish.vertical li a {
padding: 0.5em 1em 0.5em 1em;
border-top:1px solid #fff;
}
ul.suckerfish.vertical, ul.suckerfish.vertical ul {
line-height:1.4em;
}
ul.suckerfish.vertical li:hover ul, ul.suckerfish.vertical li.sfhover ul {
margin: -2.4em 0 0 10.9em;
}
ul.suckerfish.vertical li:hover ul li a, ul.suckerfish.vertical li.sfhover ul li a {
border: 0px solid #FFF;
}
ul.suckerfish.vertical li:hover a, ul.suckerfish.vertical li.sfhover a{
padding-right:1.1em;
}
ul.suckerfish.vertical li:hover ul li, ul.suckerfish.vertical li.sfhover ul li {
border-bottom:1px solid #fff;
}
/***** END LAYOUT AND APPEARANCE: VERTICAL *****/
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}
body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}
hr {height:1px;}
a {text-decoration:none;}
dt {font-weight:bold;}
ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}
.txtOptionInput {width:11em;}
#contentWrapper .chkOptionInput {border:0;}
.externalLink {text-decoration:underline;}
.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}
.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}
/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
#mainMenu .tiddlyLinkExisting,
#mainMenu .tiddlyLinkNonExisting,
#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}
#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}
.wizard {padding:0.1em 1em 0em 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
#messageArea a {text-decoration:underline;}
.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}
.tabset {padding:1em 0em 0em 0.5em;}
.tab {margin:0em 0em 0em 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}
#contentWrapper {display:block;}
#splashScreen {display:none;}
#displayArea {margin:1em 17em 0em 14em;}
.toolbar {text-align:right; font-size:.9em;}
.tiddler {padding:1em 1em 0em 1em;}
.missing .viewer,.missing .title {font-style:italic;}
.title {font-size:1.6em; font-weight:bold;}
.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}
.tiddler .button {padding:0.2em 0.4em;}
.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}
.footer {font-size:.9em;}
.footer li {display:inline;}
.annotation {padding:0.5em; margin:0.5em;}
* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}
.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0em; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}
.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
.sparkline {line-height:1em;}
.sparktick {outline:0;}
.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}
* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
<<tabs txtMoreTab "未完成" "內容空白的文章" TabMoreMissing "未引用" "未被引用的文章" TabMoreOrphans "預設文章" "已預設內容的隱藏文章" TabMoreShadowed>>
/***
|Name|TaggedTemplateTweak|
|Source|http://www.TiddlyTools.com/#TaggedTemplateTweak|
|Version|1.1.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|Story.prototype.chooseTemplateForTiddler()|
|Description|use alternative ViewTemplate/EditTemplate for tiddler's tagged with specific tag values|
The core function, "story.chooseTemplateForTiddler(title,template)" is essentially a "pass-thru" that returns the same template it was given, and is provided by the core so that plugins can customize the template selection logic to select alternative templates, based on whatever programmatic criteria is appropriate. This tweak extends story.chooseTemplateForTiddler() so that ''whenever a tiddler is marked with a specific tag value, it can be viewed and/or edited using alternatives to the standard tiddler templates.''
!!!!!Usage
<<<
Each alternative template is associated with a specific tiddler tag value by using that tag value as a prefix added to the standard TiddlyWiki template titles, [[ViewTemplate]] and [[EditTemplate]].
For example, any tiddlers that are tagged with ''<<tag media>>'' will look for alternative templates named [[mediaViewTemplate]] and [[mediaEditTemplate]]. Additionally, in order to find templates that have proper WikiWord tiddler titles (e.g., [[MediaViewTemplate]] and [[MediaEditTemplate]]), the plugin will also attempt to use a capitalized form of the tag value (e.g., ''Media'') as a prefix. //This capitalization is for comparison purposes only and will not alter the actual tag values that are stored in the tiddler.//
If no matching alternative template can be found by using //any// of the tiddler's tags (either "as-is" or capitalized), the tiddler defaults to using the appropriate standard [[ViewTemplate]] or [[EditTemplate]] definition.
''To add your own custom templates:''
>First, decide upon a suitable tag keyword to uniquely identify your custom templates and create custom view and/or edit templates using that keyword as a prefix (e.g., "KeywordViewTemplate" and "KeywordEditTemplate"). Then, simply create a tiddler and tag it with your chosen keyword... that's it! As long as the tiddler is tagged with your keyword, it will be displayed using the corresponding alternative templates. If you remove the tag or rename/delete the alternative templates, the tiddler will revert to using the standard viewing and editing templates.
<<<
!!!!!Examples
<<<
|Sample tiddler| tag | view template | edit template |
|[[MediaSample - QuickTime]]| <<tag media>> | [[MediaViewTemplate]] | [[MediaEditTemplate]] |
|[[MediaSample - Windows]]| <<tag media>> | [[MediaViewTemplate]] | [[MediaEditTemplate]] |
|[[CDSample]]| <<tag CD>> | [[CDViewTemplate]] | [[CDEditTemplate]] |
|<<newTiddler label:"create new task..." title:SampleTask tag:task text:"Type some text and then press DONE to view the task controls">> | <<tag task>> | [[TaskViewTemplate]] | [[EditTemplate]] |
//(note: if these samples are not present in your document, please visit// http://www.TiddlyTools.com/ //to view these sample tiddlers on-line)//
<<<
!!!!!Revisions
<<<
2007.06.23 [1.1.0] re-written to use automatic 'tag prefix' search instead of hard coded check for each tag. Allows new custom tags to be used without requiring code changes to this plugin.
2007.06.11 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.taggedTemplate= {major: 1, minor: 1, revision: 0, date: new Date(2007,6,18)};
Story.prototype.taggedTemplate_chooseTemplateForTiddler = Story.prototype.chooseTemplateForTiddler
Story.prototype.chooseTemplateForTiddler = function(title,template)
{
// get default template from core
var template=this.taggedTemplate_chooseTemplateForTiddler.apply(this,arguments);
// if the tiddler to be rendered doesn't exist yet, just return core result
var tiddler=store.getTiddler(title); if (!tiddler) return template;
// look for template whose prefix matches a tag on this tiddler
for (t=0; t<tiddler.tags.length; t++) {
var tag=tiddler.tags[t];
if (store.tiddlerExists(tag+template)) { template=tag+template; break; }
// try capitalized tag (to match WikiWord template titles)
var cap=tag.substr(0,1).toUpperCase()+tag.substr(1);
if (store.tiddlerExists(cap+template)) { template=cap+template; break; }
}
return template;
}
//}}}
/***
|''Name:''|~TaggerPlugin|
|''Version:''|1.0.1 (2006-06-01)|
|''Source:''|http://tw.lewcid.org//#TaggerPlugin|
|''Author:''|SaqImtiaz|
|''Description:''|Provides a drop down listing current tiddler tags, and allowing toggling of tags.|
|''Documentation:''|[[TaggerPluginDocumentation]]|
|''Source Code:''|[[TaggerPluginSource]]|
|''~TiddlyWiki:''|Version 2.0.8 or better|
***/
// /%
config.tagger={defaults:{label:"Tags: ",tooltip:"Manage tiddler tags",taglist:"true",excludeTags:"",notags:"tiddler has no tags",aretags:"current tiddler tags:",toggletext:"add tags:"}};config.macros.tagger={};config.macros.tagger.arrow=(document.all?"▼":"▾");config.macros.tagger.handler=function(_1,_2,_3,_4,_5,_6){var _7=config.tagger.defaults;var _8=_5.parseParams("tagman",null,true);var _9=((_8[0].label)&&(_8[0].label[0])!=".")?_8[0].label[0]+this.arrow:_7.label+this.arrow;var _a=((_8[0].tooltip)&&(_8[0].tooltip[0])!=".")?_8[0].tooltip[0]:_7.tooltip;var _b=((_8[0].taglist)&&(_8[0].taglist[0])!=".")?_8[0].taglist[0]:_7.taglist;var _c=((_8[0].exclude)&&(_8[0].exclude[0])!=".")?(_8[0].exclude[0]).readBracketedList():_7.excludeTags.readBracketedList();if((_8[0].source)&&(_8[0].source[0])!="."){var _d=_8[0].source[0];}if(_d&&!store.getTiddler(_d)){return false;}var _e=function(e){if(!e){var e=window.event;}var _11=Popup.create(this);var _12=store.getTags();var _13=new Array();for(var i=0;i<_12.length;i++){_13.push(_12[i][0]);}if(_d){var _15=store.getTiddler(_d);_13=_15.tags.sort();}var _16=_6.tags.sort();var _17=function(_18,_19,_1a){var sp=createTiddlyElement(createTiddlyElement(_11,"li"),"span",null,"tagger");var _1c=createTiddlyButton(sp,_18,_1a+" '"+_19+"'",taggerOnToggle,"button","toggleButton");_1c.setAttribute("tiddler",_6.title);_1c.setAttribute("tag",_19);insertSpacer(sp);if(window.createTagButton_orig_mptw){createTagButton_orig_mptw(sp,_19)}else{createTagButton(sp,_19);}};createTiddlyElement(_11,"li",null,"listTitle",(_6.tags.length==0?_7.notags:_7.aretags));for(var t=0;t<_16.length;t++){_17("[x]",_16[t],"remove tag ");}createTiddlyElement(createTiddlyElement(_11,"li"),"hr");if(_b!="false"){createTiddlyElement(_11,"li",null,"listTitle",_7.toggletext);for(var i=0;i<_13.length;i++){if(!_6.tags.contains(_13[i])&&!_c.contains(_13[i])){_17("[ ]",_13[i],"add tag ");}}createTiddlyElement(createTiddlyElement(_11,"li"),"hr");}var _1f=createTiddlyButton(createTiddlyElement(_11,"li"),("Create new tag"),null,taggerOnToggle);_1f.setAttribute("tiddler",_6.title);if(_d){_1f.setAttribute("source",_d);}Popup.show(_11,false);e.cancelBubble=true;if(e.stopPropagation){e.stopPropagation();}return (false);};createTiddlyButton(_1,_9,_a,_e,"button","taggerDrpBtn");};window.taggerOnToggle=function(e){var tag=this.getAttribute("tag");var _22=this.getAttribute("tiddler");var _23=store.getTiddler(_22);if(!tag){var _24=prompt("Enter new tag:","");if(_24!=""&&_24!=null){var tag=_24;if(this.getAttribute("source")){var _26=store.getTiddler(this.getAttribute("source"));_26.tags.pushUnique(_24);}}else{return false;}}if(!_23||!_23.tags){store.saveTiddler(_22,_22,"",config.options.txtUserName,new Date(),tag);}else{if(_23.tags.find(tag)==null){_23.tags.push(tag);}else{if(!_24){_23.tags.splice(_23.tags.find(tag),1);}}store.saveTiddler(_23.title,_23.title,_23.text,_23.modifier,_23.modified,_23.tags);}story.refreshTiddler(_22,null,true);if(config.options.chkAutoSave){saveChanges();}return false;};setStylesheet(".tagger a.button {font-weight: bold;display:inline; padding:0px;}\n"+".tagger #toggleButton {padding-left:2px; padding-right:2px; margin-right:1px; font-size:110%;}\n"+"#nestedtagger {background:#2E5ADF; border: 1px solid #0331BF;}\n"+".popup .listTitle {color:#000;}\n"+"","TaggerStyles");window.lewcidTiddlerSwapTag=function(_27,_28,_29){for(var i=0;i<_27.tags.length;i++){if(_27.tags[i]==_28){_27.tags[i]=_29;return true;}}return false;};window.lewcidRenameTag=function(e){var tag=this.getAttribute("tag");var _2d=prompt("Rename tag '"+tag+"' to:",tag);if((_2d==tag)||(_2d==null)){return false;}if(store.tiddlerExists(_2d)){if(confirm(config.messages.overwriteWarning.format([_2d.toString()]))){story.closeTiddler(_2d,false,false);}else{return null;}}tagged=store.getTaggedTiddlers(tag);if(tagged.length!=0){for(var j=0;j<tagged.length;j++){lewcidTiddlerSwapTag(tagged[j],tag,_2d);}}if(store.tiddlerExists(tag)){store.saveTiddler(tag,_2d);}if(document.getElementById("tiddler"+tag)){var _2f=document.getElementById(story.idPrefix+tag);var _30=story.positionTiddler(_2f);var _31=document.getElementById(story.container);story.closeTiddler(tag,false,false);story.createTiddler(_31,_30,_2d,null);story.saveTiddler(_2d);}if(config.options.chkAutoSave){saveChanges();}return false;};window.onClickTag=function(e){if(!e){var e=window.event;}var _34=resolveTarget(e);var _35=(!isNested(_34));if((Popup.stack.length>1)&&(_35==true)){Popup.removeFrom(1);}else{if(Popup.stack.length>0&&_35==false){Popup.removeFrom(0);}}var _36=(_35==false)?"popup":"nestedtagger";var _37=createTiddlyElement(document.body,"ol",_36,"popup",null);Popup.stack.push({root:this,popup:_37});var tag=this.getAttribute("tag");var _39=this.getAttribute("tiddler");if(_37&&tag){var _3a=store.getTaggedTiddlers(tag);var _3b=[];var li,r;for(r=0;r<_3a.length;r++){if(_3a[r].title!=_39){_3b.push(_3a[r].title);}}var _3d=config.views.wikified.tag;if(_3b.length>0){var _3e=createTiddlyButton(createTiddlyElement(_37,"li"),_3d.openAllText.format([tag]),_3d.openAllTooltip,onClickTagOpenAll);_3e.setAttribute("tag",tag);createTiddlyElement(createTiddlyElement(_37,"li"),"hr");for(r=0;r<_3b.length;r++){createTiddlyLink(createTiddlyElement(_37,"li"),_3b[r],true);}}else{createTiddlyText(createTiddlyElement(_37,"li",null,"disabled"),_3d.popupNone.format([tag]));}createTiddlyElement(createTiddlyElement(_37,"li"),"hr");var h=createTiddlyLink(createTiddlyElement(_37,"li"),tag,false);createTiddlyText(h,_3d.openTag.format([tag]));createTiddlyElement(createTiddlyElement(_37,"li"),"hr");var _40=createTiddlyButton(createTiddlyElement(_37,"li"),("Rename tag '"+tag+"'"),null,lewcidRenameTag);_40.setAttribute("tag",tag);}Popup.show(_37,false);e.cancelBubble=true;if(e.stopPropagation){e.stopPropagation();}return (false);};if(!window.isNested){window.isNested=function(e){while(e!=null){var _42=document.getElementById("contentWrapper");if(_42==e){return true;}e=e.parentNode;}return false;};}config.shadowTiddlers.TaggerPluginDocumentation="The documentation is available [[here.|http://tw.lewcid.org/#TaggerPluginDocumentation]]";config.shadowTiddlers.TaggerPluginSource="The uncompressed source code is available [[here.|http://tw.lewcid.org/#TaggerPluginSource]]";
// %/
/***
|''Name:''|TagsTreePlugin|
|''Description:''|Displays tags hierachy as a tree of tagged tiddlers.<br>Can be used to create dynamic outline navigation.|
|''Version:''|1.0.1|
|''Date:''|Jan 04,2008|
|''Source:''|http://visualtw.ouvaton.org/VisualTW.html|
|''Author:''|Pascal Collin|
|''License:''|[[BSD open source license|License]]|
|''~CoreVersion:''|2.1.0|
|''Browser:''|Firefox 2.0; InternetExplorer 6.0|
!Demo
On the plugin [[homepage|http://visualtw.ouvaton.org/VisualTW.html]] :
*Try to tag some <<newTiddler>> with a tag displayed in the menu and edit MainMenu.
*Look at some tags like [[Plugins]] or [[menu]].
!Installation
#import the plugin,
#save and reload,
#optionally, edit TagsTreeStyleSheet.
! Usage
{{{<<tagsTree>>}}} macro accepts the following //optional// parameters.
|!#|!parameter|!description|!by default|
|1|{{{root}}}|Uses {{{root}}} tag as tree root|- In a //tiddler// content or template : uses the tiddler as root tag.<br>- In the //page// content or template (by ex MainMenu) : displays all untagged tags.|
|2|{{{excludeTag}}}|Excludes all such tagged tiddlers from the tree|Uses default excludeLists tag|
|3|{{{level}}}|Expands nodes until level {{{level}}}.<br>Value {{{0}}} hides expand/collapse buttons.|Nodes are collapsed on first level|
|4|{{{depth}}}|Hierachy depth|6 levels depth (H1 to H6 header styles)|
|5|{{{sortField}}}|Alternate sort field. By example : "index".|Sorts tags and tiddlers alphabetically (on their title)|
|6|{{{labelField}}}|Alertnate label field. By example : "label".|Displays tiddler's title|
!Useful addons
*[[FieldsEditorPlugin]] : //create//, //edit//, //view// and //delete// commands in toolbar <<toolbar fields>>.
*[[TaggerPlugin]] : Provides a drop down listing current tiddler tags, and allowing toggling of tags.
!Advanced Users
You can change the global defaults for TagsTreePlugin, like default {{{level}}} value or level styles, by editing or overriding the first config.macros.tagsTree attributes below.
!Code
***/
//{{{
config.macros.tagsTree = {
expand : "+",
collapse : "–",
depth : 6,
level : 1,
sortField : "",
labelField : "",
styles : ["h1","h2","h3","h4","h5","h6"],
trees : {}
}
config.macros.tagsTree.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var root = params[0] ? params[0] : (tiddler ? tiddler.title : null);
var excludeTag = params[1] ? params[1] : "excludeTagsTree";
var level = params[2] ? params[2] : config.macros.tagsTree.level;
var depth = params[3] ? params[3] : config.macros.tagsTree.depth;
var sortField = params[4] ? params[4] : config.macros.tagsTree.sortField;
var labelField = params[5] ? params[5] : config.macros.tagsTree.labelField;
var showButtons = (level>0);
var id = config.macros.tagsTree.getId(place);
if (config.macros.tagsTree.trees[id]==undefined) config.macros.tagsTree.trees[id]={};
config.macros.tagsTree.createSubTree(place,id,root,excludeTag,[],level>0 ? level : 1,depth, sortField, labelField,showButtons);
}
config.macros.tagsTree.createSubTree = function(place, id, root, excludeTag, ancestors, level, depth, sortField, labelField,showButtons){
var childNodes = root ? this.getChildNodes(root, ancestors) : this.getRootTags(excludeTag);
var isOpen = (level>0) || (!showButtons);
if (root && this.trees[id][root]!=undefined) isOpen = this.trees[id][root];
if (root && ancestors.length) {
var t = store.getTiddler(root);
if (childNodes.length && depth>0) {
var wrapper = createTiddlyElement(place , this.styles[Math.min(Math.max(ancestors.length,1),6)-1],null,"branch");
if (showButtons) {
b = createTiddlyButton(wrapper, isOpen ? config.macros.tagsTree.collapse : config.macros.tagsTree.expand, null, config.macros.tagsTree.onClick);
b.setAttribute("treeId",id);
b.setAttribute("tiddler",root);
}
createTiddlyText(createTiddlyLink(wrapper, root),t&&labelField ? t.fields[labelField] ? t.fields[labelField] : root : root);
}
else
createTiddlyText(createTiddlyLink(place, root,false,"leaf"),t&&labelField ? t.fields[labelField] ? t.fields[labelField] : root : root);
}
if (childNodes.length && depth) {
var d = createTiddlyElement(place,"div",null,"subtree");
d.style.display= isOpen ? "block" : "none";
if (sortField)
childNodes.sort(function(a, b){
var fa=a.fields[sortField];
var fb=b.fields[sortField];
return (fa==undefined && fb==undefined) ? a.title < b.title ? -1 : a.title > b.title ? 1 : 0 : (fa==undefined && fb!=undefined) ? 1 :(fa!=undefined && fb==undefined) ? -1 : fa < fb ? -1 : fa > fb ? 1 : 0;
})
for (var cpt=0; cpt<childNodes.length; cpt++)
this.createSubTree(d, id, childNodes[cpt].title, excludeTag, ancestors.concat(root), level-1, depth-1, sortField, labelField, showButtons);
}
}
config.macros.tagsTree.onClick = function(e){
var id = this.getAttribute("treeId");
var tiddler = this.getAttribute("tiddler");
var n = this.parentNode.nextSibling;
var isOpen = n.style.display != "none";
if(config.options.chkAnimate && anim && typeof Slider == "function")
anim.startAnimating(new Slider(n,!isOpen,null,"none"));
else
n.style.display = isOpen ? "none" : "block";
this.firstChild.nodeValue = isOpen ? config.macros.tagsTree.expand : config.macros.tagsTree.collapse;
config.macros.tagsTree.trees[id][tiddler]=!isOpen;
return false;
}
config.macros.tagsTree.getChildNodes = function(root ,ancestors){
var childs = store.getTaggedTiddlers(root);
var result = new Array();
for (var cpt=0; cpt<childs.length; cpt++)
if (childs[cpt].title!=root && ancestors.indexOf(childs[cpt].title)==-1) result.push(childs[cpt]);
return result;
}
config.macros.tagsTree.getRootTags = function(excludeTag){
var tags = store.getTags(excludeTag);
tags.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
var result = new Array();
for (var cpt=0; cpt<tags.length; cpt++) {
var t = store.getTiddler(tags[cpt][0]);
if (!t || t.tags.length==0) result.push(t ? t : {title:tags[cpt][0],fields:{}});
}
return result;
}
config.macros.tagsTree.getId = function(element){
while (!element.id && element.parentNode) element=element.parentNode;
return element.id ? element.id : "<html>";
}
config.shadowTiddlers.TagsTreeStyleSheet = "/*{{{*/\n";
config.shadowTiddlers.TagsTreeStyleSheet +=".leaf, .subtree {display:block; margin-left : 0.5em}\n";
config.shadowTiddlers.TagsTreeStyleSheet +=".subtree {margin-bottom:0.5em}\n";
config.shadowTiddlers.TagsTreeStyleSheet +="#mainMenu {text-align:left}\n";
config.shadowTiddlers.TagsTreeStyleSheet +=".branch .button {border:1px solid #DDD; color:#AAA;font-size:9px;padding:0 2px;margin-right:0.3em;vertical-align:middle;text-align:center;}\n";
config.shadowTiddlers.TagsTreeStyleSheet +="/*}}}*/";
store.addNotification("TagsTreeStyleSheet", refreshStyles);
config.shadowTiddlers.MainMenu="<<tagsTree>>"
config.shadowTiddlers.PageTemplate = config.shadowTiddlers.PageTemplate.replace(/id='mainMenu' refresh='content' /,"id='mainMenu' refresh='content' force='true' ")
//}}}
/*{{{*/
.leaf, .subtree {display:block}
/* 設定 Tree 第一層的 left margin */
#mainMenu>.subtree {margin-left:0em}
.leaf {margin-left:0.2em; margin-bottom:0.2em; color:navy;}
.subtree {margin-left:0.8em;}
.branch .button {
border:1px solid #DDD; color:#999;
font-size:10px;padding:0 4px;
margin-right:0.5em;
vertical-align:middle;text-align:center;
}
/*}}}*/
/***
|''Name:''|TiddlersBarPlugin|
|''Description:''|A bar to switch between tiddlers through tabs (like browser tabs bar).|
|''Version:''|1.2.5|
|''Date:''|Jan 18,2008|
|''Source:''|http://visualtw.ouvaton.org/VisualTW.html|
|''Author:''|Pascal Collin|
|''License:''|[[BSD open source license|License]]|
|''~CoreVersion:''|2.1.0|
|''Browser:''|Firefox 2.0; InternetExplorer 6.0, others|
!Demos
On [[homepage|http://visualtw.ouvaton.org/VisualTW.html]], open several tiddlers to use the tabs bar.
!Installation
#import this tiddler from [[homepage|http://visualtw.ouvaton.org/VisualTW.html]] (tagged as systemConfig)
#save and reload
#''if you're using a custom [[PageTemplate]]'', add {{{<div id='tiddlersBar' refresh='none' ondblclick='config.macros.tiddlersBar.onTiddlersBarAction(event)'></div>}}} before {{{<div id='tiddlerDisplay'></div>}}}
#optionally, adjust StyleSheetTiddlersBar
!Tips
*Doubleclick on the tiddlers bar (where there is no tab) create a new tiddler.
*Tabs include a button to close {{{x}}} or save {{{!}}} their tiddler.
*By default, click on the current tab close all others tiddlers.
!Configuration options
<<option chkDisableTabsBar>> Disable the tabs bar (to print, by example).
<<option chkHideTabsBarWhenSingleTab >> Automatically hide the tabs bar when only one tiddler is displayed.
<<option txtSelectedTiddlerTabButton>> ''selected'' tab command button.
/<<option txtPreviousTabKey>> previous tab access key.
<<option txtNextTabKey>> next tab access key.
!Code
***/
//{{{
config.options.chkDisableTabsBar = config.options.chkDisableTabsBar ? config.options.chkDisableTabsBar : false;
config.options.chkHideTabsBarWhenSingleTab = config.options.chkHideTabsBarWhenSingleTab ? config.options.chkHideTabsBarWhenSingleTab : false;
config.options.txtSelectedTiddlerTabButton = config.options.txtSelectedTiddlerTabButton ? config.options.txtSelectedTiddlerTabButton : "closeOthers";
config.options.txtPreviousTabKey = config.options.txtPreviousTabKey ? config.options.txtPreviousTabKey : "";
config.options.txtNextTabKey = config.options.txtNextTabKey ? config.options.txtNextTabKey : "";
config.macros.tiddlersBar = {
tooltip : "see ",
tooltipClose : "click here to close this tab",
tooltipSave : "click here to save this tab",
promptRename : "Enter tiddler new name",
currentTiddler : "",
previousState : false,
previousKey : config.options.txtPreviousTabKey,
nextKey : config.options.txtNextTabKey,
tabsAnimationSource : null, //use document.getElementById("tiddlerDisplay") if you need animation on tab switching.
handler: function(place,macroName,params) {
var previous = null;
if (config.macros.tiddlersBar.isShown())
story.forEachTiddler(function(title,e){
if (title==config.macros.tiddlersBar.currentTiddler){
var d = createTiddlyElement(null,"span",null,"tab tabSelected");
config.macros.tiddlersBar.createActiveTabButton(d,title);
if (previous && config.macros.tiddlersBar.previousKey) previous.setAttribute("accessKey",config.macros.tiddlersBar.nextKey);
previous = "active";
}
else {
var d = createTiddlyElement(place,"span",null,"tab tabUnselected");
var btn = createTiddlyButton(d,title,config.macros.tiddlersBar.tooltip + title,config.macros.tiddlersBar.onSelectTab);
btn.setAttribute("tiddler", title);
if (previous=="active" && config.macros.tiddlersBar.nextKey) btn.setAttribute("accessKey",config.macros.tiddlersBar.previousKey);
previous=btn;
}
var isDirty =story.isDirty(title);
var c = createTiddlyButton(d,isDirty ?"!":"x",isDirty?config.macros.tiddlersBar.tooltipSave:config.macros.tiddlersBar.tooltipClose, isDirty ? config.macros.tiddlersBar.onTabSave : config.macros.tiddlersBar.onTabClose,"tabButton");
c.setAttribute("tiddler", title);
if (place.childNodes) {
place.insertBefore(document.createTextNode(" "),place.firstChild); // to allow break line here when many tiddlers are open
place.insertBefore(d,place.firstChild);
}
else place.appendChild(d);
})
},
refresh: function(place,params){
removeChildren(place);
config.macros.tiddlersBar.handler(place,"tiddlersBar",params);
if (config.macros.tiddlersBar.previousState!=config.macros.tiddlersBar.isShown()) {
story.refreshAllTiddlers();
if (config.macros.tiddlersBar.previousState) story.forEachTiddler(function(t,e){e.style.display="";});
config.macros.tiddlersBar.previousState = !config.macros.tiddlersBar.previousState;
}
},
isShown : function(){
if (config.options.chkDisableTabsBar) return false;
if (!config.options.chkHideTabsBarWhenSingleTab) return true;
var cpt=0;
story.forEachTiddler(function(){cpt++});
return (cpt>1);
},
selectNextTab : function(){ //used when the current tab is closed (to select another tab)
var previous="";
story.forEachTiddler(function(title){
if (!config.macros.tiddlersBar.currentTiddler) {
story.displayTiddler(null,title);
return;
}
if (title==config.macros.tiddlersBar.currentTiddler) {
if (previous) {
story.displayTiddler(null,previous);
return;
}
else config.macros.tiddlersBar.currentTiddler=""; // so next tab will be selected
}
else previous=title;
});
},
onSelectTab : function(e){
var t = this.getAttribute("tiddler");
if (t) story.displayTiddler(null,t);
return false;
},
onTabClose : function(e){
var t = this.getAttribute("tiddler");
if (t) {
if(story.hasChanges(t) && !readOnly) {
if(!confirm(config.commands.cancelTiddler.warning.format([t])))
return false;
}
story.closeTiddler(t);
}
return false;
},
onTabSave : function(e) {
var t = this.getAttribute("tiddler");
if (!e) e=window.event;
if (t) config.commands.saveTiddler.handler(e,null,t);
return false;
},
onSelectedTabButtonClick : function(event,src,title) {
var t = this.getAttribute("tiddler");
if (!event) event=window.event;
if (t && config.options.txtSelectedTiddlerTabButton && config.commands[config.options.txtSelectedTiddlerTabButton])
config.commands[config.options.txtSelectedTiddlerTabButton].handler(event, src, t);
return false;
},
onTiddlersBarAction: function(event) {
var source = event.target ? event.target.id : event.srcElement.id; // FF uses target and IE uses srcElement;
if (source=="tiddlersBar") story.displayTiddler(null,'New Tiddler',DEFAULT_EDIT_TEMPLATE,false,null,null);
},
createActiveTabButton : function(place,title) {
if (config.options.txtSelectedTiddlerTabButton && config.commands[config.options.txtSelectedTiddlerTabButton]) {
var btn = createTiddlyButton(place, title, config.commands[config.options.txtSelectedTiddlerTabButton].tooltip ,config.macros.tiddlersBar.onSelectedTabButtonClick);
btn.setAttribute("tiddler", title);
}
else
createTiddlyText(place,title);
}
}
story.coreCloseTiddler = story.coreCloseTiddler? story.coreCloseTiddler : story.closeTiddler;
story.coreDisplayTiddler = story.coreDisplayTiddler ? story.coreDisplayTiddler : story.displayTiddler;
story.closeTiddler = function(title,animate,unused) {
if (title==config.macros.tiddlersBar.currentTiddler)
config.macros.tiddlersBar.selectNextTab();
story.coreCloseTiddler(title,false,unused); //disable animation to get it closed before calling tiddlersBar.refresh
var e=document.getElementById("tiddlersBar");
if (e) config.macros.tiddlersBar.refresh(e,null);
}
story.displayTiddler = function(srcElement,tiddler,template,animate,unused,customFields,toggle){
story.coreDisplayTiddler(config.macros.tiddlersBar.tabsAnimationSource,tiddler,template,animate,unused,customFields,toggle);
var title = (tiddler instanceof Tiddler)? tiddler.title : tiddler;
if (config.macros.tiddlersBar.isShown()) {
story.forEachTiddler(function(t,e){
if (t!=title) e.style.display="none";
else e.style.display="";
})
config.macros.tiddlersBar.currentTiddler=title;
}
var e=document.getElementById("tiddlersBar");
if (e) config.macros.tiddlersBar.refresh(e,null);
}
var coreRefreshPageTemplate = coreRefreshPageTemplate ? coreRefreshPageTemplate : refreshPageTemplate;
refreshPageTemplate = function(title) {
coreRefreshPageTemplate(title);
if (config.macros.tiddlersBar) config.macros.tiddlersBar.refresh(document.getElementById("tiddlersBar"));
}
//ensureVisible=function (e) {return 0} //disable bottom scrolling (not useful now)
config.shadowTiddlers.StyleSheetTiddlersBar = "/*{{{*/\n";
config.shadowTiddlers.StyleSheetTiddlersBar += "#tiddlersBar .button {border:0}\n";
config.shadowTiddlers.StyleSheetTiddlersBar += "#tiddlersBar .tab {white-space:nowrap}\n";
config.shadowTiddlers.StyleSheetTiddlersBar += "#tiddlersBar {padding : 1em 0.5em 2px 0.5em}\n";
config.shadowTiddlers.StyleSheetTiddlersBar += ".tabUnselected .tabButton, .tabSelected .tabButton {padding : 0 2px 0 2px; margin: 0 0 0 4px;}\n";
config.shadowTiddlers.StyleSheetTiddlersBar += ".tiddler, .tabContents {border:1px [[ColorPalette::TertiaryPale]] solid;}\n";
config.shadowTiddlers.StyleSheetTiddlersBar +="/*}}}*/";
store.addNotification("StyleSheetTiddlersBar", refreshStyles);
config.refreshers.none = function(){return true;}
config.shadowTiddlers.PageTemplate=config.shadowTiddlers.PageTemplate.replace(/<div id='tiddlerDisplay'><\/div>/m,"<div id='tiddlersBar' refresh='none' ondblclick='config.macros.tiddlersBar.onTiddlersBarAction(event)'></div>\n<div id='tiddlerDisplay'></div>");
//}}}
<<search>> | [[首頁]] | [[土芭樂 3.0 - 數位新思路|http://tbala.net/]] | [[TiddlyWiki 練功坊|http://tiddlywiki.tbala.net/]] | <<newTiddler label:"新增文章">> <<saveChanges>>
<!--{{{-->
<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<!--
<div class='tagging' macro='tagging'></div>
-->
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
/***
|''Name:''|abego.IncludePlugin|
|''Version:''|1.0.0 (2007-02-08)|
|''Type:''|plugin|
|''Source:''|http://tiddlywiki.abego-software.de/#IncludePlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Documentation:''|[[IncludePlugin Documentation|http://tiddlywiki.abego-software.de/#%5B%5BIncludePlugin%20Documentation%5D%5D]]|
|''Community:''|([[del.icio.us|http://del.icio.us/post?url=http://tiddlywiki.abego-software.de/index.html%23IncludePlugin]]) ([[Support|http://groups.google.com/group/TiddlyWiki]])|
|''Copyright:''|© 2007 [[abego Software|http://www.abego-software.de]]|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''~CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.5.0.9 or better; Internet Explorer 6.0|
***/
//{{{
// Ensure the global abego namespace is set up.
if (!window.abego) window.abego = {};
var invokeLater = function(func, delay, priority) {
return abego.invokeLater ? abego.invokeLater(func, delay, priority) : setTimeout(func,delay);
};
// Asynchronously load the given (local or remote) file.
//
// @param url either an URL or a local file path to a file
// Examples:
// * http://www.abego-software.de/index.html
// * file:///C:/abegoWebSite-Copy/index.html
// * C:\abegoWebSite-Copy\index.html (for Windows machines)
// (Notice: backslashes in JavaScript string constants must be escaped,
// i.e. the last example must be written as: "C:\\abegoWebSite-Copy\\index.html"
// when "hardcoded" in JavaScript source code)
//
// @param callback
// function(content,url,params,errorMessage)
// called at the end of the operation.
// On success content holds the content of the loaded file.
// On error content is undefined and errorMessage holds an error message.
// params is the params passed into abego.loadFile.
//
// @param params passed through to the callback function
//
abego.loadFile = function(url,callback,params) {
var onLoad = function(status,params,responseText,url,xhr) {
return status
? callback(responseText, url, params)
: callback(undefined, url, params, "Error loading %0".format([url]));
};
// Make sure the URL is a real URL, with protocol prefix etc.
if (url.search(/^((http(s)?)|(file)):/) != 0) {
// no protocol specified.
if (url.search(/^((.\:\\)|(\\\\)|(\/))/) == 0) {
// "url" is an "absolute" path to a local file. Prefix it with file://
url = "file://"+url;
} else {
// "url" is a "relative" URL. Make it absolute
// prefix the url with the directory containing the current document
// (This also includes the protocol prefix)
var documentPath = document.location.toString();
var i = documentPath.lastIndexOf("/");
url = documentPath.substr(0,i+1)+url;
}
// replace every \ by a /, to cover Windows style pathes
url = url.replace(/\\/mg,"/");
}
loadRemoteFile(url,onLoad,params);
};
// Asynchronously load the given (local or remote) TiddlyWiki store.
//
// @param url either an URL or a local file path to a TiddlyWiki file (absolute or relative)
// Examples:
// * http://www.abego-software.de/index.html
// * file:///C:/abegoWebSite-Copy/index.html
// * include/beta.html
// * C:\abegoWebSite-Copy\index.html (for Windows machines)
// (Notice: backslashes in JavaScript string constants must be escaped,
// i.e. the last example must be written as: "C:\\abegoWebSite-Copy\\index.html"
// when "hardcoded" in JavaScript source code)
//
// @param callbackWithStore
// function(theStore,url,params,errorMessage)
// called at the end of the operation.
// On success theStore holds the loaded store (a TiddlyWiki object).
// On error theStore is undefined and errorMessage holds an error message.
// params is the params passed into abego.loadTiddlyWikiStore
//
// @param params passed through to the callbackWithStore
//
// @progress [optional] function(message, sender, state, url, params) called in various situations during the operation,
// typically used to show "the progress" of the operation.
// sender: the constant "abego.loadTiddlyWikiStore"
// state: one of these: "Started", "Processing", "Done", "Failed"
// "Processing" means the data has been received and in now processed.
//
abego.loadTiddlyWikiStore = function(url,callbackWithStore,params,progress) {
var sendProgress = function(message, state) {
if (progress)
progress(message,"abego.loadTiddlyWikiStore",state,url,params);
};
// Load contents of a TiddlyWiki from a string
//# Returns null on success, an error message otherwise.
//# based on code from TiddlyWiki 2.2 alpha
var importTiddlyWiki = function(store,text)
{
// Crack out the content - will be refactored to share code with saveChanges()
var posOpeningDiv = text.indexOf(startSaveArea);
var limitClosingDiv = text.indexOf("<!--POST-BODY-END--"+">");
var posClosingDiv = text.lastIndexOf(endSaveArea,limitClosingDiv == -1 ? text.length : limitClosingDiv);
if((posOpeningDiv == -1) || (posClosingDiv == -1))
return config.messages.invalidFileError.format([url]);
var content = "<html><body>" + text.substring(posOpeningDiv,posClosingDiv + endSaveArea.length) + "</body></html>";
// Create the iframe
var iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);
var doc = iframe.document;
if(iframe.contentDocument)
doc = iframe.contentDocument; // For NS6
else if(iframe.contentWindow)
doc = iframe.contentWindow.document; // For IE5.5 and IE6
// Put the content in the iframe
doc.open();
doc.writeln(content);
doc.close();
// Load the content into a TiddlyWiki() object
var storeArea = doc.getElementById("storeArea");
store.loadFromDiv(storeArea,"store");
// Get rid of the iframe
iframe.parentNode.removeChild(iframe);
return null;
};
var sendError = function(message) {
sendProgress("Error when loading %0".format([url]),"Failed");
callbackWithStore(undefined, url,params, message);
return message;
};
var sendStore = function(store) {
sendProgress("Loaded %0".format([url]),"Done");
callbackWithStore(store, url, params);
return null;
};
var callback = function(content,theURL,params,errorMessage) {
if (content === undefined) {
sendError(errorMessage);
return;
}
sendProgress("Processing %0".format([url]),"Processing");
var orig_invalidFileError = config.messages.invalidFileError;
config.messages.invalidFileError = "The file '%0' does not appear to be a valid TiddlyWiki file";
try {
// Load the content into a TiddlyWiki() object
var importStore = new TiddlyWiki();
var errorText = importTiddlyWiki(importStore,content);
if (errorText)
sendError(errorText);
else
sendStore(importStore);
} catch (ex) {
sendError(exceptionText(ex));
} finally {
config.messages.invalidFileError = orig_invalidFileError;
}
};
sendProgress("Start loading %0".format([url]),"Started");
abego.loadFile(url,callback,params);
};
//==============================================================================
// Include Plugin
(function(){
// only install once
if (abego.TiddlyWikiIncluder) return;
// --------------------------------------------------
// Constants
var WAITING = "waiting";
var LOADING = "loading";
var ANI_DURATION_HIDE_STATE = 1000;
var REFRESH_PRIORITY = -200;
var ANIMATION_PRIORITY = -100;
var UPDATE_STATE_PRIORITY = -300;
// --------------------------------------------------
// Variables
var useInclude;
var includes = []; // [] of Strings. the urls of the stores to include, in the sequence of the calls.
var includedStores = {}; // url(String) -> TiddlyWiki or String; when not (yet) loaded a status or error string.
var pendingOnLoadURLs = []; // [] of String. a list of urls that should be passed with the next "notifyListeners".
var refreshTiddlyWikiTimerID; // for delayed refresh
var listeners = [];
var progress;
// --------------------------------------------------
// Helper functions
var isIncludeEnabled = function() {
if (useInclude === undefined)
useInclude = config.options.chkUseInclude === undefined || config.options.chkUseInclude;
return useInclude;
};
var getMissingIncludeMsg = function(url) {
return "No include specified for %0".format([url])
};
// Called after one or more included TiddlyWikis are loaded
//
var notifyListeners = function() {
var urls = pendingOnLoadURLs;
pendingOnLoadURLs = [];
if (urls.length) {
for (var i= 0; i < listeners.length; i++)
listeners[i](urls);
}
};
var idleCount; // Reset to 0 when the system is "not idle", incremented inside refreshTiddlyWiki
var refreshTiddlyWiki = function() {
// To avoid to much refreshing/flickering don't refresh immediately
// but wait until the system was idle for a certain time.
if (refreshTiddlyWikiTimerID !== undefined) clearInterval(refreshTiddlyWikiTimerID);
idleCount = 0;
var sendDone = function() {
abego.TiddlyWikiIncluder.sendProgress("","","Done");
};
refreshTiddlyWikiTimerID = setInterval(function() {
idleCount++;
if (idleCount <= 10)
return;
clearInterval(refreshTiddlyWikiTimerID);
refreshTiddlyWikiTimerID = undefined;
abego.TiddlyWikiIncluder.sendProgress("Refreshing...","","");
refreshDisplay();
invokeLater(sendDone,0,REFRESH_PRIORITY);
},0);
};
// Calls callback for every loaded store and returns the first non-false/null.. value returned by callback.
//
// @param callback function(store, url)
//
var forEachLoadedStore = function(callback) {
var result;
for (var i = 0; i < includes.length; i++) {
var theStore = abego.TiddlyWikiIncluder.getStore(includes[i]);
if (theStore && (result = callback(theStore, includes[i])))
return result;
}
};
var attachToStore = function() {
if (!window.store)
return invokeLater(attachToStore,100);
var orig_fetchTiddler = store.fetchTiddler;
store.fetchTiddler = function(title) {
var t = orig_fetchTiddler.apply(this,arguments);
if (t) return t;
// When there is a shadowtiddler with that name done look for
// any included tiddler since these would hide the shadow
if (config.shadowTiddlers[title] !== undefined) return undefined;
// Don't look for the "New Tiddler" tiddler in the included TiddlyWikis,
// since returning such a tiddler (that is readonly) will make it impossible
// in the Main TiddlyWiki to create new tiddlers.
if (title == config.macros.newTiddler.title) return undefined;
return forEachLoadedStore(
function(theStore, url) {
var t = theStore.fetchTiddler(title);
if (t)
t.includeURL = url;
return t;
});
};
// We also refresh TiddlyWiki to reflect the new included Tiddlers (if we have any).
if (includes.length)
refreshTiddlyWiki();
};
var includeFromIncludeList = function() {
if (!window.store)
return invokeLater(includeFromIncludeList,100);
var includeListText = store.getTiddlerText("IncludeList");
if (includeListText)
wikify(includeListText,document.createElement("div"));
};
var getFunctionUsingForReallyEachTiddler = function(func) {
var wrapper = function() {
var orig_forEachTiddler = store.forEachTiddler;
var forEachTiddlerWithIncludes = function(callback) {
var done = {};
var includeURL;
var callbackWrapper = function(title, tiddler) {
// ensure every title is only processed once
if (done[title])
return;
done[title] = 1;
// for "included tiddlers" set the includeURL;
if (includeURL)
tiddler.includeURL = includeURL;
callback.apply(this,arguments);
};
// forEachTiddler over the original tiddlers
orig_forEachTiddler.call(store, callbackWrapper);
// add all shadowTiddler titles to done
// (to avoid an included store hides a shadow tiddler)
for (var n in config.shadowTiddlers)
done[n] = 1;
// add all the "New Tiddler" tiddlerto done
// (to avoid an included store (with "New Tiddler") makes it impossible to create new tiddlers)
done[config.macros.newTiddler.title] = 1;
// forEachTiddler over every included store
forEachLoadedStore(
function(theStore, url) {
includeURL = url;
theStore.forEachTiddler(callbackWrapper);
});
};
store.forEachTiddler = forEachTiddlerWithIncludes;
try {
return func.apply(this,arguments);
} finally {
store.forEachTiddler = orig_forEachTiddler;
}
};
return wrapper;
};
var useForReallyEachTiddler = function(object,property) {
return object[property] = getFunctionUsingForReallyEachTiddler(object[property]);
};
//================================================================================
// abego.TiddlyWikiIncluder
abego.TiddlyWikiIncluder = {};
abego.TiddlyWikiIncluder.setProgressFunction = function(func) {
progress = func;
};
abego.TiddlyWikiIncluder.getProgressFunction = function(func) {
return progress;
};
abego.TiddlyWikiIncluder.sendProgress = function(message, sender, state) {
if (progress)
progress.apply(this,arguments);
};
// Called when an included TiddlyWiki could not be loaded.
//
// By default an error message is displayed.
//
abego.TiddlyWikiIncluder.onError = function(url, errorMessage) {
displayMessage("Error when including '%0':\n%1".format([url, errorMessage]));
};
// Returns true when there are "pending" includes, i.e. TiddlyWiki that are not yet loaded.
//
// A TiddlyWiki that failed loading is not pending.
//
abego.TiddlyWikiIncluder.hasPendingIncludes = function() {
for (var i = 0; i < includes.length; i++) {
var state = abego.TiddlyWikiIncluder.getState(includes[i]);
if (state == WAITING || state == LOADING)
return true;
}
return false;
};
// @return [] of Strings, the URLs of the includes
//
abego.TiddlyWikiIncluder.getIncludes = function() {
return includes.slice();
};
// @return [may be null] a state/error text of the store with the given URL, or null when the store is already loaded
//
abego.TiddlyWikiIncluder.getState = function(url) {
var s = includedStores[url];
if (!s)
return getMissingIncludeMsg(url);
return typeof s == "string" ? s : null;
};
// @return [may be null] the (TiddlyWiki) store with the given URL, null if not (yet) loaded.
//
abego.TiddlyWikiIncluder.getStore = function(url) {
var s = includedStores[url];
if (!s)
return getMissingIncludeMsg(url);
return s instanceof TiddlyWiki ? s : null;
};
// Includes the (local or remote) TiddlyWiki store with the given url.
//
// stores with urls already already included are ignored.
//
// @param url see url@abego.loadTiddlyWikiStore
// @param delayMilliSeconds [optional] if defined loading starts delayMilliSeconds later, otherwise "immediately"
//
abego.TiddlyWikiIncluder.include = function(url, delayMilliSeconds) {
if (!isIncludeEnabled() || includedStores[url])
return;
var self = this;
includes.push(url);
includedStores[url] = WAITING;
var loadStoreCallback = function(theStore,urlInCallback,params,errorMessage) {
if (theStore === undefined) {
includedStores[url] = errorMessage;
self.onError(url, errorMessage);
return;
}
includedStores[url] = theStore;
pendingOnLoadURLs.push(url);
invokeLater(notifyListeners);
};
var loadStore = function() {
includedStores[url] = LOADING;
abego.loadTiddlyWikiStore(url,loadStoreCallback,null,progress);
};
if (delayMilliSeconds)
invokeLater(loadStore, delayMilliSeconds);
else
loadStore();
};
// iterates over all tiddlers of "the store" and all tiddlers of included (and loaded) stores
//
abego.TiddlyWikiIncluder.forReallyEachTiddler = function(callback) {
var caller = function() {
store.forEachTiddler(callback);
};
getFunctionUsingForReallyEachTiddler(caller).call(store);
};
// function abego.TiddlyWikiIncluder.getFunctionUsingForReallyEachTiddler(func)
//
// Returns a function that behaves as func, but every call to store.forEachTiddler will actually
// be a call to forReallyEachTiddler, i.e. iterate over the tiddlers the main store and of the
// included TiddlyWikis
//
// @return the patched function
//
abego.TiddlyWikiIncluder.getFunctionUsingForReallyEachTiddler = getFunctionUsingForReallyEachTiddler;
// function abego.TiddlyWikiIncluder.useForReallyEachTiddler(object,property)
//
// Patches the function hold in the given property of the object in such a way that every call
// to store.forEachTiddler will actually be a call to forReallyEachTiddler, i.e. iterate over the
// tiddlers the main staire and of the included TiddlyWikis
//
// @param object
// @param property the name of the property of the object containing the function to be patched.
// @return the patched function
//
abego.TiddlyWikiIncluder.useForReallyEachTiddler = useForReallyEachTiddler;
// Add a listener function to the TiddlyWikiIncluder.
//
// @param listener function(urls)
// url: [] of Strings, containing the urls of the TiddlyWiki just included
// (see url@abego.TiddlyWikiIncluder.include)
// called whenever one or more TiddlyWiki store are successfully included.
//
abego.TiddlyWikiIncluder.addListener = function(listener) {
listeners.push(listener);
};
// -------------------------------------------------------------------------------
// TiddlyWikiIncluder initialization code
abego.TiddlyWikiIncluder.addListener(refreshTiddlyWiki);
//----------------------------------------------------------------------------
// Options Support
if (config.options.chkUseInclude === undefined) config.options.chkUseInclude = true;
config.shadowTiddlers.AdvancedOptions += "\n<<option chkUseInclude>> Include ~TiddlyWikis (IncludeList | IncludeState | [[help|http://tiddlywiki.abego-software.de/#%5B%5BIncludePlugin%20Documentation%5D%5D]])\n^^(Reload this ~TiddlyWiki to make changes become effective)^^";
config.shadowTiddlers.IncludeState = "<<includeState>>";
//================================================================================
// Default Progress Handling for abego.TiddlyWikiIncluder
var showAnimated = function(e, showing, duration) {
if (!anim || !abego.ShowAnimation) {
e.style.display = showing ? "block" : "none";
return;
}
anim.startAnimating(new abego.ShowAnimation(e,showing,duration));
};
abego.TiddlyWikiIncluder.getDefaultProgressFunction = function() {
setStylesheet(
".includeProgressState{\n"+
"background-color:#FFCC00;\n"+
"position:absolute;\n"+
"right:0.2em;\n"+
"top:0.2em;\n"+
"width:7em;\n"+
"padding-left:0.2em;\n"+
"padding-right:0.2em\n"+
"}\n",
"abegoInclude");
var createStateElem = function() {
var e = document.createElement("div");
e.className = "includeProgressState";
e.style.display = "none";
document.body.appendChild(e);
return e;
};
var stateElem = createStateElem();
var showState = function(message) {
removeChildren(stateElem);
createTiddlyText(stateElem,message);
showAnimated(stateElem,true,0);
};
var hideState = function() {
// hide the state the next idle time
invokeLater(function() {
showAnimated(stateElem,false,ANI_DURATION_HIDE_STATE);
},100,ANIMATION_PRIORITY);
};
var myProgressFunction = function(message, sender, state, url, params) {
if (state == "Done" || state == "Failed") {
hideState();
return;
}
if (sender == "abego.loadTiddlyWikiStore") {
idleCount = 0;
if (state == "Processing")
showState("Including...");
} else {
showState(message);
}
};
return myProgressFunction;
};
abego.TiddlyWikiIncluder.setProgressFunction(abego.TiddlyWikiIncluder.getDefaultProgressFunction());
//================================================================================
// The "include" macro
//
// Syntax: <<include {url}* [delay: {milliSeconds}] [hide: true] >>
//
config.macros.include = {};
config.macros.include.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
params = paramString.parseParams("url",null,true,false,true); // allowEval, cascadeDefaults, names allowed
var delay = parseInt(getParam(params,"delay","0"));
var urls = params[0]["url"];
var hide = getFlag(params, "hide", false);
if (!hide)
createTiddlyText(createTiddlyElement(place,"code"),wikifier.source.substring(wikifier.matchStart, wikifier.nextMatch));
for (var i = 0; urls && i < urls.length; i++)
abego.TiddlyWikiIncluder.include(urls[i],delay);
};
//================================================================================
// The "includeState" macro
//
// Syntax: <<includeState>>
config.macros.includeState = {};
config.macros.includeState.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var getFullState = function () {
var s = "";
var includes = abego.TiddlyWikiIncluder.getIncludes();
if (!includes.length)
return "{{noIncludes{\nNo includes or 'include' is disabled (see AdvancedOptions)\n}}}\n";
s += "|!Address|!State|\n";
for (var i = 0; i < includes.length; i++) {
var inc = includes[i];
s += "|{{{"+inc+"}}}|";
var t = abego.TiddlyWikiIncluder.getState(inc);
s += t ? "{{{"+t+"}}}" : "included";
s += "|\n"
}
s += "|includeState|k\n";
return s;
};
var updateState = function(){
removeChildren(div);
wikify(getFullState(),div);
if (abego.TiddlyWikiIncluder.hasPendingIncludes())
invokeLater(updateState,500,UPDATE_STATE_PRIORITY);
};
var div = createTiddlyElement(place,"div");
invokeLater(updateState,0,UPDATE_STATE_PRIORITY);
};
//================================================================================
// Tiddler extension/modification
var orig_Tiddler_isReadOnly = Tiddler.prototype.isReadOnly;
// Includes tiddlers are readonly.
Tiddler.prototype.isReadOnly = function() {
return orig_Tiddler_isReadOnly.apply(this,arguments) || this.isIncluded();
}
Tiddler.prototype.isIncluded = function() {
return this.includeURL != undefined;
};
Tiddler.prototype.getIncludeURL = function() {
return this.includeURL;
};
//================================================================================
// TiddlyWiki modifications
// In some TiddlyWiki functions the "forEachTiddler" should work on all tiddlers, also those from
// included store. (E.g. TiddlyWiki.prototype.getTags)
//
// But not for all (e.g. TiddlyWiki.prototype.getTiddlers is used for saving, but only the "own" tiddlers should be saved)
//
// Therefore explicitly list the functions that should be "wrapped" to use the "forReallyEachTiddler".
//
var tiddlyWikiFunctionsUsingForReallyEachTiddler = {
getMissingLinks: 1, getOrphans: 1,getTags:1, reverseLookup: 1, updateTiddlers: 1};
for (var n in tiddlyWikiFunctionsUsingForReallyEachTiddler)
useForReallyEachTiddler(TiddlyWiki.prototype,n);
//================================================================================
// Make IntelliTagger "Include-aware"
var patchIntelliTagger = function() {
if (abego.IntelliTagger)
useForReallyEachTiddler(abego.IntelliTagger,"assistTagging");
};
//================================================================================
// Perform plugin startup tasks
attachToStore();
invokeLater(includeFromIncludeList,100);
invokeLater(patchIntelliTagger,100);
})();
//}}}
''horizontal:''
{{{
* menu #1
** [[item #1-1]]
** [[item #1-2]]
** [[item #1-3]]
* menu #2
** [[item #2-1]]
** [[item #2-2]]
** [[menu #2-3]]
* menu #3
** [[item #2-1]]
** [[item #2-2]]
** [[menu #2-3]]
<<dropMenu>>
}}}
* menu #1
** [[item #1-1]]
** [[item #1-2]]
** [[item #1-3]]
* menu #2
** [[item #2-1]]
** [[item #2-2]]
** [[menu #2-3]]
* menu #3
** [[item #2-1]]
** [[item #2-2]]
** [[menu #2-3]]
<<dropMenu>>
''vertical:''
{{{
* menu #1
** [[item #1-1]]
** [[item #1-2]]
** [[item #1-3]]
* menu #2
** [[item #2-1]]
** [[item #2-2]]
** [[menu #2-3]]
<<dropMenu vertical>>
}}}
* menu #1
** [[item #1-1]]
** [[item #1-2]]
** [[item #1-3]]
* menu #2
** [[item #2-1]]
** [[item #2-2]]
** [[menu #2-3]]
<<dropMenu vertical>>
/% %/
!使用說明
{{{
1. 規劃標籤名
2. 根據 "標籤名" 產生 [編輯] 與 [顯示] 的 Template
例如 : 標籤名為 HTML, 那麼二個 Template 的名稱為 HTMLEditTemplate, HTMLViewTemplate,
以後只要文章的標籤有 HTML, 便會自動套用這二個 Template
3. 將以下巨集命令加入 ToolBar 文章中
<<newTiddler label:"新增網頁" tag:"HTML" template:"HTMLEditTemplate" title:"新增網頁">>
^ ^ ^ ^
按鈕名稱 新文章的標籤 新文章第一次使用的 Template 新文章的 Title
* 必須要安裝 TaggedTemplateTweak 這個插件, 才有以上功能
}}}
請點選以下按鈕, 測試 easyEdit 功能
<<newTiddler label:新增網頁 tag:"HTML" template:"HTMLEditTemplate" title:"新增網頁">>
!HTMLEditTemplate
{{{
<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='easyEdit text'></div>
^
這是重點
<div class='editor' macro='edit tags'></div>
<div class='editorFooter'>
<span macro='message views.editor.tagPrompt'></span>
<span macro='tagChooser'></span>
</div>
}}}
!HTMLViewTemplate
{{{
<div class='toolbar' macro='toolbar closeTiddler closeOthers +easyEdit > fields syncing permalink references jump'></div>
^
這是重點
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
}}}
/***
|''Name:''|easyEditPlugin|
|''Description:''|Lite and extensible Wysiwyg editor for TiddlyWiki.|
|''Version:''|1.3.3|
|''Date:''|Dec 21,2007|
|''Source:''|http://visualtw.ouvaton.org/VisualTW.html|
|''Author:''|Pascal Collin|
|''License:''|[[BSD open source license|License]]|
|''~CoreVersion:''|2.1.0|
|''Browser:''|Firefox 2.0; InternetExplorer 6.0|
!Installation
#import the plugin,
#save and reload,
#use the <<toolbar easyEdit>> button in the tiddler's toolbar (in default ViewTemplate) or add {{{easyEdit}}} command in your own toolbar.
! Useful Addons
*[[HTMLFormattingPlugin|http://www.tiddlytools.com/#HTMLFormattingPlugin]] to embed wiki syntax in html tiddlers.<<br>>//__Tips__ : When this plugin is installed, you can use anchor syntax to link tiddlers in wysiwyg mode (example : #example). Anchors are converted back and from wiki syntax when editing.//
*[[TaggedTemplateTweak|http://www.TiddlyTools.com/#TaggedTemplateTweak]] to use alternative ViewTemplate/EditTemplate for tiddler's tagged with specific tag values.
!Configuration
|Buttons in the toolbar (empty = all).<<br>>//Example : bold,underline,separator,forecolor//<<br>>The buttons will appear in this order.| <<option txtEasyEditorButtons>>|
|EasyEditor default height | <<option txtEasyEditorHeight>>|
|Stylesheet applied to the edited richtext |[[EasyEditDocStyleSheet]]|
|Template called by the {{{write}}} button |[[EasyEditTemplate]]|
!How to extend EasyEditor
*To add your own buttons, add some code like the following in a systemConfig tagged tiddler (//use the prompt attribute only if there is a parameter//) :
**{{{EditorToolbar.buttons.heading = {label:"H", toolTip : "Set heading level", prompt: "Enter heading level"};}}}
**{{{EditorToolbar.buttonsList +=",heading";}}}
*To get the list of all possible commands, see the documentation of the [[Gecko built-in rich text editor|http://developer.mozilla.org/en/docs/Midas]] or the [[IE command identifiers|http://msdn2.microsoft.com/en-us/library/ms533049.aspx]].
*To go further in customization, see [[Link button|EasyEditPlugin-LinkButton]] as an example.
!Code
***/
//{{{
var geckoEditor={};
var IEeditor={};
config.options.txtEasyEditorHeight = config.options.txtEasyEditorHeight ? config.options.txtEasyEditorHeight : "500px";
config.options.txtEasyEditorButtons = config.options.txtEasyEditorButtons ? config.options.txtEasyEditorButtons : "";
// TW2.1.x compatibility
config.browser.isGecko = config.browser.isGecko ? config.browser.isGecko : (config.userAgent.indexOf("gecko") != -1);
config.macros.annotations = config.macros.annotations ? config.macros.annotations : {handler : function() {}}
// EASYEDITOR MACRO
config.macros.easyEdit = {
handler : function(place,macroName,params,wikifier,paramString,tiddler) {
var field = params[0];
var height = params[1] ? params[1] : config.options.txtEasyEditorHeight;
var editor = field ? new easyEditor(tiddler,field,place,height) : null;
},
gather: function(element){
var iframes = element.getElementsByTagName("iframe");
if (iframes.length!=1) return null
var text = "<html>"+iframes[0].contentWindow.document.body.innerHTML+"</html>";
text = config.browser.isGecko ? geckoEditor.postProcessor(text) : (config.browser.isIE ? IEeditor.postProcessor(text) : text);
return text;
}
}
// EASYEDITOR CLASS
function easyEditor(tiddler,field,place,height) {
this.tiddler = tiddler;
this.field = field;
this.browser = config.browser.isGecko ? geckoEditor : (config.browser.isIE ? IEeditor : null);
this.wrapper = createTiddlyElement(place,"div",null,"easyEditor");
this.wrapper.setAttribute("easyEdit",this.field);
this.iframe = createTiddlyElement(null,"iframe");
this.browser.setupFrame(this.iframe,height,contextualCallback(this,this.onload));
this.wrapper.appendChild(this.iframe);
}
easyEditor.prototype.onload = function(){
this.editor = this.iframe.contentWindow;
this.doc = this.editor.document;
if (!this.browser.isDocReady(this.doc)) return null;
if (!this.tiddler.isReadOnly() && this.doc.designMode.toLowerCase()!="on") {
this.doc.designMode = "on";
if (this.browser.reloadOnDesignMode) return false; // IE fire readystatechange after designMode change
}
var internalCSS = store.getTiddlerText("EasyEditDocStyleSheet");
setStylesheet(internalCSS,"EasyEditDocStyleSheet",this.doc);
this.browser.initContent(this.doc,store.getValue(this.tiddler,this.field));
var barElement=createTiddlyElement(null,"div",null,"easyEditorToolBar");
this.wrapper.insertBefore(barElement,this.wrapper.firstChild);
this.toolbar = new EditorToolbar(this.doc,barElement,this.editor);
this.browser.plugEvents(this.doc,contextualCallback(this,this.scheduleButtonsRefresh));
this.editor.focus();
}
easyEditor.SimplePreProcessoror = function(text) {
var re = /^<html>(.*)<\/html>$/m;
var htmlValue = re.exec(text);
var value = (htmlValue && (htmlValue.length>0)) ? htmlValue[1] : text;
return value;
}
easyEditor.prototype.scheduleButtonsRefresh=function() { //doesn't refresh buttons state when rough typing
if (this.nextUpdate) window.clearTimeout(this.nextUpdate);
this.nextUpdate = window.setTimeout(contextualCallback(this.toolbar,EditorToolbar.onUpdateButton),easyEditor.buttonDelay);
}
easyEditor.buttonDelay = 200;
// TOOLBAR CLASS
function EditorToolbar(target,parent,window){
this.target = target;
this.window=window;
this.elements={};
var row = createTiddlyElement(createTiddlyElement(createTiddlyElement(parent,"table"),"tbody"),"tr");
var buttons = (config.options.txtEasyEditorButtons ? config.options.txtEasyEditorButtons : EditorToolbar.buttonsList).split(",");
for(var cpt = 0; cpt < buttons.length; cpt++){
var b = buttons[cpt];
var button = EditorToolbar.buttons[b];
if (button) {
if (button.separator)
createTiddlyElement(row,"td",null,"separator").innerHTML+=" ";
else {
var cell=createTiddlyElement(row,"td",null,b+"Button");
if (button.onCreate) button.onCreate.call(this, cell, b);
else EditorToolbar.createButton.call(this, cell, b);
}
}
}
}
EditorToolbar.createButton = function(place,name){
this.elements[name] = createTiddlyButton(place,EditorToolbar.buttons[name].label,EditorToolbar.buttons[name].toolTip,contextualCallback(this,EditorToolbar.onCommand(name)),"button");
}
EditorToolbar.onCommand = function(name){
var button = EditorToolbar.buttons[name];
return function(){
var parameter = false;
if (button.prompt) {
var parameter = this.target.queryCommandValue(name);
parameter = prompt(button.prompt,parameter);
}
if (parameter != null) {
this.target.execCommand(name, false, parameter);
EditorToolbar.onUpdateButton.call(this);
}
return false;
}
}
EditorToolbar.getCommandState = function(target,name){
try {return target.queryCommandState(name)}
catch(e){return false}
}
EditorToolbar.onRefreshButton = function (name){
if (EditorToolbar.getCommandState(this.target,name)) addClass(this.elements[name].parentNode,"buttonON");
else removeClass(this.elements[name].parentNode,"buttonON");
this.window.focus();
}
EditorToolbar.onUpdateButton = function(){
for (b in this.elements)
if (EditorToolbar.buttons[b].onRefresh) EditorToolbar.buttons[b].onRefresh.call(this,b);
else EditorToolbar.onRefreshButton.call(this,b);
}
EditorToolbar.buttons = {
separator : {separator : true},
bold : {label:"B", toolTip : "Bold"},
italic : {label:"I", toolTip : "Italic"},
underline : {label:"U", toolTip : "Underline"},
strikethrough : {label:"S", toolTip : "Strikethrough"},
insertunorderedlist : {label:"\u25CF", toolTip : "Unordered list"},
insertorderedlist : {label:"1.", toolTip : "Ordered list"},
justifyleft : {label:"[\u2261", toolTip : "Align left"},
justifyright : {label:"\u2261]", toolTip : "Align right"},
justifycenter : {label:"\u2261", toolTip : "Align center"},
justifyfull : {label:"[\u2261]", toolTip : "Justify"},
removeformat : {label:"\u00F8", toolTip : "Remove format"},
fontsize : {label:"\u00B1", toolTip : "Set font size", prompt: "Enter font size"},
forecolor : {label:"C", toolTip : "Set font color", prompt: "Enter font color"},
fontname : {label:"F", toolTip : "Set font name", prompt: "Enter font name"},
heading : {label:"H", toolTip : "Set heading level", prompt: "Enter heading level (example : h1, h2, ...)"},
indent : {label:"\u2192[", toolTip : "Indent paragraph"},
outdent : {label:"[\u2190", toolTip : "Outdent paragraph"},
inserthorizontalrule : {label:"\u2014", toolTip : "Insert an horizontal rule"},
insertimage : {label:"\u263C", toolTip : "Insert image", prompt: "Enter image url"}
}
EditorToolbar.buttonsList = "bold,italic,underline,strikethrough,separator,increasefontsize,decreasefontsize,fontsize,forecolor,fontname,separator,removeformat,separator,insertparagraph,insertunorderedlist,insertorderedlist,separator,justifyleft,justifyright,justifycenter,justifyfull,indent,outdent,separator,heading,separator,inserthorizontalrule,insertimage";
if (config.browser.isGecko) {
EditorToolbar.buttons.increasefontsize = {onCreate : EditorToolbar.createButton, label:"A", toolTip : "Increase font size"};
EditorToolbar.buttons.decreasefontsize = {onCreate : EditorToolbar.createButton, label:"A", toolTip : "Decrease font size"};
EditorToolbar.buttons.insertparagraph = {label:"P", toolTip : "Format as paragraph"};
}
// GECKO (FIREFOX, ...) BROWSER SPECIFIC METHODS
geckoEditor.setupFrame = function(iframe,height,callback) {
iframe.setAttribute("style","width: 100%; height:" + height);
iframe.addEventListener("load",callback,true);
}
geckoEditor.plugEvents = function(doc,onchange){
doc.addEventListener("keyup", onchange, true);
doc.addEventListener("keydown", onchange, true);
doc.addEventListener("click", onchange, true);
}
geckoEditor.postProcessor = function(text){return text};
geckoEditor.preProcessor = function(text){return easyEditor.SimplePreProcessoror(text)}
geckoEditor.isDocReady = function() {return true;}
geckoEditor.reloadOnDesignMode=false;
geckoEditor.initContent = function(doc,content){
if (content) doc.execCommand("insertHTML",false,geckoEditor.preProcessor(content));
}
// INTERNET EXPLORER BROWSER SPECIFIC METHODS
IEeditor.setupFrame = function(iframe,height,callback) {
iframe.width="99%"; //IE displays the iframe at the bottom if 100%. CSS layout problem ? I don't know. To be studied...
iframe.height=height.toString();
iframe.attachEvent("onreadystatechange",callback);
}
IEeditor.plugEvents = function(doc,onchange){
doc.attachEvent("onkeyup", onchange);
doc.attachEvent("onkeydown", onchange);
doc.attachEvent("onclick", onchange);
}
IEeditor.isDocReady = function(doc){
if (doc.readyState!="complete") return false;
if (!doc.body) return false;
return (doc && doc.getElementsByTagName && doc.getElementsByTagName("head") && doc.getElementsByTagName("head").length>0);
}
IEeditor.postProcessor = function(text){return text};
IEeditor.preProcessor = function(text){return easyEditor.SimplePreProcessoror(text)}
IEeditor.reloadOnDesignMode=true;
IEeditor.initContent = function(doc,content){
if (content) doc.body.innerHTML=IEeditor.preProcessor(content);
}
function contextualCallback(obj,func){
return function(){return func.call(obj)}
}
Story.prototype.previousGatherSaveEasyEdit = Story.prototype.previousGatherSaveEasyEdit ? Story.prototype.previousGatherSaveEasyEdit : Story.prototype.gatherSaveFields; // to avoid looping if this line is called several times
Story.prototype.gatherSaveFields = function(e,fields){
if(e && e.getAttribute) {
var f = e.getAttribute("easyEdit");
if(f){
var newVal = config.macros.easyEdit.gather(e);
if (newVal) fields[f] = newVal;
}
this.previousGatherSaveEasyEdit(e, fields);
}
}
config.commands.easyEdit={
text: "編輯網頁",
tooltip: "Edit this tiddler in wysiwyg mode",
readOnlyText: "檢視",
readOnlyTooltip: "View the source of this tiddler",
handler : function(event,src,title) {
clearMessage();
var tiddlerElem = document.getElementById(story.idPrefix + title);
var fields = tiddlerElem.getAttribute("tiddlyFields");
story.displayTiddler(null,title,"EasyEditTemplate",false,null,fields);
return false;
}
}
config.shadowTiddlers.ViewTemplate = config.shadowTiddlers.ViewTemplate.replace(/\+editTiddler/,"+editTiddler easyEdit");
config.shadowTiddlers.EasyEditTemplate = config.shadowTiddlers.EditTemplate.replace(/macro='edit text'/,"macro='easyEdit text'");
config.shadowTiddlers.EasyEditToolBarStyleSheet = "/*{{{*/\n";
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar {font-size:0.8em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".editor iframe {border:1px solid #DDD}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar td{border:1px solid #888; padding:2px 1px 2px 1px; vertical-align:middle}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar td.separator{border:0}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .button{border:0;color:#444}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .buttonON{background-color:#EEE}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar {margin:0.25em 0}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .boldButton {font-weight:bold}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .italicButton .button {font-style:italic;padding-right:0.65em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .underlineButton .button {text-decoration:underline}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .strikeButton .button {text-decoration:line-through}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .unorderedListButton {margin-left:0.7em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .justifyleftButton .button {padding-left:0.1em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .justifyrightButton .button {padding-right:0.1em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .justifyfullButton .button, .easyEditorToolBar .indentButton .button, .easyEditorToolBar .outdentButton .button {padding-left:0.1em;padding-right:0.1em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .increasefontsizeButton .button {padding-left:0.15em;padding-right:0.15em; font-size:1.3em; line-height:0.75em}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .decreasefontsizeButton .button {padding-left:0.4em;padding-right:0.4em; font-size:0.8em;}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .forecolorButton .button {color:red;}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet += ".easyEditorToolBar .fontnameButton .button {font-family:serif}\n" ;
config.shadowTiddlers.EasyEditToolBarStyleSheet +="/*}}}*/";
store.addNotification("EasyEditToolBarStyleSheet", refreshStyles);
config.shadowTiddlers.EasyEditDocStyleSheet = "/*{{{*/\n \n/*}}}*/";
if (config.annotations) config.annotations.EasyEditDocStyleSheet = "This stylesheet is applied when editing a text with the wysiwyg easyEditor";
//}}}
/***
!Link button add-on
***/
//{{{
EditorToolbar.createLinkButton = function(place,name) {
this.elements[name] = createTiddlyButton(place,EditorToolbar.buttons[name].label,EditorToolbar.buttons[name].toolTip,contextualCallback(this,EditorToolbar.onInputLink()),"button");
}
EditorToolbar.onInputLink = function() {
return function(){
var browser = config.browser.isGecko ? geckoEditor : (config.browser.isIE ? IEeditor : null);
var value = browser ? browser.getLink(this.target) : "";
value = prompt(EditorToolbar.buttons["createlink"].prompt,value);
if (value) browser.doLink(this.target,value);
else if (value=="") this.target.execCommand("unlink", false, value);
EditorToolbar.onUpdateButton.call(this);
return false;
}
}
EditorToolbar.buttonsList += ",separator,createlink";
EditorToolbar.buttons.createlink = {onCreate : EditorToolbar.createLinkButton, label:"L", toolTip : "Set link", prompt: "Enter link url"};
geckoEditor.getLink=function(doc){
var range=doc.defaultView.getSelection().getRangeAt(0);
var container = range.commonAncestorContainer;
var node = (container.nodeType==3) ? container.parentNode : range.startContainer.childNodes[range.startOffset];
if (node && node.tagName=="A") {
var r=doc.createRange();
r.selectNode(node);
doc.defaultView.getSelection().addRange(r);
return (node.getAttribute("tiddler") ? "#"+node.getAttribute("tiddler") : node.href);
}
else return (container.nodeType==3 ? "#"+container.textContent.substr(range.startOffset, range.endOffset-range.startOffset).replace(/ $/,"") : "");
}
geckoEditor.doLink=function(doc,link){ // store tiddler in a temporary attribute to avoid url encoding of tiddler's name
var pin = "href"+Math.random().toString().substr(3);
doc.execCommand("createlink", false, pin);
var isTiddler=(link.charAt(0)=="#");
var node = doc.defaultView.getSelection().getRangeAt(0).commonAncestorContainer;
var links= (node.nodeType!=3) ? node.getElementsByTagName("a") : [node.parentNode];
for (var cpt=0;cpt<links.length;cpt++)
if (links[cpt].href==pin){
links[cpt].href=isTiddler ? "javascript:;" : link;
links[cpt].setAttribute("tiddler",isTiddler ? link.substr(1) : "");
}
}
geckoEditor.beforeLinkPostProcessor = geckoEditor.beforelinkPostProcessor ? geckoEditor.beforelinkPostProcessor : geckoEditor.postProcessor;
geckoEditor.postProcessor = function(text){
return geckoEditor.beforeLinkPostProcessor(text).replace(/<a tiddler="([^"]*)" href="javascript:;">(.*?)(?:<\/a>)/gi,"[[$2|$1]]").replace(/<a tiddler="" href="/gi,'<a href="');
}
geckoEditor.beforeLinkPreProcessor = geckoEditor.beforeLinkPreProcessor ? geckoEditor.beforeLinkPreProcessor : geckoEditor.preProcessor
geckoEditor.preProcessor = function(text){
return geckoEditor.beforeLinkPreProcessor(text).replace(/\[\[([^|\]]*)\|([^\]]*)]]/g,'<a tiddler="$2" href="javascript:;">$1</a>');
}
IEeditor.getLink=function(doc){
var node=doc.selection.createRange().parentElement();
if (node.tagName=="A") return node.href;
else return (doc.selection.type=="Text"? "#"+doc.selection.createRange().text.replace(/ $/,"") :"");
}
IEeditor.doLink=function(doc,link){
doc.execCommand("createlink", false, link);
}
IEeditor.beforeLinkPreProcessor = IEeditor.beforeLinkPreProcessor ? IEeditor.beforeLinkPreProcessor : IEeditor.preProcessor
IEeditor.preProcessor = function(text){
return IEeditor.beforeLinkPreProcessor(text).replace(/\[\[([^|\]]*)\|([^\]]*)]]/g,'<a ref="#$2">$1</a>');
}
IEeditor.beforeLinkPostProcessor = IEeditor.beforelinkPostProcessor ? IEeditor.beforelinkPostProcessor : IEeditor.postProcessor;
IEeditor.postProcessor = function(text){
return IEeditor.beforeLinkPostProcessor(text).replace(/<a href="#([^>]*)">([^<]*)<\/a>/gi,"[[$2|$1]]");
}
IEeditor.beforeLinkInitContent = IEeditor.beforeLinkInitContent ? IEeditor.beforeLinkInitContent : IEeditor.initContent;
IEeditor.initContent = function(doc,content){
IEeditor.beforeLinkInitContent(doc,content);
var links=doc.body.getElementsByTagName("A");
for (var cpt=0; cpt<links.length; cpt++) {
links[cpt].href=links[cpt].ref; //to avoid IE conversion of relative URLs to absolute
links[cpt].removeAttribute("ref");
}
}
config.shadowTiddlers.EasyEditToolBarStyleSheet += "\n/*{{{*/\n.easyEditorToolBar .createlinkButton .button {color:blue;text-decoration:underline;}\n/*}}}*/";
config.shadowTiddlers.EasyEditDocStyleSheet += "\n/*{{{*/\na {color:#0044BB;font-weight:bold}\n/*}}}*/";
//}}}
For this "List" task we don't use the default action "addToList" (that simply adds all selected items to the list) but create the list using the "write" action and refer to the build-in variable "index" that is incremented for every tiddler being processed.
{{{
<<forEachTiddler
where
'tiddler.tags.contains("Notes")'
write
'(index < 10) ? "* [["+tiddler.title+"]]\n" : ""'
>>
}}}
In the write parameter there is a conditional output: when we are processing the tiddlers 0 to 9 it will write a line with:
{{{* [[theTiddlerName]]}}}
Tiddler 10 and the following ones will generate no output (as the empty string is specified).
''//Result://''
<<forEachTiddler
where
'tiddler.tags.contains("Notes")'
write
'(index < 10) ? "* [["+tiddler.title+"]]\n" : ""'
>>
{{{
<<forEachTiddler
where
'tiddler.tags.contains("plugin")'
write
'""'
end 'count+" Tiddlers found\n"'
none '"No Tiddlers found\n"'
>>
}}}
The macro writes an empty string for every tiddler tagged "basic", i.e. it writes nothing.
Just at the end it writes the number of found tiddlers (using the ''end'' feature of the ForEachTiddler macro) or "No Tiddler found" if no tiddler is tagged with "basic" (using the ''none'' parameter) .
''//Result://''
<<forEachTiddler
where
'tiddler.tags.contains("plugin")'
write
'""'
end 'count+" Tiddlers found\n"'
none '"No Tiddlers found\n"'
>>
The following macro call exports all tiddlers to a text file "c:/~MyTiddlyWikiExport.txt", using a customized format.
{{{
<<forEachTiddler
script 'function getSortedTagsText(tiddler) {var tags = tiddler.tags; if (!tags) return ""; tags.sort(); var result = ""; for (var i = 0; i < tags.length;i++) {result += tags[i]+ " ";} return result;} function writeTiddler(tiddler) {return "==== "+tiddler.title+"=========================\nTags: "+ getSortedTagsText(tiddler)+"\nModified: "+tiddler.modified.convertToYYYYMMDDHHMM()+"\nModifier: "+tiddler.modifier+"\n--------------------------------------------------\n"+tiddler.text+"\n--------------------------------------------------\n(End of "+tiddler.title+")\n\n\n\n"}'
write
'writeTiddler(tiddler)'
toFile 'file:///c:/MyTiddlyWikiExport.txt' withLineSeparator '\r\n'
>>
}}}
For better readablility here the script text in a nicer layout:
{{{
function getSortedTagsText(tiddler) {
var tags = tiddler.tags;
if (!tags)
return "";
tags.sort();
var result = "";
for (var i = 0; i < tags.length;i++) {
result += tags[i]+ " ";
}
return result;
}
function writeTiddler(tiddler) {
return "==== "+tiddler.title+"=========================\n"+
"Tags: "+ getSortedTagsText(tiddler)+"\n"+
"Modified: "+tiddler.modified.convertToYYYYMMDDHHMM()+"\n"+
"Modifier: "+tiddler.modifier+"\n"+
"--------------------------------------------------\n"+
tiddler.text+"\n"+
"--------------------------------------------------\n"
"(End of "+tiddler.title+")\n\n\n\n"
}
}}}
<<forEachTiddler
script 'function getSortedTagsText(tiddler) {var tags = tiddler.tags; if (!tags) return ""; tags.sort(); var result = ""; for (var i = 0; i < tags.length;i++) {result += tags[i]+ " ";} return result;} function writeTiddler(tiddler) {return "==== "+tiddler.title+"=========================\nTags: "+ getSortedTagsText(tiddler)+"\nModified: "+tiddler.modified.convertToYYYYMMDDHHMM()+"\nModifier: "+tiddler.modifier+"\n--------------------------------------------------\n"+tiddler.text+"\n--------------------------------------------------\n(End of "+tiddler.title+")\n\n\n\n"}'
write
'writeTiddler(tiddler)'
toFile 'file:///c:/MyTiddlyWikiExport.txt' withLineSeparator '\r\n'
>>
<<forEachTiddler
where
'tiddler.tags.contains("文章整理")'
sortBy
'tiddler.title.toUpperCase()'
write '" [["+tiddler.title+" ]] \"view ["+tiddler.title+"]\" [["+tiddler.title+"]] "'
begin '"<<tabs txtMyAutoTab "'
end '">"+">"'
none '"//No tiddler tagged with \"文章整理\"//"'
>>
//{{{
//============================================================================
// getCreateDate Function
//============================================================================
//
// Returns the "create date" as generated by the AutoTaggerPlugin
// (http://www.TiddlyTools.com/#AutoTaggerPlugin).
// The create date must be stored in the default format "YYYY.0MM.0DD".
//
// @return [may be null] the create date (as a String) or null if no create
// date is found.
//
version.extensions.getCreateDate = {major: 1, minor: 0, revision: 0,
date: new Date(2005,11,21),
provider: "http://tiddlywiki.abego-software.de"};
//
function getCreateDate(tiddler) {
if (!tiddler || !tiddler.tags) {
return null;
}
for(var i = 0; i < tiddler.tags.length; i++) {
var matches = tiddler.tags[i].match(/^[0-9]{4}\.[0-9]{2}\.[0-9]{2}$/);
if (matches && matches.length > 0) {
return matches[0];
}
}
return null;
}
//}}}
/***
|''Name:''|zh-HantTranslationPlugin|
|''Description:''|Translation of TiddlyWiki into Traditional Chinese|
|''Source:''|http://tiddlywiki-zh.googlecode.com/svn/trunk/|
|''Subversion:''|http://svn.tiddlywiki.org/Trunk/association/locales/core/zh-Hant/locale.zh-Hant.js|
|''Author:''|BramChen (bram.chen (at) gmail (dot) com)|
|''Version:''|2.2.6|
|''Date:''|Dec 01, 2007|
|''Comments:''|Please make comments at http://groups-beta.google.com/group/TiddlyWiki-zh/|
|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|''~CoreVersion:''|2.2.0|
***/
//{{{
// --
// -- Translateable strings
// --
// Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone
config.locale = 'zh-Hant'; // W3C language tag
if (config.options.txtUserName == 'YourName' || !config.options.txtUserName) // do not translate this line, but do translate the next line
merge(config.options,{txtUserName: "YourName"});
merge(config.tasks,{
save: {text: "儲存", tooltip: "儲存變更至此 TiddlyWiki", action: saveChanges},
sync: {text: "同步", tooltip: "將你的資料內容與外部伺服器與檔案同步", content: '<<sync>>'},
importTask: {text: "導入", tooltip: "自其他檔案或伺服器導入文章或套件", content: '<<importTiddlers>>'},
tweak: {text: "選項", tooltip: "改變此 TiddlyWiki 的顯示與行為的設定", content: '<<options>>'},
plugins: {text: "套件管理", tooltip: "管理已安裝的套件", content: '<<plugins>>'}
});
merge(config.optionsDesc,{
txtUserName: "編輯文章所使用之作者署名",
chkRegExpSearch: "啟用正規式搜尋",
chkCaseSensitiveSearch: "搜尋時,區分大小寫",
chkAnimate: "使用動畫顯示",
chkSaveBackups: "儲存變更前,保留備份檔案",
chkAutoSave: "自動儲存變更",
chkGenerateAnRssFeed: "儲存變更時,也儲存 RSS feed",
chkSaveEmptyTemplate: "儲存變更時,也儲存空白範本",
chkOpenInNewWindow: "於新視窗開啟連結",
chkToggleLinks: "點擊已開啟文章將其關閉",
chkHttpReadOnly: "非本機瀏覽文件時,隱藏編輯功能",
chkForceMinorUpdate: "修改文章時,不變更作者名稱與日期時間",
chkConfirmDelete: "刪除文章前須確認",
chkInsertTabs: "使用 tab 鍵插入定位字元,而非跳至下一個欄位",
txtBackupFolder: "存放備份檔案的資料夾",
txtMaxEditRows: "編輯模式中顯示列數",
txtFileSystemCharSet: "指定儲存文件所在之檔案系統之字集 (僅適用於 Firefox/Mozilla only)"});
// Messages
merge(config.messages,{
customConfigError: "套件載入發生錯誤,詳細請參考 PluginManager",
pluginError: "發生錯誤: %0",
pluginDisabled: "未執行,因標籤設為 'systemConfigDisable'",
pluginForced: "已執行,因標籤設為 'systemConfigForce'",
pluginVersionError: "未執行,套件需較新版本的 TiddlyWiki",
nothingSelected: "尚未作任何選擇,至少需選擇一項",
savedSnapshotError: "此 TiddlyWiki 未正確存檔,詳見 http://www.tiddlywiki.com/#DownloadSoftware",
subtitleUnknown: "(未知)",
undefinedTiddlerToolTip: "'%0' 尚無內容",
shadowedTiddlerToolTip: "'%0' 尚無內容, 但已定義隱藏的預設值",
tiddlerLinkTooltip: "%0 - %1, %2",
externalLinkTooltip: "外部連結至 %0",
noTags: "未設定標籤的文章",
notFileUrlError: "須先將此 TiddlyWiki 存至檔案,才可儲存變更",
cantSaveError: "無法儲存變更。可能的原因有:\n- 你的瀏覽器不支援此儲存功能(Firefox, Internet Explorer, Safari and Opera 經適當設定後可儲存變更)\n- 也可能是你的 TiddlyWiki 檔名包含不合法的字元所致。\n- 或是 TiddlyWiki 文件被改名或搬移。",
invalidFileError: " '%0' 非有效之 TiddlyWiki 文件",
backupSaved: "已儲存備份",
backupFailed: "無法儲存備份",
rssSaved: "RSS feed 已儲存",
rssFailed: "無法儲存 RSS feed ",
emptySaved: "已儲存範本",
emptyFailed: "無法儲存範本",
mainSaved: "主要的TiddlyWiki已儲存",
mainFailed: "無法儲存主要 TiddlyWiki,所作的改變未儲存",
macroError: "巨集 <<\%0>> 執行錯誤",
macroErrorDetails: "執行巨集 <<\%0>> 時,發生錯誤 :\n%1",
missingMacro: "無此巨集",
overwriteWarning: "'%0' 已存在,[確定]覆寫之",
unsavedChangesWarning: "注意! 尚未儲存變更\n\n[確定]存檔,或[取消]放棄存檔?",
confirmExit: "--------------------------------\n\nTiddlyWiki 以更改內容尚未儲存,繼續的話將遺失這些更動\n\n--------------------------------",
saveInstructions: "SaveChanges",
unsupportedTWFormat: "未支援此 TiddlyWiki 格式:'%0'",
tiddlerSaveError: "儲存文章 '%0' 時,發生錯誤。",
tiddlerLoadError: "載入文章 '%0' 時,發生錯誤。",
wrongSaveFormat: "無法使用格式 '%0' 儲存,請使用標准格式存放",
invalidFieldName: "無效的欄位名稱:%0",
fieldCannotBeChanged: "無法變更欄位:'%0'",
loadingMissingTiddler: "正從伺服器 '%1' 的:\n\n工作區 '%3' 中的 '%2' 擷取文章 '%0'"});
merge(config.messages.messageClose,{
text: "關閉",
tooltip: "關閉此訊息"});
config.messages.backstage = {
open: {text: "控制台", tooltip: "開啟控制台執行編寫工作"},
close: {text: "關閉", tooltip: "關閉控制台"},
prompt: "控制台:",
decal: {
edit: {text: "編輯", tooltip: "編輯 '%0'"}
}
};
config.messages.listView = {
tiddlerTooltip: "檢視全文",
previewUnavailable: "(無法預覽)"
};
config.messages.dates.months = ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"];
config.messages.dates.days = ["星期日", "星期一","星期二", "星期三", "星期四", "星期五", "星期六"];
// config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
config.messages.dates.shortMonths = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"];
// config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
config.messages.dates.shortDays = ["日", "一","二", "三", "四", "五", "六"];
// suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
config.messages.dates.daySuffixes = ["st","nd","rd","th","th","th","th","th","th","th",
"th","th","th","th","th","th","th","th","th","th",
"st","nd","rd","th","th","th","th","th","th","th",
"st"];
config.messages.dates.am = "上午";
config.messages.dates.pm = "下午";
merge(config.messages.tiddlerPopup,{
});
merge(config.views.wikified.tag,{
labelNoTags: "未設標籤",
labelTags: "標籤: ",
openTag: "開啟標籤 '%0'",
tooltip: "顯示標籤為 '%0' 的文章",
openAllText: "開啟以下所有文章",
openAllTooltip: "開啟以下所有文章",
popupNone: "僅此文標籤為 '%0'"});
merge(config.views.wikified,{
defaultText: "",
defaultModifier: "(未完成)",
shadowModifier: "(預設)",
dateFormat: "YYYY年0MM月0DD日",
createdPrompt: "建立於"});
merge(config.views.editor,{
tagPrompt: "設定標籤之間以空白區隔,[[標籤含空白時請使用雙中括弧]],或點選現有之標籤加入",
defaultText: ""});
merge(config.views.editor.tagChooser,{
text: "標籤",
tooltip: "點選現有之標籤加至本文章",
popupNone: "未設定標籤",
tagTooltip: "加入標籤 '%0'"});
merge(config.messages,{
sizeTemplates:
[
{unit: 1024*1024*1024, template: "%0\u00a0GB"},
{unit: 1024*1024, template: "%0\u00a0MB"},
{unit: 1024, template: "%0\u00a0KB"},
{unit: 1, template: "%0\u00a0B"}
]});
merge(config.macros.search,{
label: " 尋找",
prompt: "搜尋本 Wiki",
accessKey: "F",
successMsg: " %0 篇符合條件: %1",
failureMsg: " 無符合條件: %0"});
merge(config.macros.tagging,{
label: "引用標籤:",
labelNotTag: "無引用標籤",
tooltip: "列出標籤為 '%0' 的文章"});
merge(config.macros.timeline,{
dateFormat: "YYYY年0MM月0DD日"});
merge(config.macros.allTags,{
tooltip: "顯示文章- 標籤為'%0'",
noTags: "沒有標籤"});
config.macros.list.all.prompt = "依字母排序";
config.macros.list.missing.prompt = "被引用且內容空白的文章";
config.macros.list.orphans.prompt = "未被引用的文章";
config.macros.list.shadowed.prompt = "這些隱藏的文章已預設內容";
config.macros.list.touched.prompt = "自下載或新增後被修改過的文章";
merge(config.macros.closeAll,{
label: "全部關閉",
prompt: "關閉所有開啟中的 tiddler (編輯中除外)"});
merge(config.macros.permaview,{
label: "引用連結",
prompt: "可存取現有開啟之文章的連結位址"});
merge(config.macros.saveChanges,{
label: "儲存變更",
prompt: "儲存所有文章,產生新的版本",
accessKey: "S"});
merge(config.macros.newTiddler,{
label: "新增文章",
prompt: "新增 tiddler",
title: "新增文章",
accessKey: "N"});
merge(config.macros.newJournal,{
label: "新增日誌",
prompt: "新增 jounal",
accessKey: "J"});
merge(config.macros.options,{
wizardTitle: "增訂的進階選項",
step1Title: "增訂的選項儲存於瀏覽器的 cookies",
step1Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='false' name='chkUnknown'>顯示未知選項</input>",
unknownDescription: "//(未知)//",
listViewTemplate: {
columns: [
{name: 'Option', field: 'option', title: "選項", type: 'String'},
{name: 'Description', field: 'description', title: "說明", type: 'WikiText'},
{name: 'Name', field: 'name', title: "名稱", type: 'String'}
],
rowClasses: [
{className: 'lowlight', field: 'lowlight'}
]}
});
merge(config.macros.plugins,{
wizardTitle: "擴充套件管理",
step1Title: "- 已載入之套件",
step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
skippedText: "(此套件因剛加入,故尚未執行)",
noPluginText: "未安裝套件",
confirmDeleteText: "確認是否刪除此文章:\n\n%0",
removeLabel: "移除 systemConfig 標籤",
removePrompt: "移除 systemConfig 標籤",
deleteLabel: "刪除",
deletePrompt: "永遠刪除所選",
listViewTemplate : {
columns: [
{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "套件", type: 'Tiddler'},
{name: 'Size', field: 'size', tiddlerLink: 'size', title: "大小", type: 'Size'},
{name: 'Forced', field: 'forced', title: "強制執行", tag: 'systemConfigForce', type: 'TagCheckbox'},
{name: 'Disabled', field: 'disabled', title: "停用", tag: 'systemConfigDisable', type: 'TagCheckbox'},
{name: 'Executed', field: 'executed', title: "已載入", type: "Boolean", trueText: "是", falseText: "否"},
{name: 'Startup Time', field: 'startupTime', title: "載入時間", type: 'String'},
{name: 'Error', field: 'error', title: "載入狀態", type: 'Boolean', trueText: "錯誤", falseText: "正常"},
{name: 'Log', field: 'log', title: "紀錄", type: 'StringList'}
],
rowClasses: [
{className: 'error', field: 'error'},
{className: 'warning', field: 'warning'}
]}
});
merge(config.macros.toolbar,{
moreLabel: "其他",
morePrompt: "顯示更多工具命令"});
merge(config.macros.refreshDisplay,{
label: "刷新",
prompt: "刷新此 TiddlyWiki 顯示"
});
merge(config.macros.importTiddlers,{
readOnlyWarning: "TiddlyWiki 於唯讀模式下,不支援導入文章。請由本機(file://)開啟 TiddlyWiki 文件",
wizardTitle: "自其他檔案或伺服器導入文章",
step1Title: "步驟一:指定伺服器或來源文件",
step1Html: "指定伺服器類型:<select name='selTypes'><option value=''>選取...</option></select><br>請輸入網址或路徑:<input type='text' size=50 name='txtPath'><br>...或選擇來源文件:<input type='file' size=50 name='txtBrowse'><br><hr>...或選擇指定的饋入來源:<select name='selFeeds'><option value=''>選取...</option></select>",
openLabel: "開啟",
openPrompt: "開啟檔案或",
openError: "讀取來源文件時發生錯誤",
statusOpenHost: "正與伺服器建立連線",
statusGetWorkspaceList: "正在取得可用之文章清單",
step2Title: "步驟二:選擇工作區",
step2Html: "輸入工作區名稱:<input type='text' size=50 name='txtWorkspace'><br>...或選擇工作區:<select name='selWorkspace'><option value=''>選取...</option></select>",
cancelLabel: "取消",
cancelPrompt: "取消本次導入動作",
statusOpenWorkspace: "正在開啟工作區",
statusGetTiddlerList: "正在取得可用之文章清單",
step3Title: "步驟三:選擇欲導入之文章",
step3Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='true' name='chkSync'>保持這些文章與伺服器的連結,便於同步後續的變更。</input><br><input type='checkbox' name='chkSave'>儲存此伺服器的詳細資訊於標籤為 'systemServer' 的文章名為:</input> <input type='text' size=25 name='txtSaveTiddler'>",
importLabel: "導入",
importPrompt: "導入所選文章",
confirmOverwriteText: "確定要覆寫這些文章:\n\n%0",
step4Title: "步驟四:正在導入%0 篇文章",
step4Html: "<input type='hidden' name='markReport'></input>", // DO NOT TRANSLATE
doneLabel: "完成",
donePrompt: "關閉",
statusDoingImport: "正在導入文章 ...",
statusDoneImport: "所選文章已導入",
systemServerNamePattern: "%2 位於 %1",
systemServerNamePatternNoWorkspace: "%1",
confirmOverwriteSaveTiddler: "此 tiddler '%0' 已經存在。點擊「確定」以伺服器上料覆寫之,或「取消」不變更後離開",
serverSaveTemplate: "|''Type:''|%0|\n|''網址:''|%1|\n|''工作區:''|%2|\n\n此文為自動產生紀錄伺服器之相關資訊。",
serverSaveModifier: "(系統)",
listViewTemplate: {
columns: [
{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "文章", type: 'Tiddler'},
{name: 'Size', field: 'size', tiddlerLink: 'size', title: "大小", type: 'Size'},
{name: 'Tags', field: 'tags', title: "標籤", type: 'Tags'}
],
rowClasses: [
]}
});
merge(config.macros.sync,{
listViewTemplate: {
columns: [
{name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "文章", type: 'Tiddler'},
{name: 'Server Type', field: 'serverType', title: "伺服器類型", type: 'String'},
{name: 'Server Host', field: 'serverHost', title: "伺服器主機", type: 'String'},
{name: 'Server Workspace', field: 'serverWorkspace', title: "伺服器工作區", type: 'String'},
{name: 'Status', field: 'status', title: "同步情形", type: 'String'},
{name: 'Server URL', field: 'serverUrl', title: "伺服器網址", text: "View", type: 'Link'}
],
rowClasses: [
],
buttons: [
{caption: "同步更新這些文章", name: 'sync'}
]},
wizardTitle: "將你的資料內容與外部伺服器與檔案同步",
step1Title: "選擇欲同步的文章",
step1Html: '<input type="hidden" name="markList"></input>', // DO NOT TRANSLATE
syncLabel: "同步",
syncPrompt: "同步更新這些文章",
hasChanged: "已更動",
hasNotChanged: "未更動",
syncStatusList: {
none: {text: "...", color: 'transparent'},
changedServer: {text: "伺服器資料已更動", color: '#80ff80'},
changedLocally: {text: "本機資料已更動", color: '#80ff80'},
changedBoth: {text: "已同時更新本機與伺服器上的資料", color: '#ff8080'},
notFound: {text: "伺服器無此資料", color: '#ffff80'},
putToServer: {text: "已儲存更新資料至伺服器", color: '#ff80ff'},
gotFromServer: {text: "已從伺服器擷取更新資料", color: '#80ffff'}
}
});
merge(config.macros.annotations,{
});
merge(config.commands.closeTiddler,{
text: "關閉",
tooltip: "關閉本文"});
merge(config.commands.closeOthers,{
text: "關閉其他",
tooltip: "關閉其他文章"});
merge(config.commands.editTiddler,{
text: "編輯",
tooltip: "編輯本文",
readOnlyText: "檢視",
readOnlyTooltip: "檢視本文之原始內容"});
merge(config.commands.saveTiddler,{
text: "完成",
tooltip: "確定修改"});
merge(config.commands.cancelTiddler,{
text: "取消",
tooltip: "取消修改",
warning: "確定取消對 '%0' 的修改嗎?",
readOnlyText: "完成",
readOnlyTooltip: "返回正常顯示模式"});
merge(config.commands.deleteTiddler,{
text: "刪除",
tooltip: "刪除文章",
warning: "確定刪除 '%0'?"});
merge(config.commands.permalink,{
text: "引用連結",
tooltip: "本文引用連結"});
merge(config.commands.references,{
text: "引用",
tooltip: "引用本文的文章",
popupNone: "本文未被引用"});
merge(config.commands.jump,{
text: "捲頁",
tooltip: "捲頁至其他已開啟的文章"});
merge(config.commands.syncing,{
text: "同步",
tooltip: "本文章與伺服器或其他外部檔案的同步資訊",
currentlySyncing: "<div>同步類型:<span class='popupHighlight'>'%0'</span></"+"div><div>與伺服器:<span class='popupHighlight'>%1 同步</span></"+"div><div>工作區:<span class='popupHighlight'>%2</span></"+"div>", // Note escaping of closing <div> tag
notCurrentlySyncing: "無進行中的同步動作",
captionUnSync: "停止同步此文章",
chooseServer: "與其他伺服器同步此文章:",
currServerMarker: "\u25cf ",
notCurrServerMarker: " "});
merge(config.commands.fields,{
text: "欄位",
tooltip: "顯示此文章的擴充資訊",
emptyText: "此文章沒有擴充欄位",
listViewTemplate: {
columns: [
{name: 'Field', field: 'field', title: "擴充欄位", type: 'String'},
{name: 'Value', field: 'value', title: "內容", type: 'String'}
],
rowClasses: [
],
buttons: [
]}});
merge(config.shadowTiddlers,{
DefaultTiddlers: "GettingStarted",
GettingStarted: "使用此 TiddlyWiki 的空白範本之前,請先修改以下預設文章:\n* SiteTitle 及 SiteSubtitle:網站的標題和副標題,顯示於頁面上方<br />(在儲存變更後,將顯示於瀏覽器視窗的標題列)。\n* MainMenu:主選單(通常在頁面左側)。\n* DefaultTiddlers:內含一些文章的標題,可於載入TiddlyWiki 後的預設開啟。\n請輸入您的大名,作為所建立/ 編輯的文章署名:<<option txtUserName>>",
MainMenu: "[[使用說明|GettingStarted]]\n\n\n^^~TiddlyWiki 版本:<<version>>\n© 2007 [[UnaMesa|http://www.unamesa.org/]]^^",
OptionsPanel: "這些設定將暫存於瀏覽器\n請簽名<<option txtUserName>>\n (範例:WikiWord)\n\n <<option chkSaveBackups>> 儲存備份\n <<option chkAutoSave>> 自動儲存\n <<option chkRegExpSearch>> 正規式搜尋\n <<option chkCaseSensitiveSearch>> 區分大小寫搜尋\n <<option chkAnimate>> 使用動畫顯示\n----\n [[進階選項|AdvancedOptions]]",
SiteTitle: "我的 TiddlyWiki",
SiteSubtitle: "一個可重複使用的個人網頁式筆記本",
SiteUrl: 'http://www.tiddlywiki.com/',
SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal " YYYY年0MM月0DD日" "日誌">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "偏好設定 »" "變更 TiddlyWiki 選項">>',
SideBarTabs: '<<tabs txtMainTab "最近更新" "依更新日期排序" TabTimeline "全部" "所有文章" TabAll "分類" "所有標籤" TabTags "更多" "其他" TabMore>>',
StyleSheet: '[[StyleSheetLocale]]',
TabMore: '<<tabs txtMoreTab "未完成" "內容空白的文章" TabMoreMissing "未引用" "未被引用的文章" TabMoreOrphans "預設文章" "已預設內容的隱藏文章" TabMoreShadowed>>'});
merge(config.annotations,{
AdvancedOptions: "此預設文章可以存取一些進階選項。",
ColorPalette: "此預設文章裡的設定值,將決定 ~TiddlyWiki 使用者介面的配色。",
DefaultTiddlers: "當 ~TiddlyWiki 在瀏覽器中開啟時,此預設文章裡列出的文章,將被自動顯示。",
EditTemplate: "此預設文章裡的 HTML template 將決定文章進入編輯模式時的顯示版面。",
GettingStarted: "此預設文章提供基本的使用說明。",
ImportTiddlers: "此預設文章提供存取導入中的文章。",
MainMenu: "此預設文章的內容,為於螢幕左側主選單的內容",
MarkupPreHead: "此文章的內容將加至 TiddlyWiki 文件的 <head> 段落的起始",
MarkupPostHead: "此文章的內容將加至 TiddlyWiki 文件的 <head> 段落的最後",
MarkupPreBody: "此文章的內容將加至 TiddlyWiki 文件的 <body> 段落的起始",
MarkupPostBody: "此文章的內容將加至 TiddlyWiki 文件的 <body> 段落的最後,於 script 區塊之前",
OptionsPanel: "此預設文章的內容,為於螢幕右側副選單中的選項面板裡的內容",
PageTemplate: "此預設文章裡的 HTML template 決定的 ~TiddlyWiki 主要的版面配置",
PluginManager: "此預設文章提供存取套件管理員",
SideBarOptions: "此預設文章的內容,為於螢幕右側副選單中選項面板裡的內容",
SideBarTabs: "此預設文章的內容,為於螢幕右側副選單中的頁籤面板裡的內容",
SiteSubtitle: "此預設文章的內容為頁面的副標題",
SiteTitle: "此預設文章的內容為頁面的主標題",
SiteUrl: "此預設文章的內容須設定為文件發佈時的完整網址",
StyleSheetColors: "此預設文章內含的 CSS 規則,為相關的頁面元素的配色。''勿修改此文'',請於 StyleSheet 中作增修。",
StyleSheet: "此預設文章內容可包含 CSS 規則",
StyleSheetLayout: "此預設文章內含的 CSS 規則,為相關的頁面元素的版面配置。''勿修改此文'',請於 StyleSheet 中作增修。",
StyleSheetLocale: "此預設文章內含的 CSS 規則,可依翻譯語系做適當調整",
StyleSheetPrint: "此預設文章內含的 CSS 規則,用於列印時的樣式",
TabAll: "此預設文章的內容,為於螢幕右側副選單中的「全部」頁籤的內容",
TabMore: "此預設文章的內容,為於螢幕右側副選單中的「更多」頁籤的內容",
TabMoreMissing: "此預設文章的內容,為於螢幕右側副選單中的「未完成」頁籤的內容",
TabMoreOrphans: "此預設文章的內容,為於螢幕右側副選單中的「未引用」頁籤的內容",
TabMoreShadowed: "此預設文章的內容,為於螢幕右側副選單中的「預設文章」頁籤的內容",
TabTags: "此預設文章的內容,為於螢幕右側副選單中的「分類」頁籤的內容",
TabTimeline: "此預設文章的內容,為於螢幕右側副選單中的「最近更新」頁籤的內容",
ViewTemplate: "此預設文章裡的 HTML template 決定文章顯示的樣子"
});
//}}}
/***
|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
|''Version:''|1.0.9 (2007-07-14)|
|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Table of Content<html><a name="TOC"/></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
!Description<html><a name="Description"/></html>
With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts.
Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts (e.g. {{{[[Quotes/BAX95]]}}} or {{{[[Hobbies|AboutMe/Hobbies]]}}}), use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
''Syntax:''
|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//. <<br>>If you use a partName containing spaces you need to quote it (e.g. {{{"Major Overview"}}} or {{{[[Shortcut List]]}}}).|
|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
|<html><i>any tiddler content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Applications<html><a name="Applications"/></html>
!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Citation Index<html><a name="Citation"/></html>
Create a tiddler "Citations" that contains your "citations".
Wrap every citation with a part and a proper name.
''Example''
{{{
<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.//
in //Proc. ICSM//, 1998.</part>
<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.//
Thesis, Uni Stuttgart, 2002.</part>
<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.//
in //Proc. ICSM//, 1999.</part>
}}}
You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
{{{
* Item 1
* Item 2
* Item 3
}}}
into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
''Example''
{{{
|!Subject|!Items|
|subject1|<<tiddler ./Cell1>>|
|subject2|<<tiddler ./Cell2>>|
<part Cell1 hidden>
* Item 1
* Item 2
* Item 3
</part>
...
}}}
Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
BTW: The same approach can be used to create bullet lists with items that contain more than one line.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Creating Tabs<html><a name="Tabs"/></html>
The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
''Example''
The standard tabs at the sidebar are defined by the following eight tiddlers:
* SideBarTabs
* TabAll
* TabMore
* TabMoreMissing
* TabMoreOrphans
* TabMoreShadowed
* TabTags
* TabTimeline
Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
{{{
<<tabs txtMainTab
Timeline Timeline SideBarTabs/Timeline
All 'All tiddlers' SideBarTabs/All
Tags 'All tags' SideBarTabs/Tags
More 'More lists' SideBarTabs/More>>
<part Timeline hidden><<timeline>></part>
<part All hidden><<list all>></part>
<part Tags hidden><<allTags>></part>
<part More hidden><<tabs txtMoreTab
Missing 'Missing tiddlers' SideBarTabs/Missing
Orphans 'Orphaned tiddlers' SideBarTabs/Orphans
Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
<part Missing hidden><<list missing>></part>
<part Orphans hidden><<list orphans>></part>
<part Shadowed hidden><<list shadowed>></part>
}}}
Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
{{{
<<forEachTiddler
sortBy 'tiddler.modified' descending
write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
}}}
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Using Sliders<html><a name="Sliders"/></html>
Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
''Example''
In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
{{{
...
<<slider chkAboutDetails About/Details details "Click here to see more details">>
<part Details hidden>
To give you a better overview ...
</part>
...
}}}
Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Revision history<html><a name="Revisions"/></html>
* v1.0.9 (2007-07-14)
** Bugfix: Error when using the SideBarTabs example and switching between "More" and "Shadow". Thanks to cmari for reporting the issue.
* v1.0.8 (2007-06-16)
** Speeding up display of tiddlers containing multiple pard definitions. Thanks to Paco Rivière for reporting the issue.
** Support "./partName" syntax inside <<tabs ...>> macro
* v1.0.7 (2007-03-07)
** Bugfix: <<tiddler "./partName">> does not always render correctly after a refresh (e.g. like it happens when using the "Include" plugin). Thanks to Morris Gray for reporting the bug.
* v1.0.6 (2006-11-07)
** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
* v1.0.5 (2006-03-02)
** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
* v1.0.4 (2006-02-28)
** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
* v1.0.3 (2006-02-26)
** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
* v1.0.2 (2006-02-05)
** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
* v1.0.1 (2006-01-27)
** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
* v1.0.0 (2006-01-25)
** initial version
/***
|Name|ShowUpdatesPlugin|
|Created by|SaqImtiaz|
|Version|0.2 |
|Requires|~TW2.x|
!!!Description:
Allows you to list tiddlers that have changed since the users last visit. You can list only all changed tiddlers, or filter them to only show tiddlers that have or do not have a specific tag. By default a simple list of the titles of changed tiddlers is created. However, using an extremely versatile syntax you can provide a custom template for the generated text.
!!!Installation:
Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
!!!Syntax:
{{{<<showUpdates>>}}}
additional optional params:
{{{<showUpdates excludeTag:TagToExclude onlyTag:TagToList maxEntries:10 write:CustomWriteParameter >>}}}
excludeTag: ~TagToExclude
onlyTag: ~TagToList
maxEntries: max number of entries displayed when there are no updates. (default is 10, which can be changed in the config.macros.showUpdates.settings part of the code)
write: if a write parameter is not provided, an un-numbered list of the updates is generated. Alternatively, you can specify a custom 'template' for the text generated. The syntax for the write parameter is identical to that of the forEachTiddler macro. Additonal documentation on this syntax will be provided soon.
Some of the variables available in the write parameter are 'index', 'count' and 'lastVisit' where lastVisit is the date of the last visit in the format YYYYMMDDHHMM. Also areUpdates is a boolean that is true if there are new updates since the users last visit.
!!!To Do:
*refactor code to facilitate translations
*a streamlined version without the custom write parameter
!!!Code
***/
//{{{
window.lewcidLastVisit = '';
window.old_lewcid_whatsnew_restart = window.restart;
window.restart = function()
{
if(config.options.txtLastVisit)
lewcidLastVisit= config.options.txtLastVisit;
config.options.txtLastVisit = (new Date()).convertToYYYYMMDDHHMM();
saveOptionCookie('txtLastVisit');
window.old_lewcid_whatsnew_restart();
}
TiddlyWiki.prototype.lewcidGetTiddlers = function(field,excludeTag,includeTag,updatesOnly)
{
var results = [];
this.forEachTiddler(function(title,tiddler)
{
if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
if(includeTag == undefined || tiddler.isTagged(includeTag))
if ( updatesOnly == false || tiddler.modified.convertToYYYYMMDDHHMM()>lewcidLastVisit)
results.push(tiddler);
});
if(field)
results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
return results;
}
config.macros.showUpdates={};
config.macros.showUpdates.settings =
{
maxEntries: 10 //max items to show, if there are no updates since last visit
}
config.macros.showUpdates.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var args = paramString.parseParams("list",null,true);
var write = getParam(args, "write", undefined);
var onlyTag = getParam(args, "onlyTag", undefined);
var excludeTag = getParam(args, "excludeTag", undefined);
var sortBy = "modified";
var maxEntries = getParam(args,"maxEntries",this.settings.maxEntries);
if (lewcidLastVisit)
{var tiddlers = store.lewcidGetTiddlers(sortBy,excludeTag,onlyTag,true);
var areUpdates = tiddlers.length>0? true:false;}
//if (!lewcidLastVisit)
// {var countLine = "!!最近的更新:";
var tiddlers = store.lewcidGetTiddlers(sortBy,excludeTag,onlyTag,false);
var areUpdates = false;
// }
//else if (tiddlers.length == 0)
// {var countLine = "!!@@color:red;上次拜訪後, 沒有任何更新 " + (Date.convertFromYYYYMMDDHHMM(lewcidLastVisit)).formatString(" (DD/MM/YY)") + "@@\n!!最近的更新:";
// var tiddlers = store.lewcidGetTiddlers(sortBy,excludeTag,onlyTag,false);}
// else
// {var countLine ="!!@@ 自從上次拜訪後, 共有 "+ tiddlers.length +" 更新 " + (Date.convertFromYYYYMMDDHHMM(lewcidLastVisit)).formatString(" (DD/MM/YY)") + "@@";}
tiddlers = tiddlers.reverse();
var lastVisit = lewcidLastVisit? lewcidLastVisit:undefined;
var count = areUpdates == true? tiddlers.length : maxEntries;
var sp = createTiddlyElement(place,"span","showUpdates");
if (write==undefined)
{
//wikify(countLine,sp);
var list = createTiddlyElement(sp,"ul");
for (var i = 0; i < count; i++)
{
var tiddler = tiddlers[i];
createTiddlyLink(createTiddlyElement(list,"li"), tiddler.title, true);
}
}
else
{
var list = '';
for (var index = 0; index < count; index++) {
var tiddler = tiddlers[index];
list += eval(write); }
wikify(list, sp);
}
}
//}}}
/***
|''巨集名稱:''|toBalaAjax|
|''版本:''|1.0 (2008-03-06)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''|toBalaHTML, toBalaFile|
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0|
|''使用語法''| |
***/
//{{{
config.macros.toBalaAjax = {};
config.macros.toBalaAjax.handler = function(place,macroName,params,wikifier,paramString,tiddler){
if (!params[0]) {
alert("請給檔名");
return;
}
// 檔名
var xfname = params[0];
var slabel = params[1] || "儲存網頁";
var olabel = params[2] || "檢視網頁";
var xwidth = params[3] || 550;
var xheight = params[4] || 400;
createTiddlyButton(place, olabel, olabel,
function(e) { config.macros.toBalaHTML.show(e,"",xfname,xwidth,xheight); },
null, null, null);
if (readOnly) {
return;
}
createTiddlyButton(place, slabel, slabel,
function(e) { config.macros.toBalaSaveFile.show(e, tiddler.title,xfname); },
null, null, null);
}
//}}}
!使用說明
{{{
<<toBalaAjax "ajax\html\testAjax.htm" "儲存網頁 : ajax\html\testAjax.htm">>
}}}
!實作範例
<<toBalaAjax "ajax\html\testAjax.htm" "儲存網頁 : ajax\html\testAjax.htm">>
/*{{{*/
<html>
<head>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf8">
</head>
<body>
<h2 align=center>顯示網頁載入時間的警告視窗</h2>
<hr>
<script>
today = new Date(); // 產生日期物件
hour = today.getHours(); // 取得時數
minute = today.getMinutes(); // 取得分數
second = today.getSeconds(); // 取得秒數
string = "網頁載入時間是"+hour+"點"+minute+"分"+second+"秒"; // 連接字串
</script>
<a href="javascript:alert(string)">網頁載入時間</a>
<hr>
</body>
</html>
/*}}}*/
<<toBalaAjax "ajax\AjaxCall.htm" "儲存網頁 : ajax\AjaxCall.htm" "" "700" "450">>
/*{{{*/
<!--
程式名稱 : AjaxCall.html
程式描述 : Call XMLHTTP Object
適用瀏覽器 : Firefox 2.0,IE 5.5
參考文件 :
-->
<html>
<head>
<meta http-equiv="Expires" content="0">
<meta http-equiv="Content-Type" content="text/html; charset=utf8">
<script>
var xmlhttp
function show(){
if (window.XMLHttpRequest) {
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
xmlhttp = new XMLHttpRequest();
}
else if (window.ActiveXObject){
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e){
try{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e){}
}
}
xmlhttp.open(document.getElementById("c").value,document.getElementById("u").value,false);
xmlhttp.send("");
if (parseInt(xmlhttp.status)>300){
alert(xmlhttp.status);
}
else {
document.getElementById("h").innerHTML=xmlhttp.getAllResponseHeaders();
if (xmlhttp.getResponseHeader("Content-Type") == "text/xml"){
xmlobj=xmlhttp.responseXML;
document.getElementById("x").innerHTML=xmlobj.getElementsByTagName('app')[0].firstChild.nodeValue;
}
else{
document.getElementById("x").innerHTML="";
var testFrame = document.getElementById("myFrame");
var doc = testFrame.contentDocument;
if (doc == undefined || doc == null)
doc = testFrame.contentWindow.document;
doc.open();
doc.write(xmlhttp.responseText);
doc.close();
}
}
}
</script>
</head>
<style type="text/css">
body {
background-color:white;
}
</style>
<body>
<h2>Ajax 測試程式 (使用本機的瀏覽器以 "開啟檔案" 方式執行)</h2>
<hr>
Method: <input id="c" value="GET" style="width: 100px;" type="text"><span> (要大寫) </span><br>
URL: <input style="width: 250px;" id="u" value="http://" type="text"><br>
<button onclick="show();">傳送命令</button>
<hr>
<b>Response Header</b>
<pre id="h"></pre>
<b>Response XML</b>
<pre id="x"></pre>
<b>Response HTML</b><br>
<iframe id="myFrame" height="30%" width="100%"></iframe>
</body>
</html>
<!--
HTTP Methods
-------------
HTTP defines eight methods (sometimes referred to as "verbs") indicating the desired
action to be performed on the identified resource.
HEAD
Asks for the response identical to the one that would correspond to a GET request,
but without the response body. This is useful for retrieving meta-information
written in response headers, without having to transport the entire content.
GET
Requests a representation of the specified resource. By far the most common method
used on the Web today. Should not be used for operations that cause side-effects
(using it for actions in web applications is a common mis-use). See 'safe methods'
below.
POST
Submits data to be processed (e.g. from an HTML form) to the identified resource.
The data is included in the body of the request. This may result in the creation
of a new resource or the updates of existing resources or both.
PUT
Uploads a representation of the specified resource.
DELETE
Deletes the specified resource.
TRACE
Echoes back the received request, so that a client can see what intermediate
servers are adding or changing in the request.
OPTIONS
Returns the HTTP methods that the server supports. This can be used to check the
functionality of a web server.
CONNECT
For use with a proxy that can change to being an SSL tunnel.
WebDAV added the following methods to HTTP:
* PROPFIND — Used to retrieve properties, persisted as XML, from a resource.
It is also overloaded to allow one to retrieve the collection
structure (a.k.a. directory hierarchy) of a remote system.
* PROPPATCH — Used to change and delete multiple properties on a resource
in a single atomic act.
* MKCOL — Used to create collections (a.k.a. directory).
* COPY — Used to copy a resource from one URI to another.
* MOVE — Used to move a resource from one URI to another.
* LOCK — Used to put a lock on a resource. WebDAV supports both shared and
exclusive locks.
* UNLOCK — To remove a lock from a resource.
-->
/*}}}*/
config.options.chkAutoSave=false;
config.options.chkSaveBackups=false;
config.options.txtUserName="比目魚";
config.options.txtMaxEditRows=25;
config.options.chkHttpReadOnly=true;
config.options.chkHideTabsBarWhenSingleTab=true;
config.options.chkDisableWikiLinks=true;
config.options.chkAllowLinksFromShadowTiddlers=true;
config.options.chkDisableNonExistingWikiLinks=true;
/***
|''巨集名稱:''|toBalaFile|
|''版本:''|1.0 (2008-03-18)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''||
***/
//{{{
config.macros.toBalaFile={
editLabel: "編輯",
editTip: "編輯",
saveLabel: "儲存",
saveTip: "儲存",
cancelLabel: "取消",
cancelTip: "取消",
GUIEditButtonOnclick: function(e,xtiddler,xbox,xfname,xx,yy) {
if (config.browser.isIE) {
window.event.cancelBubble=true;
} else {
e.cancelBubble = true;
if (e.stopPropagation)
e.stopPropagation();
}
removeChildren(xbox);
var boxtool = createTiddlyElement(xbox,"div",null,"toBalaFileToolBar",null,null);
var boxtext = createTiddlyElement(xbox,"textarea",null,null,null,null);
wikify("@@color:#841;border:2px dotted #841;padding:2px;檔案 : <nowiki>"+xfname+"</nowiki>@@ ",boxtool);
createTiddlyButton(boxtool,this.saveLabel,this.saveTip,
function(e) { config.macros.toBalaFile.GUISaveButtonOnclick(e,xtiddler,xbox,xfname,boxtext,xx,yy); },
"toBalaFileSave",null,null);
createTiddlyButton(boxtool,this.cancelLabel,this.cancelTip,
function(e) { config.macros.toBalaFile.GUICancelButtonOnclick(e,xtiddler,xbox,xfname,xx,yy); },
"toBalaFileCancel",null,null);
// alert(xtitle+":"+xx+":"+yy);
boxtext.setAttribute("rows",20);
boxtext.setAttribute("cols",88);
boxtext.value = xtiddler.text.substring(xx,yy);
},
GUISaveButtonOnclick: function(e,xtiddler1,xbox1,xfname1,boxtext1,xx1,yy1) {
if (config.browser.isIE) {
window.event.cancelBubble=true;
} else {
e.cancelBubble = true;
if (e.stopPropagation)
e.stopPropagation();
}
removeChildren(xbox1);
var xtext=boxtext1.value;
if (config.browser.isIE) {
xtext=xtext.replace(/\r/g,""); // 請看最後的註解
}
var toptext=xtiddler1.text.substring(0,xx1);
var bottomtext=xtiddler1.text.substring(yy1);
xtiddler1.text=toptext+xtext+"\n"+bottomtext;
story.refreshTiddler(xtiddler1.title,null,true);
saveChanges(false); // 參數 false, 代表不管 Dirty 與否, 均要執行存檔動作
var baseDIR = getLocalPath(document.URL);
if (config.browser.isWindows) {
var baseDIR = baseDIR.replace(/\//g,"");
var x = baseDIR.lastIndexOf("\\");
} else {
var x = baseDIR.lastIndexOf("/");
}
baseDIR = baseDIR.substr(0,x+1);
var s = saveFile(baseDIR+ (config.browser.isWindows ? xfname1 : xfname1.replace(/\\/g,"\/")),
convertUnicodeToUTF8(boxtext1.value));
if (!s) {
alert("存檔失敗");
}
},
GUICancelButtonOnclick: function(e,xtiddler1,xbox1,xfname1,xx1,yy1) {
if (config.browser.isIE) {
window.event.cancelBubble=true;
} else {
e.cancelBubble = true;
if (e.stopPropagation)
e.stopPropagation();
}
removeChildren(xbox1);
var boxtool = createTiddlyElement(xbox1,"div",null,"toBalaFileToolBar",null,null);
wikify("@@color:#841;border:2px dotted #841;padding:2px;檔案 : <nowiki>"+xfname1+"</nowiki>@@ ",boxtool);
createTiddlyButton(boxtool,this.editLabel,this.editTip,
function(e) { config.macros.toBalaFile.GUIEditButtonOnclick(e,xtiddler1,xbox1,xfname1,xx1,yy1);},
"toBalaFileEdit",null,null);
var boxarea = createTiddlyElement(xbox1,"div",null,null,null,null);
wikify("{{{\n"+xtiddler1.text.substring(xx1,yy1)+"\n}}}",boxarea);
},
handler : function(place,macroName,params,wikifier,paramString,tiddler){
if (!params[0]) {
alert("請給檔名");
return;
}
var xtext=store.getTiddlerText(tiddler.title);
var x1=xtext.indexOf("/"+"/"+"/"+"%" + params[0]);
// 自動產生 TiddlyWiki 備註
if (x1 == -1) {
xtext=xtext+"\n\n/"+"/"+"/"+"%" + params[0] + "\n" + "/"+"/"+"%"+"/";
tiddler.text=xtext;
story.refreshTiddler(tiddler.title,null,true);
saveChanges(false);
return;
}
var x2=xtext.indexOf("/"+"/"+"%"+"/",x1);
x1=x1+params[0].length+5;
var box = createTiddlyElement(place,"div",tiddler.title+"-"+x1,"toBalaFile",null,null);
var boxtool = createTiddlyElement(box,"div",null,"toBalaFileToolBar",null,null);
wikify("@@color:#841;border:2px dotted #841;padding:2px;檔案 : <nowiki>"+params[0]+"</nowiki>@@ ",boxtool);
var boxtext = createTiddlyElement(box,"div",null,null,null,null);
wikify("{{{\n"+xtext.substring(x1,x2)+"\n}}}",boxtext);
if (readOnly) {
return;
}
createTiddlyButton(boxtool,this.editLabel,this.editTip,
function(e) { config.macros.toBalaFile.GUIEditButtonOnclick(e,tiddler,box,params[0],x1,x2);},
"toBalaFileEdit",null,null);
}
};
/*
HTML 的 textarea 標籤, 對於換行在不同的瀏覽器有不同的處理方式
As you may know, the humble line break actually has three forms depending on which operating system is doing the breaking. On Unix machines, a single newline character ‘\n’ does the job. On Macs, a carriage return ‘\r’ is used. DOS and Windows use both: ‘\r\n’. It’s one of those relatively subtle issues that can bite you hard if you don’t know what to look out for.
*/
//}}}
!說明
{{{
<<toBalaFile "檔名">>
}}}
!範例
<<toBalaFile "xml\abc.xml">>
///%xml\abc.xml
<?xml version='1.0'?>
<root>
<marble color="red"/>
<marble color='red'/>
</root>
//%/
/***
|''巨集名稱:''|toBalaFlashPlayer|
|''版本:''|1.0 (2008-03-06)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''| |
***/
//{{{
config.macros.toBalaFlashPlayer = {};
config.macros.toBalaFlashPlayer.handler = function(place,macroName,params,wikifier,paramString,tiddler){
var xname = params[0] || tiddler.title;
var label = params[1] || "撥放 " + xname;
var xwidth = params[2] || 600;
var xheight = params[3] || 450;
createTiddlyButton(place, label, label,
function(e) { config.macros.toBalaFlashPlayer.show(e, xname, xwidth, xheight); },
null, null, null);
}
config.macros.toBalaFlashPlayer.show = function(ev,fname,xw,xh) {
if (config.browser.isIE) {
window.event.cancelBubble=true;
} else {
ev.cancelBubble = true;
if (ev.stopPropagation)
ev.stopPropagation();
}
if (config.browser.isIE)
var URLtext = document.URL.replace(/\\/g,"\/");
else
var URLtext = document.URL;
//alert(URLtext);
var x = URLtext.lastIndexOf("/");
var baseDIR = URLtext.substr(0,x);
var jsDIR = '<script type="text/javascript" src="' + baseDIR + '/jslib/swfobject/swfobject.js"></script>'
//alert(jsDIR);
var generator=window.open('','name','height=' + xh +',width=' + xw);
generator.document.open();
generator.document.write('<html><head><title>toBala Flash Player</title>');
generator.document.write(jsDIR);
generator.document.write('</head><body>');
generator.document.write('<div id="flashTag" style="width:'+(xw-5)+'px;height:'+(xh-5)+'px;overflow:auto"></div>');
generator.document.write('<script type="text/javascript">');
generator.document.write('var xo = new SWFObject("' + baseDIR + '/' + fname + '","mymovie","'+(xw-5)+'","'+(xh-5)+'","8", "#336699");');
//generator.document.write('xo.addParam("wmode", "transparent");');
generator.document.write('xo.write("flashTag");');
generator.document.write('</script>');
generator.document.write('</body></html>');
generator.document.close();
}
//}}}
使用 toBalaFlashPlayer 巨集, 來撥放指定的 Flash 檔, 命令格式如下 :
{{{
<<toBalaFlashPlayer "movie/infotree.swf" "" "820" "610">>
}}}
!實作
<<toBalaFlashPlayer "movie/infotree.swf" "" "820" "610">>
/***
|''巨集名稱:''|toBalaHTML|
|''版本:''|1.0 (2008-03-18)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''|<<toBalaHTML title filename label tooltip width height>> |
***/
//{{{
config.macros.toBalaHTML = {};
config.macros.toBalaHTML.handler = function(place,macroName,params,wikifier,paramString,tiddler){
var title = params[0] || tiddler.title;
var xfname = params[1] || ""; // 外部檔名
var label = params[2] || "測試網頁";
var tooltip = params[3] || "開啟另一個視窗, 檢視網頁";
var xwidth = params[4] || 550;
var xheight = params[5] || 400;
createTiddlyButton(place, label, tooltip,
function(e) { config.macros.toBalaHTML.show(e,title,xfname,xwidth,xheight); },
null, null, null);
}
config.macros.toBalaHTML.show = function(ev,xtitle,xf,xw,xh) {
if (config.browser.isIE) {
window.event.cancelBubble=true;
} else {
ev.cancelBubble=true;
if (ev.stopPropagation)
ev.stopPropagation();
}
// 將視窗置中
var winl = (screen.width-xw)/2;
var wint = (screen.height-xh)/2;
if (winl < 0) winl = 0;
if (wint < 0) wint = 0;
var settings = 'height=' + xh + ', width=' + xw + ',top=' + wint + ',left=' + winl;
if (xf != "") {
xf=xf.replace(/\\/mg,"/");
// alert(xf+":"+settings);
var xwin=window.open(xf,"HTMLWIN",settings);
xwin.window.focus();
} else {
// alert(store.getTiddlerText(xtitle));
xtext=store.getTiddlerText(xtitle);
x1=xtext.indexOf("/*{{{*/");
x2=xtext.indexOf("/*}}}*/");
// alert(xtext.substring(x1+8,x2));
xtext = xtext.substring(x1+8,x2);
var xwin=window.open('','',settings);
xwin.document.open();
xwin.document.write(xtext);
xwin.document.close();
}
}
/*
window.open() 參數
----------------------------------------------
* width:定義新視窗的寬度。
* height:定義新視窗的高度。
* resizable:能否讓用戶調整視窗大小,可能的設定值為:yes或no(或者1與0)。
* menubar:是否要顯示主功能表,可能的設定值為:yes或no。
* toolbar:是否要顯示「標準按鈕」工具列,可能的設定值為:yes或no。
* location:是否要顯示網址欄位,可能的設定值為:yes或no。
* scrollbars:能否顯示捲軸,可能的設定值為:yes或no。
* status:是否要呈現狀態列,可能的設定值為:yes或no。
* directories:是否要呈現額外的按鈕(例如:「連結」列,以及「標準按鈕」以外的其他按鈕),可能的設定值為:yes或no。
* copyhistory:是否要複製原有瀏覽器視窗的瀏覽歷程(history),可能的設定值為:yes或no。
* fullscreen:是否要以全螢幕方式開啟新視窗(適用於IE瀏覽器),可能的設定值為:yes或no。
*/
//}}}
<<toBalaHTML "" "ajax/html/e4x.htm" "網頁測試 : ajax/html/e4x.htm">><<toBalaFile "ajax\html\e4x.htm">>
///%ajax\html\e4x.htm
<html>
<script type="text/javascript;e4x=1">
myquestion = <question>
<display>Is it animal, vegetable, or mineral?</display>
<answerOption>Animal</answerOption>
<answerOption>Vegetable</answerOption>
<answerOption>Mineral</answerOption>
</question>;
alert("The question is '" + myquestion.display + "'");
</script>
</html>
//%/
這範例程式可在 IE, Firefox, Opera 等瀏覽器中執行
<<toBalaHTML "" "ajax\html\contentEditableFF.htm">><<toBalaFile "ajax\html\contentEditableFF.htm">>
///%ajax\html\contentEditableFF.htm
<body>
<iFrame id="jia" src="about:blank"></iFrame>
<script>
// window.onload = function(){
var jia = document.getElementById("jia");
jia.contentWindow.document.designMode="on";
jia.contentWindow.document.contentEditable=true;
jia.contentWindow.document.open();
jia.contentWindow.document.write("<html><head></head><body></body></html>");
jia.contentWindow.document.close();
// }
</script>
</body>
//%/
必須關閉瀏覽器 [阻檔 POPUP 視窗] 功能, 以下程式才能執行
<<toBalaHTML "" "ajax\html\popup.htm">><<toBalaFile "ajax\html\popup.htm">>
///%ajax\html\popup.htm
<html>
<head>
<script type="text/javascript">
function show_popup()
{
var p=window.createPopup();
var pbody=p.document.body;
pbody.style.backgroundColor="lime";
pbody.style.border="solid black 1px";
pbody.innerHTML="This is a pop-up! Click outside to close.";
p.show(150,150,200,50,document.body);
}
</script>
</head>
<body>
<button onclick="show_popup()">Create pop-up!</button>
</body>
</html>
//%/
/***
|''巨集名稱:''|toBalaJava|
|''版本:''|1.0 (2008-04-01)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''|toBalaRun|
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''|<<toBalaJava "程式檔名" "前置編譯參數" "前置執行參數" "後置執行參數" "按鈕提示資訊" "按鈕提示資訊">> |
***/
//{{{
config.macros.toBalaJava = {};
config.macros.toBalaJava.handler = function(place,macroName,params,wikifier,paramString,tiddler){
if (readOnly) {
return;
}
if (!params[0].match(".java")) {
alert("請給檔名或副檔名錯誤");
return;
}
// 程式檔名
var xfname = params[0];
// 前置編譯參數
var cprearg = params[1] || "";
// 前置執行參數
var eprearg = params[2] || "";
// 後置執行參數
var postarg = params[3] || "";
var cargs = (cprearg=="" ? "":cprearg+" ") + xfname;
//alert(cargs);
var p = xfname.indexOf(".java");
yfname=xfname.substring(0,p);
p=yfname.lastIndexOf("\\");
yfname=yfname.substring(p+1,yfname.length);
var rargs = (eprearg==""?"":eprearg+" ") + yfname + (postarg==""?"":" "+postarg);
//alert(rargs);
// 按鈕提示資訊
var clabel = params[4] || "翻譯程式 :" + xfname;
var rlabel = params[5] || "執行程式 :" + yfname;
// 翻譯程式
createTiddlyButton(place, clabel, clabel,
function(e) { config.macros.toBalaRun.cmd(e,"tbjavac.exe",cargs,"yes"); },
null, null, null);
// 執行程式
createTiddlyButton(place, rlabel, rlabel,
function(e) { config.macros.toBalaRun.cmd(e,"tbjava.exe",rargs,"yes"); },
null, null, null);
}
//}}}
!程式範例
{{{
使用 toBalaFile 巨集, 編輯及儲存 Java 程式, 命令格式如下 :
<<toBalaFile "java\hello.java">>
}}}
<<toBalaFile "java\hello.java">>
----
{{{
使用 toBalaJava 巨集, 編譯及執行 Java 程式, 命令格式如下 :
<<toBalaJava "java\hello.java" "" "">>
}}}
<<toBalaJava "java\hello.java" "" "">>
!程式範例 : 輸入 [編譯] 及 [執行] 參數
<<toBalaFile "java\assertion.java">>
----
{{{
使用 toBalaJava 巨集, 編譯及執行 Java 程式, 命令格式如下 :
<<toBalaJava "java\assertion.java" "-source 1.4" "-ea">>
"-source 1.4" 是編譯參數
"-ea" 是執行參數
}}}
<<toBalaJava "java\assertion.java" "-source 1.4" "-ea">>
///%java\hello.java
public class hello {
public static void main(String[] args){
System.out.println("大家好好");
}
}
//%/
///%java\assertion.java
class assertion {
public static void main(String[] args){
int i=-11;
if (i%3==0)
{ System.out.println("0"); }
else if (i%3==1)
{ System.out.println("1"); }
else {
assert((i%3)==2):i;
}
}
}
//%/
{{{
if (!window.toBala) window.toBala = {};
toBala.showMsg = function(msg,xw,xh) {
var winl = (screen.width-xw)/2;
var wint = (screen.height-xh)/2;
if (winl < 0) winl = 0;
if (wint < 0) wint = 0;
var settings = 'height=' + xh + ', width=' + xw + ',top=' + wint + ',left=' + winl;
var xwin=window.open("","",settings);
xwin.document.open();
xwin.document.write(msg);
xwin.document.close();
return xwin;
};
}}}
/***
|''巨集名稱:''|toBalaManager|
|''版本:''|1.0 (2008-05-05)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''~TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''||
***/
//{{{
config.macros.toBalaManager={
preText: "",
save: function(e,ytiddler,yform) {
//for (var i=0,str="";i<yform.elements.length;++i){
// str=str+yform.elements[i].type+":"+yform.elements[i].tagName+":"+yform.elements[i].value;
//}
//alert(str);
// 設定主標題
var xt = store.getTiddler("SiteTitle");
xt.text= yform.elements[0].value;
// 設定次標題
var xt = store.getTiddler("SiteSubtitle");
xt.text= yform.elements[1].value;
// 設定 MainMenu
var xt = store.getTiddler("MainMenu");
xt.text= yform.elements[2].value;
// 設定 ToolBar
var xt = store.getTiddler("ToolBar");
xt.text= yform.elements[3].value;
// 備份 SiteTitle, SiteSubtitle, ToolBar, MainMenu
ytiddler.text=preText;
saveChanges(false); // 參數 false, 代表不管 Dirty 與否, 均要執行存檔動作
},
handler : function(place,macroName,params,wikifier,paramString,tiddler){
if (readOnly) {
return;
}
createTiddlyButton(place,"儲存","儲存",
function(e) { config.macros.toBalaManager.save(e,tiddler,xform); },
"toBalaManagerSave",null,null);
var xform = createTiddlyElement(place,"form",tiddler.title+"-form","toBalaManager",null,null);
// 讀取主標題
var xlabel1 = createTiddlyElement(xform,"label",null,"",null,null);
wikify("''主標題 (~SiteTitle) : ''",xlabel1);
var xtitle1 = createTiddlyElement(xform,"input",null,"xManagerMT",null,null);
var xt = store.getTiddler("SiteTitle");
xtitle1.value=xt.text;
wikify("\n\n",xform);
// 讀取次標題
var xlabel2 = createTiddlyElement(xform,"label",null,"",null,null);
wikify("''次標題 (~SiteSubtitle) : ''",xlabel2);
var xtitle2 = createTiddlyElement(xform,"input",null,"xManagerST",null,null);
var xt = store.getTiddler("SiteSubtitle");
xtitle2.value=xt.text;
wikify("\n\n",xform);
// 讀取 MainMenu
var xlabel4 = createTiddlyElement(xform,"label",null,"",null,null);
wikify("''資訊樹 (TagsTree)''",xlabel4);
wikify("\n",xform);
var xmm = createTiddlyElement(xform,"textarea",null,"xManagerMM",null,null);
var xt = store.getTiddler("MainMenu");
xmm.value=xt.text;
wikify("\n\n",xform);
// 讀取 ToolBar
var xlabel3 = createTiddlyElement(xform,"label",null,"",null,null);
wikify("''工具列 (~ToolBar)''",xlabel3);
wikify("\n",xform);
var xtb = createTiddlyElement(xform,"textarea",null,"xManagerTB",null,null);
var xt = store.getTiddler("ToolBar");
xtb.value=xt.text;
wikify("\n\n",xform);
// 備份 SiteTitle, SiteSubtitle, ToolBar, MainMenu
var xtext=store.getTiddlerText(tiddler.title);
var x1 = xtext.indexOf("/"+"/"+"/"+"%");
xtext = ( x1 == -1 ? xtext : xtext.substring(0,x1) );
preText=xtext+"\n\n/"+"/"+"/"+"%" ;
preText=preText+"\n\n"+xtitle1.value;
preText=preText+"\n\n"+xtitle2.value;
preText=preText+"\n\n"+xmm.value;
preText=preText+"\n\n"+xtb.value;
preText=preText+"\n" + "/"+"/"+"%"+"/";
}
};
setStylesheet(".toBalaManager {background:#eee; font-size:12px;border:1px solid #ccc; padding:2px; margin:5px;}\n"+
".xManagerMT {width:550px;height:25px;}\n" +
".xManagerST {width:550px;height:25px;}\n" +
".xManagerTB {width:680px;height:150px;}\n" +
".xManagerMM {width:680px;height:250px;}\n" +
".toBalaManagerSave {border:1px solid #ccc;font-size:14px;padding:2px;margin-left:4px;margin-bottom:2px;}\n" ,
"toBalaManagerStyles");
//}}}
/***
|''巨集名稱:''|toBalaNotes|
|''版本:''|1.0 (2008-03-18)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''||
***/
//{{{
config.macros.toBalaNotes={
editLabel: "編輯",
editTip: "編輯",
saveLabel: "儲存",
saveTip: "儲存",
cancelLabel: "取消",
cancelTip: "取消",
heading: "@@font-size:12px;color:#841;註解@@",
GUIEditButtonOnclick: function(e,xtiddler,xbox,xx,yy) {
removeChildren(xbox);
var boxtool = createTiddlyElement(xbox,"div",null,"toBalaNotesToolBar",null,null);
var boxtext = createTiddlyElement(xbox,"textarea",null,null,null,null);
wikify(this.heading+" : ",boxtool);
createTiddlyButton(boxtool,this.saveLabel,this.saveTip,
function(e) { config.macros.toBalaNotes.GUISaveButtonOnclick(e,xtiddler,xbox,boxtext,xx,yy); },
"toBalaNotesSave",null,null);
createTiddlyButton(boxtool,this.cancelLabel,this.cancelTip,
function(e) { config.macros.toBalaNotes.GUICancelButtonOnclick(e,xtiddler,xbox,xx,yy); },
"toBalaNotesCancel",null,null);
// alert(xtitle+":"+xx+":"+yy);
boxtext.setAttribute("rows",15);
boxtext.setAttribute("cols",88);
boxtext.value = xtiddler.text.substring(xx,yy);
},
GUISaveButtonOnclick: function(e,xtiddler1,xbox1,boxtext1,xx1,yy1) {
removeChildren(xbox1);
toptext=xtiddler1.text.substring(0,xx1);
bottomtext=xtiddler1.text.substring(yy1);
xtiddler1.text=toptext+boxtext1.value+bottomtext;
story.refreshTiddler(xtiddler1.title,null,true);
saveChanges(false); // 參數 false, 代表不管 Dirty 與否, 均要執行存檔動作
},
GUICancelButtonOnclick: function(e,xtiddler1,xbox1,xx1,yy1) {
removeChildren(xbox1);
var boxtool = createTiddlyElement(xbox1,"div",null,"toBalaNotesToolBar",null,null);
wikify(this.heading+" : ",boxtool);
createTiddlyButton(boxtool,this.editLabel,this.editTip,
function(e) { config.macros.toBalaNotes.GUIEditButtonOnclick(e,xtiddler1,xbox1,xx1,yy1);},
"toBalaNotesEdit",null,null);
var boxarea = createTiddlyElement(xbox1,"div",null,null,null,null);
wikify(xtiddler1.text.substring(xx1,yy1),boxarea);
},
handler : function(place,macroName,params,wikifier,paramString,tiddler){
var xtext=store.getTiddlerText(tiddler.title);
var x1=xtext.indexOf("/"+"/"+"/"+"%" + params[0]);
// 自動產生 TiddlyWiki 備註
if (x1 == -1) {
xtext=xtext+"\n\n/"+"/"+"/"+"%" + params[0] + "\n" + "/"+"/"+"%"+"/";
tiddler.text=xtext;
story.refreshTiddler(tiddler.title,null,true);
saveChanges(false); // 參數 false, 代表不管 Dirty 與否, 均要執行存檔動作
clearMessage();
return;
}
var x2=xtext.indexOf("/"+"/"+"%"+"/",x1);
x1=x1+params[0].length+5;
var box = createTiddlyElement(place,"div",tiddler.title+"-"+x1,"toBalaNotes",null,null);
var boxtool = createTiddlyElement(box,"div",null,"toBalaNotesToolBar",null,null);
wikify(this.heading+" : ",boxtool);
var boxtext = createTiddlyElement(box,"div",null,null,null,null);
wikify(xtext.substring(x1,x2),boxtext);
if (readOnly) {
return;
}
createTiddlyButton(boxtool,this.editLabel,this.editTip,
function(e) { config.macros.toBalaNotes.GUIEditButtonOnclick(e,tiddler,box,x1,x2);},
"toBalaNotesEdit",null,null);
}
};
setStylesheet(".toBalaNotes {background:#eee; border:1px solid #ccc; padding:2px; margin:5px;}\n"+
".toBalaNotesEdit {border:1px solid #ccc;padding:2px;margin-top:2px;margin-bottom:2px;}\n" +
".toBalaNotesCancel {border:1px solid #ccc;padding:2px;margin-top:2px;margin-right:2px;margin-bottom:2px;}\n" +
".toBalaNotesToolBar {border-bottom: 2px dotted #556b2f;padding:1px 0px 5px 0px;margin:1px;}\n" +
".toBalaNotesSave {border:1px solid #ccc;padding:2px;margin-top:2px;margin-right:2px;margin-bottom:2px;}\n",
"toBalaNotesStyles");
//}}}
/***
|''巨集名稱:''|toBalaRun|
|''版本:''|1.0 (2008-04-01)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''| |
***/
//{{{
config.macros.toBalaRun = {};
config.macros.toBalaRun.handler = function(place,macroName,params,wikifier,paramString,tiddler){
if (readOnly) {
return;
}
if (!params[0]) {
alert("請給檔名");
return;
}
var fname = params[0]; // 執行檔名
var varg = params[1] || ""; // 執行參數
var label = params[2] || "執行 : "+fname + " " + varg;
var tooltip = params[3] || "執行檔名 : "+fname + " " + varg;
var result = params[4] || "yes";
window.status="";
createTiddlyButton(place, label, tooltip,
function(e) { config.macros.toBalaRun.cmd(e,fname,varg,result); },
null, null, null);
}
config.macros.toBalaRun.cmd = function(ev,xfname,xarg,xresult) {
if (!xfname.match(".bat")) {
//var xwin = toBala.showMsg("<h1>執行中, 請稍後</h1>",250,50);
window.status = xfname + " " + xarg + " 執行中, 請稍後";
}
if (config.browser.isIE) {
window.event.cancelBubble=true;
} else {
ev.cancelBubble = true;
if (ev.stopPropagation)
ev.stopPropagation();
}
var baseDIR = getLocalPath(document.URL);
//alert(baseDIR);
if (config.browser.isWindows) {
var baseDIR = baseDIR.replace(/\//g,"");
var x = baseDIR.lastIndexOf("\\");
xfname = "\\" + xfname.replace(/\//g,"\\");
xarg = xarg.replace(/\//g,"\\");
} else {
var x = baseDIR.lastIndexOf("/");
xfname = "/" + xfname.replace(/\\/g,"/");
xarg = xarg.replace(/\\/g,"/");
}
baseDIR = baseDIR.substr(0,x);
//alert(baseDIR);
if (config.browser.isIE) {
var theShell = new ActiveXObject("WScript.Shell");
if (theShell) {
var runstr = baseDIR + xfname + " " + xarg;
// alert(runstr);
try {
theShell.run(runstr,1, (xresult == "yes" ? true:false));
} catch (e) {
try { // 執行系統命令
var runstr = xfname.substring(1)+ " " + xarg;
theShell.run(runstr,1, (xresult == "yes" ? true:false));
} catch(e) {
if (!xfname.match(".bat"))
window.status="";
displayMessage("執行失敗 :" + runstr);
return;
}
}
}
} else if (config.browser.isGecko) {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
try {
var runstr = baseDIR +xfname;
// alert(runstr);
file.initWithPath(runstr);
}catch (e) {
if (!xfname.match(".bat"))
window.status="";
displayMessage("執行字串格式錯誤 : " + runstr);
return;
}
try {
var process = Components.classes['@mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
var Aargs = xarg.split(" ");
// alert(Aargs);
process.init(file);
process.run((xresult == "yes" ? true:false), Aargs, Aargs.length);
} catch (e) {
try { // 執行系統命令
runstr=xfname.substring(1);
file.initWithPath(runstr);
process.init(file);
process.run((xresult == "yes" ? true:false), Aargs, Aargs.length);
} catch (e) {
if (!xfname.match(".bat"))
window.status="";
displayMessage("執行失敗 : " + runstr);
return;
}
}
}
// 顯示執行結果
if ( !xfname.match(".bat") && !xfname.match(".sh") && xresult == "yes") {
window.status="";
xf = baseDIR+xfname+".htm"
// alert(xf);
var xh = 400;
var xw = 780;
var winl = (screen.width-xw)/2;
var wint = (screen.height-xh)/2;
if (winl < 0) winl = 0;
if (wint < 0) wint = 0;
var settings = 'height=' + xh + ', width=' + xw + ',top=' + wint + ',left=' + winl + ',scrollbars=yes';
var ywin=window.open("file:///"+xf,"HTMLWIN",settings);
}
}
//}}}
<<toBalaFile "xml\dtd01.xml">><<toBalaRun "java\jaxp\JAXPDTD.bat" "xml\dtd01.xml" "檢核">>
----
<<toBalaFile "xml\dtd02.xml">><<toBalaRun "java\jaxp\JAXPDTD.bat" "xml\dtd02.xml" "檢核">>
///%xml\dtd01.xml
<?xml version="1.0" encoding="big5"?>
<!DOCTYPE PersonData [
<!ELEMENT PersonData (name,spouse)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT spouse (#PCDATA)>
]>
<PersonData>
<spouse>Teley</spouse>
<name>Ryan</name>
</PersonData>
//%/
///%xml\dtd02.xml
<?xml version="1.0"?>
<!DOCTYPE user [
<!ELEMENT user (name+,e-mail*,title?)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT e-mail (#PCDATA)>
<!ELEMENT title (#PCDATA)>
]>
<user>
<name>Austin Sours</name>
<e-mail>xchen@cmt.com.tw</e-mail>
<e-mail>aaa@x,y,z</e-mail>
</user>
//%/
<<toBalaFile "xml\well01.xml">><<toBalaRun "java\jaxp\JAXPWell.bat" "xml\well01.xml" "檢核">>
----
<<toBalaFile "xml\well02.xml">><<toBalaRun "java\jaxp\JAXPWell.bat" "xml\well02.xml" "檢核">>
///%xml\well01.xml
<root>
<marble color="red"/>
<marble color='red'/>
</root>
//%/
///%xml\well02.xml
<first_tag>
<second_tag>
<third_tag>Contents of third tag
</second_tag>
</First_tag>
//%/
<<toBalaFile "xml\stype.xsd">>
<<toBalaFile "xml\stype.xml">><<toBalaRun "java\jaxp\JAXPSchema.bat" "xml\stype.xml xml\stype.xsd" "檢核">>
///%xml\stype.xsd
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://xsdtesting">
<xs:element name="author" type="xs:date"/>
</xs:schema>
//%/
///%xml\stype.xml
<?xml version="1.0"?>
<x:author xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:x="http://xsdtesting"
xsi:schemaLocation="http://xsdtesting stype.xsd">
123.5
</x:author>
//%/
<<toBalaFile "xml\deftemp.xml">>
DOM Tree
----------------
{{{
.
|
|--- <?xml-stylesheet type="text/xsl" .... ?>
|--- <root>
|
|--- <text>
|
|
|--- <text>
|
|
|--- <!-- abcd -->
}}}
<<toBalaFile "xml\deftemp.xsl">><<toBalaRun "java\jaxp\JAXPXSLT.bat" "xml\deftemp.xml xml\deftemp.xsl" "轉換">>
///%xml\deftemp.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="deftemp.xsl"?>
<root>
<text>
Roses are <color>red</color>, violets are <color>blue</color>.
</text>
<text>
My <color>green</color> and <color id="xxx">yellow</color>
sweater is just the right hue.
<?name type="bebo"?>
</text>
<!-- abcd -->
</root>
//%/
///%xml\deftemp.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- <xsl:template match="*|/">
</xsl:template> -->
<!-- <xsl:template match="processing-instruction()">
PI : <xsl:value-of select="."/>
</xsl:template> -->
<!-- <xsl:template match="root"/> -->
</xsl:stylesheet>
//%/
<<toBalaRun "tbzoomit.bat" "" "ZoomIt v1.8" "" "no">>
!~ZoomIt v1.8
By Mark Russinovich
Published: March 10, 2008
網址 : http://technet.microsoft.com/zh-tw/sysinternals/bb897434(en-us).aspx
''Introduction''
~ZoomIt is screen zoom and annotation tool for technical presentations that include application demonstrations. ~ZoomIt runs unobtrusively in the tray and activates with customizable hotkeys to zoom in on an area of the screen, move around while zoomed, and draw on the zoomed image. I wrote ~ZoomIt to fit my specific needs and use it in all my presentations.
~ZoomIt works on all versions of Windows and you can use pen input for ~ZoomIt drawing on tablet ~PCs.
''Using ~ZoomIt''
The first time you run ~ZoomIt it presents a configuration dialog that describes ~ZoomIt's behavior, let's you specify alternate hotkeys for zooming and for entering drawing mode without zooming, and customize the drawing pen color and size. I use the draw-without-zoom option to annotate the screen at its native resolution, for example. ~ZoomIt also includes a break timer feature that remains active even when you tab away from the timer window and allows you to return to the timer window by clicking on the ~ZoomIt tray icon.
!tbzoomit.bat 程式碼
{{{
@echo off
REM 取得這個 Batch File 的目錄
set toBalaPath=%~p0
REM 取得這個 Batch File 的磁碟代號
set toBalaDrv=%~d0
REM 轉換磁碟及目錄
call %toBalaDrv%
cd %toBalaPath%
REM 設定環境變數
REM -----------------
set PATH=.;%cd%\cmdtools;%PATH%
start /B zoomit.exe
exit
}}}
/***
|''巨集名稱:''|toBalaWrite|
|''版本:''|1.0 (2008-04-01)|
|''原始檔:''||
|''作者:''|S L CHEN|
|''授權:''|BSD|
|''倚賴''||
|''TiddlyWiki 適用版本''|2.3+|
|''適用瀏覽器''|Firefox 1.5+, IE 6.0+|
|''使用語法''| |
***/
//{{{
config.macros.toBalaWrite = {};
config.macros.toBalaWrite.handler = function(place, macroName, params, wikifier, paramString, tiddler) {
var title = params[0];
if(title == tiddler.title) {
title = "Error: Can't use the same tiddler from which the macro is called (infinite recursion)!";
alert(title);
return;
}
var tiddler = store.getTiddler(title);
var contents = tiddler.text;
wikify(contents, place);
}
//}}}
/***
|''Name:''|DropDownMenuPlugin|
|''Description:''|Create dropdown menus from unordered lists|
|''Author:''|Saq Imtiaz ( lewcid@gmail.com )|
|''Source:''|http://tw.lewcid.org/#DropDownMenuPlugin|
|''Code Repository:''|http://tw.lewcid.org/svn/plugins|
|''Version:''|2.1|
|''Date:''|11/04/2007|
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''~CoreVersion:''|2.2.5|
!!Usage:
* create a two-level unordered list using wiki syntax, and place {{{<<dropMenu>>}}} on the line after it.
* to create a vertical menu use {{{<<dropMenu vertical>>}}} instead.
* to assign custom classes to the list, just pass them as parameters to the macro {{{<<dropMenu className1 className2 className3>>}}}
!!Features:
*Supports just a single level of drop-downs, as anything more usually provides a poor experience for the user.
* Very light weight, about 1.5kb of JavaScript and 4kb of CSS.
* Comes with two built in css 'themes', the default horizontal and vertical.
!!Customizing:
* to customize the appearance of the menu's, you can either add a custom class as described above or, you can edit the CSS via the StyleSheetDropDownMenu shadow tiddler.
***/
// /%
//!BEGIN-PLUGIN-CODE
config.macros.dropMenu={
dropdownchar: "\u25bc",
handler : function(place,macroName,params,wikifier,paramString,tiddler){
list = findRelated(place.lastChild,"UL","tagName","previousSibling");
if (!list)
return;
addClass(list,"suckerfish");
if (params.length){
addClass(list,paramString);
}
this.fixLinks(list);
},
fixLinks : function(el){
var els = el.getElementsByTagName("li");
for(var i = 0; i < els.length; i++) {
if(els[i].getElementsByTagName("ul").length>0){
var link = findRelated(els[i].firstChild,"A","tagName","nextSibling");
if(!link){
var ih = els[i].firstChild.data;
els[i].removeChild(els[i].firstChild);
var d = createTiddlyElement(null,"a",null,null,ih+this.dropdownchar,{href:"javascript:;"});
els[i].insertBefore(d,els[i].firstChild);
}
else{
link.firstChild.data = link.firstChild.data + this.dropdownchar;
removeClass(link,"tiddlyLinkNonExisting");
}
}
els[i].onmouseover = function() {
addClass(this, "sfhover");
};
els[i].onmouseout = function() {
removeClass(this, "sfhover");
};
}
}
};
config.shadowTiddlers["StyleSheetDropDownMenuPlugin"] =
"/*{{{*/\n"+
"/***** LAYOUT STYLES - DO NOT EDIT! *****/\n"+
"ul.suckerfish, ul.suckerfish ul {\n"+
" margin: 0;\n"+
" padding: 0;\n"+
" list-style: none;\n"+
" line-height:1.4em;\n"+
"}\n\n"+
"ul.suckerfish li {\n"+
" display: inline-block; \n"+
" display: block;\n"+
" float: left; \n"+
"}\n\n"+
"ul.suckerfish li ul {\n"+
" position: absolute;\n"+
" left: -999em;\n"+
"}\n\n"+
"ul.suckerfish li:hover ul, ul.suckerfish li.sfhover ul {\n"+
" left: auto;\n"+
"}\n\n"+
"ul.suckerfish ul li {\n"+
" float: none;\n"+
" border-right: 0;\n"+
" border-left:0;\n"+
"}\n\n"+
"ul.suckerfish a, ul.suckerfish a:hover {\n"+
" display: block;\n"+
"}\n\n"+
"ul.suckerfish li a.tiddlyLink, ul.suckerfish li a, #mainMenu ul.suckerfish li a {font-weight:bold;}\n"+
"/**** END LAYOUT STYLES *****/\n"+
"\n\n"+
"/**** COLORS AND APPEARANCE - DEFAULT *****/\n"+
"ul.suckerfish li a {\n"+
" padding: 0.5em 1.5em;\n"+
" color: #FFF;\n"+
" background: #0066aa;\n"+
" border-bottom: 0;\n"+
" font-weight:bold;\n"+
"}\n\n"+
"ul.suckerfish li:hover a, ul.suckerfish li.sfhover a{\n"+
" background: #00558F;\n"+
"}\n\n"+
"ul.suckerfish li:hover ul a, ul.suckerfish li.sfhover ul a{\n"+
" color: #000;\n"+
" background: #eff3fa;\n"+
" border-top:1px solid #FFF;\n"+
"}\n\n"+
"ul.suckerfish ul li a:hover {\n"+
" background: #e0e8f5;\n"+
"}\n\n"+
"ul.suckerfish li a{\n"+
" width:9em;\n"+
"}\n\n"+
"ul.suckerfish ul li a, ul.suckerfish ul li a:hover{\n"+
" display:inline-block;\n"+
" width:9em;\n"+
"}\n\n"+
"ul.suckerfish li {\n"+
" border-left: 1px solid #00558F;\n"+
"}\n"+
"/***** END COLORS AND APPEARANCE - DEFAULT *****/\n"+
"\n\n"+
"/***** LAYOUT AND APPEARANCE: VERTICAL *****/\n"+
"ul.suckerfish.vertical li{\n"+
" width:10em;\n"+
" border-left: 0px solid #00558f;\n"+
"}\n\n"+
"ul.suckerfish.vertical ul li, ul.suckerfish.vertical li a, ul.suckerfish.vertical li:hover a, ul.suckerfish.vertical li.sfhover a {\n"+
" border-left: 0.8em solid #00558f;\n"+
"}\n\n"+
"ul.suckerfish.vertical li a, ul.suckerfish.vertical li:hover a, ul.suckerfish.vertical li.sfhover a, ul.suckerfish.vertical li.sfhover a:hover{\n"+
" width:8em;\n"+
"}\n\n"+
"ul.suckerfish.vertical {\n"+
" width:10em; text-align:left;\n"+
" float:left;\n"+
"}\n\n"+
"ul.suckerfish.vertical li a {\n"+
" padding: 0.5em 1em 0.5em 1em;\n"+
" border-top:1px solid #fff;\n"+
"}\n\n"+
"ul.suckerfish.vertical, ul.suckerfish.vertical ul {\n"+
" line-height:1.4em;\n"+
"}\n\n"+
"ul.suckerfish.vertical li:hover ul, ul.suckerfish.vertical li.sfhover ul { \n"+
" margin: -2.4em 0 0 10.9em;\n"+
"}\n\n"+
"ul.suckerfish.vertical li:hover ul li a, ul.suckerfish.vertical li.sfhover ul li a {\n"+
" border: 0px solid #FFF;\n"+
"}\n\n"+
"ul.suckerfish.vertical li:hover a, ul.suckerfish.vertical li.sfhover a{\n"+
" padding-right:1.1em;\n"+
"}\n\n"+
"ul.suckerfish.vertical li:hover ul li, ul.suckerfish.vertical li.sfhover ul li {\n"+
" border-bottom:1px solid #fff;\n"+
"}\n\n"+
"/***** END LAYOUT AND APPEARANCE: VERTICAL *****/\n"+
"/*}}}*/";
store.addNotification("StyleSheetDropDownMenuPlugin",refreshStyles);
//!END-PLUGIN-CODE
// %/
<<importTiddlers inline>>
/***
|''Name:''|LaunchApplicationPlugin|
|''Author:''|Lyall Pearce|
|''Source:''|http://www.Remotely-Helpful.com/TiddlyWiki/LaunchApplication.html|
|''License:''|[[Creative Commons Attribution-Share Alike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''Version:''|1.4.0|
|''~CoreVersion:''|2.3.0|
|''Requires:''| |
|''Overrides:''| |
|''Description:''|Launch an application from within TiddlyWiki using a button|
!!!!!Usage
<<<
{{{<<LaunchApplication "buttonLabel" "tooltip" "application" ["arguments" ...]>>}}}
{{{<<LaunchApplicationButton "buttonLabel" "tooltip" "application" ["arguments" ...]>>}}}
{{{<<LaunchApplicationLink "buttonLabel" "tooltip" "application" ["arguments" ...]>>}}}
* buttonLabel is anything you like
* tooltip is anything you like
* application is a path to the executable (which is Operating System dependant)
* arguments is any command line arguments the application requires.
* You must supply relative path from the location of the TiddlyWiki OR a fully qualified path
* Forward slashes works fine for Windows
{{{<<LaunchApplication...>>}}} functions the same as {{{<<LaunchApplicationButton...>>}}}
eg.
{{{
<<LaunchApplicationButton "Emacs" "Linux Emacs" "file:///usr/bin/emacs">>
}}}
<<LaunchApplicationButton "Emacs" "Linux Emacs" "file:///usr/bin/emacs">>
{{{
<<LaunchApplicationLink "LocalProgram" "Program relative to Tiddly html file" "localDir/bin/emacs">>
}}}
<<LaunchApplicationLink "LocalProgram" "Program relative to Tiddly html file" "localDir/bin/emacs">>
{{{
<<LaunchApplicationButton "Open Notepad" "Text Editing" "file:///e:/Windows/notepad.exe">>
}}}
<<LaunchApplicationButton "Open Notepad" "Text Editing" "file:///e:/Windows/notepad.exe">>
{{{
<<LaunchApplicationLink "C Drive" "Folder" "file:///c:/">>
}}}
<<LaunchApplicationLink "C Drive" "Folder" "file:///c:/">>
!!!!!Revision History
* 1.1.0 - leveraged some tweaks from from Bradly Meck's version (http://bradleymeck.tiddlyspot.com/#LaunchApplicationPlugin) and the example text.
* 1.2.0 - Make launching work in Linux too and use displayMessage() to give diagnostics/status info.
* 1.3.0 - execute programs relative to TiddlyWiki html file plus fix to args for firefox.
* 1.3.1 - parameters to the macro are properly parsed, allowing dynamic paramters using {{{ {{javascript}} }}} notation.
* 1.4.0 - updated core version and fixed empty tooltip and added launch link capability
***/
*http://www.isaserver.org
*Microsoft ISA Server 2006 Unleashed (by Michael Noel)
*Dr. Tom Shinder's ISA Server 2006 Migration Guide (by Thomas W Shinder)
*GFI:出許多 ISA 更多進階的篩選器、報表產生器
*ISA 也有硬體防火牆
*通過聯合國組織 Common CriTer EAL 4+
enable
interface
ip route
hostname
banner
router
access-list
聯絡我: mailto:bearflatfish@gmail.com
<html><a href="../weblog/weblog.html" target="mainFrame"> 更多的 ''比目魚'' 請看 My Weblog </a></html>
{{item1{__802.1X DC 設定__}}} <<toBalaFlashPlayer "flash/5-1-802.1x-1-DC.swf" "有影片有真相" "800" "620">>
{{item1{__802.1X IAS 設定 - 1__}}} <<toBalaFlashPlayer "flash/5-1-802.1x-2-IAS.swf" "有影片有真相" "800" "620">>
{{item1{__802.1X IAS 設定 - 2__}}} <<toBalaFlashPlayer "flash/5-1-802.1x-2-IAS-b.swf" "有影片有真相" "800" "620">>
/***
|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
|''Version:''|1.0.9 (2007-07-14)|
|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Table of Content<html><a name="TOC"/></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
!Description<html><a name="Description"/></html>
With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts.
Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts (e.g. {{{[[Quotes/BAX95]]}}} or {{{[[Hobbies|AboutMe/Hobbies]]}}}), use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
''Syntax:''
|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//. <<br>>If you use a partName containing spaces you need to quote it (e.g. {{{"Major Overview"}}} or {{{[[Shortcut List]]}}}).|
|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
|<html><i>any tiddler content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Applications<html><a name="Applications"/></html>
!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Citation Index<html><a name="Citation"/></html>
Create a tiddler "Citations" that contains your "citations".
Wrap every citation with a part and a proper name.
''Example''
{{{
<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.//
in //Proc. ICSM//, 1998.</part>
<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.//
Thesis, Uni Stuttgart, 2002.</part>
<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.//
in //Proc. ICSM//, 1999.</part>
}}}
You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
{{{
* Item 1
* Item 2
* Item 3
}}}
into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
''Example''
{{{
|!Subject|!Items|
|subject1|<<tiddler ./Cell1>>|
|subject2|<<tiddler ./Cell2>>|
<part Cell1 hidden>
* Item 1
* Item 2
* Item 3
</part>
...
}}}
Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
BTW: The same approach can be used to create bullet lists with items that contain more than one line.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Creating Tabs<html><a name="Tabs"/></html>
The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
''Example''
The standard tabs at the sidebar are defined by the following eight tiddlers:
* SideBarTabs
* TabAll
* TabMore
* TabMoreMissing
* TabMoreOrphans
* TabMoreShadowed
* TabTags
* TabTimeline
Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
{{{
<<tabs txtMainTab
Timeline Timeline SideBarTabs/Timeline
All 'All tiddlers' SideBarTabs/All
Tags 'All tags' SideBarTabs/Tags
More 'More lists' SideBarTabs/More>>
<part Timeline hidden><<timeline>></part>
<part All hidden><<list all>></part>
<part Tags hidden><<allTags>></part>
<part More hidden><<tabs txtMoreTab
Missing 'Missing tiddlers' SideBarTabs/Missing
Orphans 'Orphaned tiddlers' SideBarTabs/Orphans
Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
<part Missing hidden><<list missing>></part>
<part Orphans hidden><<list orphans>></part>
<part Shadowed hidden><<list shadowed>></part>
}}}
Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
{{{
<<forEachTiddler
sortBy 'tiddler.modified' descending
write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
}}}
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Using Sliders<html><a name="Sliders"/></html>
Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
''Example''
In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
{{{
...
<<slider chkAboutDetails About/Details details "Click here to see more details">>
<part Details hidden>
To give you a better overview ...
</part>
...
}}}
Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Revision history<html><a name="Revisions"/></html>
* v1.0.9 (2007-07-14)
** Bugfix: Error when using the SideBarTabs example and switching between "More" and "Shadow". Thanks to cmari for reporting the issue.
* v1.0.8 (2007-06-16)
** Speeding up display of tiddlers containing multiple pard definitions. Thanks to Paco Rivière for reporting the issue.
** Support "./partName" syntax inside <<tabs ...>> macro
* v1.0.7 (2007-03-07)
** Bugfix: <<tiddler "./partName">> does not always render correctly after a refresh (e.g. like it happens when using the "Include" plugin). Thanks to Morris Gray for reporting the bug.
* v1.0.6 (2006-11-07)
** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
* v1.0.5 (2006-03-02)
** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
* v1.0.4 (2006-02-28)
** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
* v1.0.3 (2006-02-26)
** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
* v1.0.2 (2006-02-05)
** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
* v1.0.1 (2006-01-27)
** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
* v1.0.0 (2006-01-25)
** initial version
Chap 1 - OSI Module
Chap 2 - Ethernet
Chap 3 - 網路設備
Chap 4 - TCP / IP
Chap 5 - ARP、DHCP、DNS
*資安相關證照
**CISSP:需 5 年相關工作經驗,考試偏理論
***http://www.isc2.org
**SSCP:需 1 年相關工作經驗,考試偏實作
**Security+:適合新接觸資訊安全的人
**CEH:考一些老舊的駭客技術
*Technet Magazine
!網路概論 (日後補上)
* [[Chap 1 - OSI Module]]
* [[Chap 2 - Ethernet]]
* [[Chap 3 - 網路設備]]
* [[Chap 4 - TCP / IP]]
* [[Chap 5 - ARP、DHCP、DNS]]
!Cisco ICND (日後補上)
* [[Chap 0 - Cisco 網路設備]]
* [[Chap 1 - 交換技術 (Switching)]]
* [[Chap 2 - 路由技術 (Routing)]]
* [[Chap 3 - 存取控制清單 (ACL)]]
* [[Chap 4 - 廣域網路 (WAN)]]
!無線網路 (2008/07/15 online)
!網路安全 (unknown)