<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}

h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}

.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}

.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}

.tabSelected{color:[[ColorPalette::PrimaryDark]];
	background:[[ColorPalette::TertiaryPale]];
	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}

#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}

.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}

.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}

#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}

.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}

.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}

.tiddler .defaultCommand {font-weight:bold;}

.shadow .title {color:[[ColorPalette::TertiaryDark]];}

.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}

.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}

.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}

.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}

.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}

.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}

.imageLink, #displayArea .imageLink {background:transparent;}

.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}

.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}

.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}

.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}

.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}

.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}

#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
/*}}}*/
/*{{{*/
* 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; width:90%; 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;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
noscript {display:none;}
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></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>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></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 excludeLists'></span></div>
<!--}}}-->
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* MainMenu: The menu (usually on the left)
* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These InterfaceOptions for customising TiddlyWiki are saved in your browser

Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)

<<option txtUserName>>
<<option chkSaveBackups>> SaveBackups
<<option chkAutoSave>> AutoSave
<<option chkRegExpSearch>> RegExpSearch
<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
<<option chkAnimate>> EnableAnimations

----
Also see AdvancedOptions
<<importTiddlers>>
|''Type:''|file|
|''URL:''|http://tiddlywiki.bidix.info/|
|''Workspace:''|(default)|

This tiddler was automatically created to record the details of this server
PasswordOptionPlugin extends the core Options with a non encrypted password type.

Notice:
*How a style can be specified for a specific option in StyleSheet

----
Test Password: <<option pasPassword myPasOptionInput >>
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
	major: 1, minor: 0, revision: 2,
	date: new Date("Apr 19, 2007"),
	source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
	coreVersion: '2.2.0 (Beta 5)'
};

config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordInputType = "password"; // password | text
setStylesheet(".pasOptionInput {width: 11em;}\n","passwordInputTypeStyle");

merge(config.macros.option.types, {
	'pas': {
		elementType: "input",
		valueField: "value",
		eventName: "onkeyup",
		className: "pasOptionInput",
		typeValue: config.macros.option.passwordInputType,
		create: function(place,type,opt,className,desc) {
			// password field
			config.macros.option.genericCreate(place,'pas',opt,className,desc);
			// checkbox linked with this password "save this password on this computer"
			config.macros.option.genericCreate(place,'chk','chk'+opt,className,desc);
			// text savePasswordCheckboxLabel
			place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));
		},
		onChange: config.macros.option.genericOnChange
	}
});

merge(config.optionHandlers['chk'], {
	get: function(name) {
		// is there an option linked with this chk ?
		var opt = name.substr(3);
		if (config.options[opt])
			saveOptionCookie(opt);
		return config.options[name] ? "true" : "false";
	}
});

merge(config.optionHandlers, {
	'pas': {
 		get: function(name) {
			if (config.options["chk"+name]) {
				return encodeCookie(config.options[name].toString());
			} else {
				return "";
			}
		},
		set: function(name,value) {config.options[name] = decodeCookie(value);}
	}
});

// need to reload options to load passwordOptions
loadOptionsCookie();

/*
if (!config.options['pasPassword'])
	config.options['pasPassword'] = '';

merge(config.optionsDesc,{
		pasPassword: "Test password"
	});
*/
//}}}
// make it so you can by default see edit controls via http
config.options.chkHttpReadOnly = false;
window.readOnly = false; // make sure of it (for tw 2.2)
window.showBackstage = true; // show backstage too
<<tiddler UploadPluginDoc>>
This form upload any file with an UploadService describe in [[Upload]]
----
<html><center>
<form enctype="multipart/form-data" action="store.php" method="post" target="_blank">
 <input type="hidden" name="MAX_FILE_SIZE" value="3000000" />
This file : <input name="userfile" type="file" /><p>
Options* : <input type="text" name="UploadPlugin" size=80 value="backupDir=BACKUP_DIR;uploaddir=UPLOAD_DIR;user=UPLOAD_USER;password=UPLOAD_PASSWORD;debug=0;" /><p>
 <input type="submit" value="Upload" />
</form></center>
</html>
----
 * Substitute BACKUP_DIR, UPLOAD_DIR, UPLOAD_USER and UPLOAD_PASSWORD with your values. See UploadPlugin for option details.
For security reason, don't save your password in a tiddler.
UploadPlugin uses the following sequence for finding parameters :
#''macro'' parameters
#''Options'' saved in cookies
#''Plugin'' default values
!Options used by UploadPlugin
| Option | Value | Default |
|Upload Username: |<<option txtUploadUserName>>| |
|Upload Password: |<<option pasUploadPassword>>| |
|Url of the UploadService script: |<<option txtUploadStoreUrl urlInput>>| store.php |
|Relative Directory where to store the file: |<<option txtUploadDir urlInput>>| . (the script directory) |
|Filename of the uploaded file: |<<option txtUploadFilename urlInput>>| index.html |
|Directory to backup file on webserver^^(1)^^: |<<option txtUploadBackupDir urlInput>>| //null// (none/no backup) |
|Log in UploadLog |<<option chkUploadLog>> Trace Upload| true |
|Maximum of lines in UploadLog |<<option txtUploadLogMaxLine>>| 10 |

^^(1)^^No backup if Backup Directory is empty, the previous file will be overwritten. Use a '.' to backup in the script directory.

<<upload>> with these options.

!Upload Macro parameters
{{{
<<upload [storeUrl [toFilename [backupDir [uploadDir [username]]]]]>>
 Optional positional parameters can be passed to overwrite UploadOptions.
}}}

!UploadToFile Macro Macro parameters
{{{
<<uploadTofile [filename [tiddlerTitle]]>>
 tiddlerTitle, filename: if omitted the title of the current tiddler
}}}

<<uploadToFile allowedsites.txt allowedsites.txt>>
/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.4|
|''Date:''|2008-08-11|
|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#UploadPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
	major: 4, minor: 1, revision: 4,
	date: new Date("2008-08-11"),
	source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0'
};

//
// Environment
//

if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false;	// true to activate both in Plugin and UploadService

//
// Upload Macro
//

config.macros.upload = {
// default values
	defaultBackupDir: '',	//no backup
	defaultStoreScript: "store.php",
	defaultToFilename: "index.html",
	defaultUploadDir: ".",
	authenticateUser: true	// UploadService Authenticate User
};

config.macros.upload.label = {
	promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
	promptParamMacro: "Save and Upload this TiddlyWiki in %0",
	saveLabel: "save to web",
	saveToDisk: "save to disk",
	uploadLabel: "upload"
};

config.macros.upload.messages = {
	noStoreUrl: "No store URL in parmeters or options",
	usernameOrPasswordMissing: "Username or password missing"
};

config.macros.upload.handler = function(place,macroName,params) {
	if (readOnly)
		return;
	var label;
	if (document.location.toString().substr(0,4) == "http")
		label = this.label.saveLabel;
	else
		label = this.label.uploadLabel;
	var prompt;
	if (params[0]) {
		prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0],
			(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
	} else {
		prompt = this.label.promptOption;
	}
	createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
};

config.macros.upload.action = function(params)
{
		// for missing macro parameter set value from options
		if (!params) params = {};
		var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
		var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
		var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
		var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
		var username = params[4] ? params[4] : config.options.txtUploadUserName;
		var password = config.options.pasUploadPassword; // for security reason no password as macro parameter
		// for still missing parameter set default value
		if ((!storeUrl) && (document.location.toString().substr(0,4) == "http"))
			storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
		if (storeUrl.substr(0,4) != "http")
			storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
		if (!toFilename)
			toFilename = bidix.basename(window.location.toString());
		if (!toFilename)
			toFilename = config.macros.upload.defaultToFilename;
		if (!uploadDir)
			uploadDir = config.macros.upload.defaultUploadDir;
		if (!backupDir)
			backupDir = config.macros.upload.defaultBackupDir;
		// report error if still missing
		if (!storeUrl) {
			alert(config.macros.upload.messages.noStoreUrl);
			clearMessage();
			return false;
		}
		if (config.macros.upload.authenticateUser && (!username || !password)) {
			alert(config.macros.upload.messages.usernameOrPasswordMissing);
			clearMessage();
			return false;
		}
		bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password);
		return false;
};

config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir)
{
	if (!storeUrl)
		return null;
		var dest = bidix.dirname(storeUrl);
		if (uploadDir && uploadDir != '.')
			dest = dest + '/' + uploadDir;
		dest = dest + '/' + toFilename;
	return dest;
};

//
// uploadOptions Macro
//

config.macros.uploadOptions = {
	handler: function(place,macroName,params) {
		var wizard = new Wizard();
		wizard.createWizard(place,this.wizardTitle);
		wizard.addStep(this.step1Title,this.step1Html);
		var markList = wizard.getElement("markList");
		var listWrapper = document.createElement("div");
		markList.parentNode.insertBefore(listWrapper,markList);
		wizard.setValue("listWrapper",listWrapper);
		this.refreshOptions(listWrapper,false);
		var uploadCaption;
		if (document.location.toString().substr(0,4) == "http")
			uploadCaption = config.macros.upload.label.saveLabel;
		else
			uploadCaption = config.macros.upload.label.uploadLabel;

		wizard.setButtons([
				{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption,
					onClick: config.macros.upload.action},
				{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}

			]);
	},
	options: [
		"txtUploadUserName",
		"pasUploadPassword",
		"txtUploadStoreUrl",
		"txtUploadDir",
		"txtUploadFilename",
		"txtUploadBackupDir",
		"chkUploadLog",
		"txtUploadLogMaxLine"
	],
	refreshOptions: function(listWrapper) {
		var opts = [];
		for(i=0; i<this.options.length; i++) {
			var opt = {};
			opts.push();
			opt.option = "";
			n = this.options[i];
			opt.name = n;
			opt.lowlight = !config.optionsDesc[n];
			opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
			opts.push(opt);
		}
		var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
		for(n=0; n<opts.length; n++) {
			var type = opts[n].name.substr(0,3);
			var h = config.macros.option.types[type];
			if (h && h.create) {
				h.create(opts[n].colElements['option'],type,opts[n].name,opts[n].name,"no");
			}
		}

	},
	onCancel: function(e)
	{
		backstage.switchTab(null);
		return false;
	},

	wizardTitle: "Upload with options",
	step1Title: "These options are saved in cookies in your browser",
	step1Html: "<input type='hidden' name='markList'></input><br>",
	cancelButton: "Cancel",
	cancelButtonPrompt: "Cancel prompt",
	listViewTemplate: {
		columns: [
			{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
			{name: 'Option', field: 'option', title: "Option", type: 'String'},
			{name: 'Name', field: 'name', title: "Name", type: 'String'}
			],
		rowClasses: [
			{className: 'lowlight', field: 'lowlight'}
			]}
};

//
// upload functions
//

if (!bidix.upload) bidix.upload = {};

if (!bidix.upload.messages) bidix.upload.messages = {
	//from saving
	invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
	backupSaved: "Backup saved",
	backupFailed: "Failed to upload backup file",
	rssSaved: "RSS feed uploaded",
	rssFailed: "Failed to upload RSS feed file",
	emptySaved: "Empty template uploaded",
	emptyFailed: "Failed to upload empty template file",
	mainSaved: "Main TiddlyWiki file uploaded",
	mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
	//specific upload
	loadOriginalHttpPostError: "Can't get original file",
	aboutToSaveOnHttpPost: 'About to upload on %0 ...',
	storePhpNotFound: "The store script '%0' was not found."
};

bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
{
	var callback = function(status,uploadParams,original,url,xhr) {
		if (!status) {
			displayMessage(bidix.upload.messages.loadOriginalHttpPostError);
			return;
		}
		if (bidix.debugMode)
			alert(original.substr(0,500)+"\n...");
		// Locate the storeArea div's
		var posDiv = locateStoreArea(original);
		if((posDiv[0] == -1) || (posDiv[1] == -1)) {
			alert(config.messages.invalidFileError.format([localPath]));
			return;
		}
		bidix.upload.uploadRss(uploadParams,original,posDiv);
	};

	if(onlyIfDirty && !store.isDirty())
		return;
	clearMessage();
	// save on localdisk ?
	if (document.location.toString().substr(0,4) == "file") {
		var path = document.location.toString();
		var localPath = getLocalPath(path);
		saveChanges();
	}
	// get original
	var uploadParams = new Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
	var originalPath = document.location.toString();
	// If url is a directory : add index.html
	if (originalPath.charAt(originalPath.length-1) == "/")
		originalPath = originalPath + "index.html";
	var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
	var log = new bidix.UploadLog();
	log.startUpload(storeUrl, dest, uploadDir,  backupDir);
	displayMessage(bidix.upload.messages.aboutToSaveOnHttpPost.format([dest]));
	if (bidix.debugMode)
		alert("about to execute Http - GET on "+originalPath);
	var r = doHttp("GET",originalPath,null,null,username,password,callback,uploadParams,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

bidix.upload.uploadRss = function(uploadParams,original,posDiv)
{
	var callback = function(status,params,responseText,url,xhr) {
		if(status) {
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.rssSaved,bidix.dirname(url)+'/'+destfile);
			bidix.upload.uploadMain(params[0],params[1],params[2]);
		} else {
			displayMessage(bidix.upload.messages.rssFailed);
		}
	};
	// do uploadRss
	if(config.options.chkGenerateAnRssFeed) {
		var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
		var rssUploadParams = new Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
		var rssString = generateRss();
		// no UnicodeToUTF8 conversion needed when location is "file" !!!
		if (document.location.toString().substr(0,4) != "file")
			rssString = convertUnicodeToUTF8(rssString);
		bidix.upload.httpUpload(rssUploadParams,rssString,callback,Array(uploadParams,original,posDiv));
	} else {
		bidix.upload.uploadMain(uploadParams,original,posDiv);
	}
};

bidix.upload.uploadMain = function(uploadParams,original,posDiv)
{
	var callback = function(status,params,responseText,url,xhr) {
		var log = new bidix.UploadLog();
		if(status) {
			// if backupDir specified
			if ((params[3]) && (responseText.indexOf("backupfile:") > -1))  {
				var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
				displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
			}
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.mainSaved,bidix.dirname(url)+'/'+destfile);
			store.setDirty(false);
			log.endUpload("ok");
		} else {
			alert(bidix.upload.messages.mainFailed);
			displayMessage(bidix.upload.messages.mainFailed);
			log.endUpload("failed");
		}
	};
	// do uploadMain
	var revised = bidix.upload.updateOriginal(original,posDiv);
	bidix.upload.httpUpload(uploadParams,revised,callback,uploadParams);
};

bidix.upload.httpUpload = function(uploadParams,data,callback,params)
{
	var localCallback = function(status,params,responseText,url,xhr) {
		url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
		if (xhr.status == 404)
			alert(bidix.upload.messages.storePhpNotFound.format([url]));
		if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
			alert(responseText);
			if (responseText.indexOf("Debug mode") >= 0 )
				responseText = responseText.substring(responseText.indexOf("\n\n")+2);
		} else if (responseText.charAt(0) != '0')
			alert(responseText);
		if (responseText.charAt(0) != '0')
			status = null;
		callback(status,params,responseText,url,xhr);
	};
	// do httpUpload
	var boundary = "---------------------------"+"AaB03x";
	var uploadFormName = "UploadPlugin";
	// compose headers data
	var sheader = "";
	sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
	sheader += uploadFormName +"\"\r\n\r\n";
	sheader += "backupDir="+uploadParams[3] +
				";user=" + uploadParams[4] +
				";password=" + uploadParams[5] +
				";uploaddir=" + uploadParams[2];
	if (bidix.debugMode)
		sheader += ";debug=1";
	sheader += ";;\r\n";
	sheader += "\r\n" + "--" + boundary + "\r\n";
	sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
	sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
	sheader += "Content-Length: " + data.length + "\r\n\r\n";
	// compose trailer data
	var strailer = new String();
	strailer = "\r\n--" + boundary + "--\r\n";
	data = sheader + data + strailer;
	if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
	var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; ;charset=UTF-8; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
{
	if (!posDiv)
		posDiv = locateStoreArea(original);
	if((posDiv[0] == -1) || (posDiv[1] == -1)) {
		alert(config.messages.invalidFileError.format([localPath]));
		return;
	}
	var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
				store.allTiddlersAsHtml() + "\n" +
				original.substr(posDiv[1]);
	var newSiteTitle = getPageTitle().htmlEncode();
	revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
	revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
	revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
	revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
	revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
	return revised;
};

//
// UploadLog
//
// config.options.chkUploadLog :
//		false : no logging
//		true : logging
// config.options.txtUploadLogMaxLine :
//		-1 : no limit
//      0 :  no Log lines but UploadLog is still in place
//		n :  the last n lines are only kept
//		NaN : no limit (-1)

bidix.UploadLog = function() {
	if (!config.options.chkUploadLog)
		return; // this.tiddler = null
	this.tiddler = store.getTiddler("UploadLog");
	if (!this.tiddler) {
		this.tiddler = new Tiddler();
		this.tiddler.title = "UploadLog";
		this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
		this.tiddler.created = new Date();
		this.tiddler.modifier = config.options.txtUserName;
		this.tiddler.modified = new Date();
		store.addTiddler(this.tiddler);
	}
	return this;
};

bidix.UploadLog.prototype.addText = function(text) {
	if (!this.tiddler)
		return;
	// retrieve maxLine when we need it
	var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
	if (isNaN(maxLine))
		maxLine = -1;
	// add text
	if (maxLine != 0)
		this.tiddler.text = this.tiddler.text + text;
	// Trunck to maxLine
	if (maxLine >= 0) {
		var textArray = this.tiddler.text.split('\n');
		if (textArray.length > maxLine + 1)
			textArray.splice(1,textArray.length-1-maxLine);
			this.tiddler.text = textArray.join('\n');
	}
	// update tiddler fields
	this.tiddler.modifier = config.options.txtUserName;
	this.tiddler.modified = new Date();
	store.addTiddler(this.tiddler);
	// refresh and notifiy for immediate update
	story.refreshTiddler(this.tiddler.title);
	store.notify(this.tiddler.title, true);
};

bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir,  backupDir) {
	if (!this.tiddler)
		return;
	var now = new Date();
	var text = "\n| ";
	var filename = bidix.basename(document.location.toString());
	if (!filename) filename = '/';
	text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
	text += config.options.txtUserName + " | ";
	text += "[["+filename+"|"+location + "]] |";
	text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
	text += uploadDir + " | ";
	text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
	text += backupDir + " |";
	this.addText(text);
};

bidix.UploadLog.prototype.endUpload = function(status) {
	if (!this.tiddler)
		return;
	this.addText(" "+status+" |");
};

//
// Utilities
//

bidix.checkPlugin = function(plugin, major, minor, revision) {
	var ext = version.extensions[plugin];
	if (!
		(ext  &&
			((ext.major > major) ||
			((ext.major == major) && (ext.minor > minor))  ||
			((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
			// write error in PluginManager
			if (pluginInfo)
				pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
			eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
	}
};

bidix.dirname = function(filePath) {
	if (!filePath)
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(0, lastpos);
	} else {
		return filePath.substring(0, filePath.lastIndexOf("\\"));
	}
};

bidix.basename = function(filePath) {
	if (!filePath)
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("#")) != -1)
		filePath = filePath.substring(0, lastpos);
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(lastpos + 1);
	} else
		return filePath.substring(filePath.lastIndexOf("\\")+1);
};

bidix.initOption = function(name,value) {
	if (!config.options[name])
		config.options[name] = value;
};

//
// Initializations
//

// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);

// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");

//optionsDesc
merge(config.optionsDesc,{
	txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
	txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
	txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
	txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
	txtUploadUserName: "Upload Username",
	pasUploadPassword: "Upload Password",
	chkUploadLog: "do Logging in UploadLog (default: true)",
	txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"
});

// Options Initializations
bidix.initOption('txtUploadStoreUrl','');
bidix.initOption('txtUploadFilename','');
bidix.initOption('txtUploadDir','');
bidix.initOption('txtUploadBackupDir','');
bidix.initOption('txtUploadUserName','');
bidix.initOption('pasUploadPassword','');
bidix.initOption('chkUploadLog',true);
bidix.initOption('txtUploadLogMaxLine','10');


// Backstage
merge(config.tasks,{
	uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}
});
config.backstageTasks.push("uploadOptions");


//}}}

//last update: UploadPlugin v 4.1.3//

!Description
UploadPlugin with <<tag UploadService>> extend TiddlyWiki with @@upload@@ and @@save to web@@ commands.
UploadPlugin uses Username and Password from UploadOptions stored in cookies to authenticate itself to [[store.php]] or [[store.cgi]].
UploadPlugin requires PasswordOptionPlugin.
French translation available as a separate tiddler UploadPluginMsgFR

!!UploadPlugin
*If the TiddlyWiki is viewed @@from local disk@@ :
**{{{<<upload ...>>}}}
***display as '''upload'''
***after saving to disk, upload to a website.
*If the TiddlyWiki is viewed @@from a website@@ :
**{{{<<upload ...>>}}}
***display as '''save to web'''
***save to the same website.
*If GenerateAnRssFeed in AdvancedOptions is set :
**generate the content of the RSSFeed
**upload the RssFile
**Caution : use the SiteUrl tiddler to specify the right url of the TiddlyWiki in the generated RssFile
*DisplayMessage
*If a backupDir is specified
**and if a file already exists in the uploadDir withe the same fielname, move it in the backup dir and rename it with a timestamp
*if UploadLog is activated every upload action is logged in UploadLog
**only the last maxLines are stored
**if maxLine = -1 no truncation will occured
hint : if UploadLog is the first tiddler in the TimelineTab, no tiddler has been changed since last upload.

!![[store.php]]
*UserVariables to set :
//{{{
$AUTHENTICATE_USER = true; // true | false
$USERS = array(
 'UserName1'=>'Password1',
 'UserName2'=>'Password2',
 'UserName3'=>'Password3'); // set usernames and strong passwords
$DEBUG = false; // true | false
//}}}
*method GET
**display an information page
*method POST
**if $~AUTHENTICATE_USER is ''true''
***presence and value of user and password are checked with $USER and $PASSWORD
**if toFilename already exists and backDir parameter specified
***rename toFilename to backupDir/toFilename.AAAAMMDD.HHSS.html
**copy temporaryUploadedFile toFilename
**return status

!![[store.cgi]]
*UserVariables to set :
//{{{
CONFIG = {
 :users => {
 'UserName1'=>'Password1',
 'UserName2'=>'Password2',
 'UserName3'=>'Password3')
 },
 :authenticateUser => true,
 :backupExistingFile => true,
 :withUploadDir => true
}
//}}}
*same processing as store.php above

!Usage :
{{{
<<upload>>
 uses UploadOptions saved in cookies :
 txtUploadUserName: username
 pasUploadPassword : password
 txtUploadStoreUrl : store script
 txtUploadDir : relative path for upload directory
 txtUploadFilename : upload filename
 txtUploadBackupDir : relative path for backup directory

<<upload [storeUrl [toFilename [backupDir [uploadDir [username]]]]]>>
 Optional positional parameters can be passed to overwrite
 UploadOptions.
}}}

Suggestion: Install the {{{<<upload ... >>}}} macro in SideBarOptions just below {{{<<saveChanges>>}}} macro.


!User manual
See HowToUpload

!Installation :
*Install the UploadPlugin as usual
*[[Upload]] the [[store.php]] file on your php aware webserver in your TiddlyWiki directory
*Protect your server against malicious upload. Two approaches :
**set $~AUTHENTICATE_USER to true in the [[store.php]] script
***configure $USER and $PASSWORD in the [[store.php]] script on your webserver
***set UploadOptions in conformity with [[store.php]]
**Use server protection :
***for Apache web server ([[for detail see Apache documentation|http://httpd.apache.org/docs/1.3/howto/htaccess.html]]) :
****configure and upload the [[.htaccess]] [[.passwd]]
***for other web servers see the appropriate documentation
*Configure an upload button, for example in the SideBarOptions
!Suppported Browser
*Firefox and Gecko based browser: tested Ok
*Internet Explorer : tested Ok
*Safari : tested ok on OS X
*Others : Not tested, please report status.

!Revision history
<<tiddler UploadPluginRevisionHistory>>
UploadTiddlerPlugin and [[storeTiddler.php]] "upload" or "save to web" a single tiddler by updating, inserting or deleting a single tiddler in a TiddlyWiki file located on the web.

@@Now UploadTiddlerPlugin is able to Upload a Tiddler even if TiddlyWiki is located on a local file.@@

![[UploadTiddlerPlugin]]
*is activated by @@chkUploadTiddler option: <<option chkUploadTiddler>>@@
* to be abe to Upload a Tiddler even if the TiddlyWiki is located on local file@@chkUploadTiddlerFromFile option: <<option chkUploadTiddlerFromFile>>@@
*uses @@txtUploadTiddlerStoreUrl: <<option txtUploadTiddlerStoreUrl>>@@
*override TiddlyWiki.prototype.saveTiddler and deleteTiddler core functions
*uses the same options as UploadPlugin.
*do an XmlHttpRequest POST to storeTiddler.php only with the corresponding tiddler in storeArea format.
![[storeTiddler.php]]
*Username and password are checked
*[[storeTiddler.php]] parses TiddlyWiki file
*tiddler is updated, inserted or deleted in the tiddler array
*optionally create and clean backup file
*write updated TiddlyWiki file
!UploadTiddlerPlugin and UploadPlugin
*The paradigm of UploadTiddlerPlugin and [[storeTiddler.php]] is exactly the same as UploadPlugin and [[store.php]]
*No concurrency managed
*Full compatible with UploadPlugin
*[[storeTiddler.php]] can be use to remotly update a TiddlyWiki. storeTiddler.php
*a GET to [[storeTiddler.php]] display a form to upload a tiddler in a TiddlyWiki
/***
|''Name:''|UploadTiddlerMacro|
|''Description:''|Define a macro to upload a tiddler using UploadTiddlerPlugin (see UploadTiddlerPlugin for details).|
|''Version:''|1.0.0|
|''Date:''|2008-09-30|
|''Source:''|http://tiddlywiki.bidix.info/#UploadTiddlerMacro|
|''Usage:''|{{{<<uploadTiddler [tiddlerTitle]>>}}}<br>{{{tiddlerTitle: if omitted the title of the current tiddler}}}|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''CoreVersion:''|2.2.0|
|''Requires:''|UploadTiddlerPlugin|
***/
//{{{
version.extensions.UploadTiddlerMacro = {
	major: 1, minor: 0, revision: 0,
	date: new Date("2008-09-30"),
	source: 'http://tiddlywiki.bidix.info/#UploadTiddlerMacro',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0'
};

config.macros.uploadTiddler = {
	label: "uploadTiddler",
	prompt: "upload tiddler '%0' using UploadTiddlerOptions ",
	warning: "Are you sure you want to upload '%0'?",
	messages: {
		fileUploaded: "tiddler '%0' uploaded to file '%1'",
		fileNotUploaded: "tiddler '%0' NOT uploaded"
	},

	handler: function(place, macroName, params, wikifier,paramString, tiddler) {
		var tiddlerTitle = params[0];
		if (!tiddlerTitle) {
			tiddlerTitle = tiddler.title;
		} else {
			tiddler = store.getTiddler(tiddlerTitle);
		}
		createTiddlyButton(place, this.label, this.prompt.format([tiddlerTitle]),
			function () {
				config.macros.uploadTiddler.upload(tiddlerTitle, tiddler);
				return false;},
			null, null, null);
	},

	upload:  function(tiddlerTitle, tiddler) {
		var saved_chkUploadTiddler = config.options.chkUploadTiddler;
		config.options.chkUploadTiddler = true;
		bidix.uploadTiddler.upload(tiddlerTitle, tiddler,tiddlerTitle);
		config.options.chkUploadTiddler = saved_chkUploadTiddler;
	}
};

//
// Utilities
//
if (!window.bidix) window.bidix = {}; // bidix namespace

bidix.checkPlugin = function(plugin, major, minor, revision) {
	var ext = version.extensions[plugin];
	if (!
		(ext  &&
			((ext.major > major) ||
			((ext.major == major) && (ext.minor > minor))  ||
			((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
			// write error in PluginManager
			if (pluginInfo)
				pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
			eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
	}
};

//
// Initializations
//

// require UploadTiddlerPlugin 1.2.2 or better
bidix.checkPlugin("UploadTiddlerPlugin", 1, 2, 2);


//}}}
/***
|''Name:''|UploadTiddlerPlugin|
|''Description:''|Upload a tiddler and Update a remote TiddlyWiki |
|''Version:''|1.2.2|
|''Date:''|2008-09-13|
|''Source:''|http://tiddlywiki.bidix.info/#UploadTiddlerPlugin|
|''Usage:''|Uses {{{<<uploadOptions>>}}}<br>with those UploadTiddler Options : <br>chkUploadTiddler: <<option chkUploadTiddler>><br>txtUploadTiddlerStoreUrl: <<option txtUploadTiddlerStoreUrl>><br>chkUploadTiddlerFromFile: <<option chkUploadTiddlerFromFile>>|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''[[License]]:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''CoreVersion:''|2.3.0|
***/
//{{{
version.extensions.UploadTiddlerPlugin = {
	major: 1, minor: 2, revision: 2,
	date: new Date("2008-09-13"),
	source: 'http://tiddlywiki.bidix.info/#UploadTiddlerPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.3.0'
};

if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false;
bidix.uploadTiddler = {
	messages: {
		aboutToSaveTiddler: "About to update tiddler '%0'...",
		aboutToRemotelySaveTiddler: "About to REMOTELY update tiddler '%0'...",
		storeTiddlerNotFound: "Script store tiddler '%0' not found",
		tiddlerSaved: "Tiddler '%0' updated in '%1' using '%2' "
	},
	upload: function(title,tiddler,oldTitle) {
		var callback = function(status,params,responseText,url,xhr) {
			if (xhr.status == 404) {
				alert(bidix.uploadTiddler.messages.storeTiddlerNotFound.format([url]));
				return;
			}
			if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
				alert(responseText);
				if (responseText.indexOf("Debug mode") >= 0 )
					responseText = responseText.substring(responseText.indexOf("\n\n")+2);
			} else if (responseText.charAt(0) != '0')
				alert(responseText);
			else
				displayMessage(bidix.uploadTiddler.messages.tiddlerSaved.format([params[0], params[1], params[2]]));
				store.setDirty(false);
			}

		if ((config.options['chkUploadTiddler']) &&
				((document.location.toString().substr(0,4) == "http") || config.options['chkUploadTiddlerFromFile'])) {
			clearMessage();
			if (document.location.toString().substr(0,4) != "http")
				displayMessage(bidix.uploadTiddler.messages.aboutToRemotelySaveTiddler.format([title]));
			else
				displayMessage(bidix.uploadTiddler.messages.aboutToSaveTiddler.format([title]));
			var ExtTiddler = null;
			var html = null;
			if (tiddler) {
				ExtTiddler = store.getSaver().externalizeTiddler(store,tiddler);
				html = wikifyStatic(tiddler.text,null,tiddler).htmlEncode();
			}
			var form = "title="+encodeURIComponent(title);
			form = form + "&tiddler="+(ExtTiddler?encodeURIComponent(ExtTiddler):'');
			form = form + "&html="+(html?encodeURIComponent(html):'');
			var filename = (config.options['txtUploadFilename']?config.options['txtUploadFilename']:'index.html');
			form = form +"&oldTitle="+encodeURIComponent(oldTitle);
			form = form +"&fileName="+encodeURIComponent(filename);
			form = form +"&backupDir="+encodeURIComponent(config.options['txtUploadBackupDir']);
			form = form +"&user="+encodeURIComponent(config.options['txtUploadUserName']);
			form = form +"&password="+encodeURIComponent(config.options['pasUploadPassword']);
			form = form +"&uploadir="+encodeURIComponent(config.options['txtUploadDir']);
			form = form +"&debug="+encodeURIComponent(0);
			var storeScript = (config.options.txtUploadTiddlerStoreUrl
								? config.options.txtUploadTiddlerStoreUrl : 'storeTiddler.php');
			var r = doHttp("POST",storeScript,form+"\n",'application/x-www-form-urlencoded',
				config.options['txtUploadUserName'],config.options['pasUploadPassword'],callback,Array(title,filename, storeScript),null);
		}
	}
}
TiddlyWiki.prototype.saveTiddler_bidix = TiddlyWiki.prototype.saveTiddler;
TiddlyWiki.prototype.saveTiddler = function(oldTitle,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created) {
	var tiddler = TiddlyWiki.prototype.saveTiddler_bidix.apply(this,arguments);
	var title = (newTitle?newTitle:oldTitle);
	if (oldTitle == title)
		oldTitle = '';
	bidix.uploadTiddler.upload(title, tiddler, oldTitle);
}
TiddlyWiki.prototype.removeTiddler_bidix =TiddlyWiki.prototype.removeTiddler;
TiddlyWiki.prototype.removeTiddler = function(title) {
	TiddlyWiki.prototype.removeTiddler_bidix.apply(this,arguments);
	bidix.uploadTiddler.upload(title, null);
}

//
// Initializations
//

bidix.initOption = function(name,value) {
	if (!config.options[name])
		config.options[name] = value;
};

// styleSheet
setStylesheet('.txtUploadTiddlerStoreUrl {width: 22em;}',"uploadTiddlerPluginStyles");

//optionsDesc
merge(config.optionsDesc,{
	txtUploadTiddlerStoreUrl: "Url of the UploadTiddlerService script (default: storeTiddler.php)",
	chkUploadTiddler: "Do per Tiddler upload using txtUploadTiddlerStoreUrl (default: false)",
	chkUploadTiddlerFromFile: "Upload tiddler even if TiddlyWiki is located on local file (default: false)"
});

// Options Initializations
bidix.initOption('txtUploadTiddlerStoreUrl','');
bidix.initOption('chkUploadTiddler','');
bidix.initOption('chkUploadTiddlerFromFile','');


// add options in backstage UploadOptions
if (config.macros.uploadOptions) {
	if (config.macros.uploadOptions.options) {
		config.macros.uploadOptions.options.push("txtUploadTiddlerStoreUrl","chkUploadTiddler", "chkUploadTiddlerFromFile");
	}
}

//}}}

/***
|''Name:''|UploadToFileMacro|
|''Description:''|Upload a tiddler as a file using UploadPlugin context. Used with the SimonBaird's HideWhenPlugin ViewTemplate provides a new command in the tiddler toolbar.|
|''Version:''|2.0.1|
|''Date:''|Apr 21, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#UploadToFileMacro|
|''Usage:''|{{{<<uploadTofile [filename [tiddlerTitle]]>>}}}<br>{{{tiddlerTitle, filename: if omitted the title of the current tiddler}}}|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''CoreVersion:''|2.2.0|
|''Requires:''|UploadPlugin|
***/
//{{{
version.extensions.UploadToFileMacro = {
	major: 2, minor: 0, revision: 1,
	date: new Date("Apr 21, 2007"),
	source: 'http://tiddlywiki.bidix.info/#UploadToFilePlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0'
};

// require UploadPlugin 4.0.0 or better
config.macros.uploadToFile = {
	label: "upload %0 to file %1",
	prompt: "upload tiddler '%0' to file '%1' ",
	warning: "Are you sure you want to upload '%0'?",
	messages: {
		fileUploaded: "tiddler '%0' uploaded to file '%1'",
		fileNotUploaded: "tiddler '%0' NOT uploaded"
	},

	handler: function(place, macroName, params, wikifier,paramString, tiddler) {
		// parameters initialization
		var toFilename = params[0];
		var tiddlerTitle = params[1];
		if (!tiddlerTitle) {
			tiddlerTitle = tiddler.title;
		} else {
			tiddler = store.getTiddler(tiddlerTitle);
		}
		if (!toFilename) {
			toFilename = tiddlerTitle;
		}
		var prompt = this.prompt.format([tiddlerTitle, toFilename]);
		createTiddlyButton(place, this.label.format([params[1], params[0]]), this.prompt.format([tiddlerTitle, toFilename]),
			function () {
				config.macros.uploadToFile.upload(tiddler, null, toFilename);
				return false;},
			null, null, null);
	},

	upload:  function(tiddler, storeUrl, toFilename, uploadDir, backupDir, username, password) {
		if (!tiddler)	{
			alert("Tiddler not found.");
			return false;
		}
		var uploadIt = true;
		if ((this.warning) && (!confirm(this.warning.format([tiddler.title]))))
			return false;
		// parameters initialization
		storeUrl = (storeUrl ? storeUrl : config.options.txtUploadStoreUrl);
		toFilename = (toFilename ? toFilename : tiddler.title);
		backupDir = (backupDir ? backupDir : config.options.txtUploadBackupDir);
		uploadDir = (uploadDir ? uploadDir : config.options.txtUploadDir);
		username = (username ? username : config.options.txtUploadUserName);
		password = (password ? password :config.options.pasUploadPassword);
		var callback = function(status,params,responseText,url,xhr) {
			if (status) {
				if (responseText.substring(0,1) == '0') {
					// if backupDir specified
					if ((params[3]) && (responseText.indexOf("backupfile:") > -1))  {
						var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,
							responseText.indexOf("\n", responseText.indexOf("backupfile:")));
						displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
					}
					var destfile = responseText.substring(responseText.indexOf("destfile:")+9,
						responseText.indexOf("\n", responseText.indexOf("destfile:")));
					if (destfile.substring(0,2) == './')
						destfile = destfile.substring(2);
					displayMessage(config.macros.uploadToFile.messages.fileUploaded.format([params[6],destfile]),
						bidix.dirname(url)+'/'+destfile);
				} else {
					alert(responseText);
					displayMessage(config.macros.uploadToFile.messages.fileNotUploaded.format([params[1]]));
				}
			} else {
				displayMessage("HTTP Error " + xhr.status + " in accessing " + url);
			}
		};
		var uploadParams = Array(storeUrl,toFilename,uploadDir,backupDir,username,password, tiddler.title);
		return bidix.upload.httpUpload(uploadParams,tiddler.text,callback,uploadParams);

	}
};

bidix.checkPlugin = function(plugin, major, minor, revision) {
	var ext = version.extensions[plugin];
	if (!
		(ext  &&
			((ext.major > major) ||
			((ext.major == major) && (ext.minor > minor))  ||
			((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
			// write error in PluginManager
			if (pluginInfo)
				pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
			eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
	}
};

//
// Initializations
//

// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("UploadPlugin", 4, 0, 0);

//}}}
/***
|''Name:''|UploadToHomeMacro|
|''Description:''|Save TiddlyWiki using HomeParameters tiddler|
|''Version:''|0.0.2|
|''Date:''|May 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#UploadToHomeMacro|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (#2125)|
|''Requires:''|UploadPlugin|
|''Usage:''|{{{<<uploadToHome [HomeParameters]>>}}}<br>{{{HomeParameters:}}} optional - Tiddler with upload parameters in slices (see HomeParameters).|
***/
//{{{
version.extensions.UploadToHomeMacro = {
	major: 0, minor: 0, revision: 2,
	date: new Date("May 19, 2007"),
	source: 'http://tiddlywiki.bidix.info/#UploadToHomeMacro',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0 (#3125)'
};

//
// Environment
//

if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false;	// true to activate both in Plugin and UploadService
bidix.checkPlugin("UploadPlugin", 4, 1, 0);

//
// uploadUsing Macro
//

config.macros.uploadToHome = {
	handler: function(place,macroName,params) {
		if (readOnly)
			return;
		var label;
		if (document.location.toString().substr(0,4) == "http")
			label = config.macros.upload.label.saveLabel;
		else
			label = config.macros.upload.label.uploadLabel;
		var prompt;
		var homeParams = (params[0] ? params[0]:this.messages.homeParamsTiddler);
		if (store.tiddlerExists(homeParams) || store.isShadowTiddler(homeParams)) {
			prompt = this.messages.prompt.toString().format([homeParams]);
		} else {
			throw(this.messages.tiddlerNotFound.toString().format([homeParams]));
		}
		var prompt = this.messages.prompt.toString().format([homeParams]);
		createTiddlyButton(place, label, prompt, function() {config.macros.uploadToHome.action(homeParams);}, null, null, this.accessKey);
	},

	action: function(homeParams) {
		homeParams = (homeParams ? homeParams : config.macros.uploadToHome.messages.homeParamsTiddler);
		if (!store.tiddlerExists(homeParams) && !store.isShadowTiddler(homeParams)) {
			throw(config.macros.uploadToHome.messages.tiddlerNotFound.toString().format([homeParams]));
		}
		config.macros.upload.action(config.macros.uploadToHome.getParamsFromTiddler(homeParams));
	},

	getParamsFromTiddler: function(tiddlerTitle) {
		tiddlerTitle = (tiddlerTitle ? tiddlerTitle:this.messages.homeParamsTiddler);
		if (!store.tiddlerExists(tiddlerTitle) && !store.isShadowTiddler(tiddlerTitle)) {
			throw(config.macros.uploadToHome.messages.tiddlerNotFound.toString().format([tiddlerTitle]));
		}
		var sliceNames = [
			"UploadStoreUrl",
			"UploadFilename",
			"UploadBackupDir",
			"UploadDir",
			"UploadUserName"
			//"UploadPassword", // no password in tiddlers
		];
		var sliceValues = store.getTiddlerSlices(tiddlerTitle,sliceNames);
		var parameters = [];
		for(var i=0; i<sliceNames.length; i++) {
			parameters.push(sliceValues[sliceNames[i]]);
		}
		return parameters;
	},

	messages: {
		homeParamsTiddler: "HomeParameters",
		prompt: "Save and Upload this TiddlyWiki using parameters in  '%0' tiddler",
		tiddlerNotFound: "Tiddler %0 not found"
	},
	initAtLoad: function () {
		// install Shadowed HomeParameters
		var storeUrl;
		if ((document.location.toString().substr(0,4) == "http"))
			storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
		else
			storeUrl = config.macros.upload.defaultStoreScript;
		var shadowedHomeParameters = (config.shadowTiddlers['HomeParameters']?config.shadowTiddlers['HomeParameters']:'');
		shadowedHomeParameters += [
			"|UploadUserName:|"+config.options['txtUploadUserName']+"|",
			"|UploadStoreUrl:|"+storeUrl+"|",
			"|UploadDir:|.|",
			"|UploadFilename:|index.html|",
			"|UploadBackupDir:||"
			].join("\n");
		merge(config.shadowTiddlers,{'HomeParameters':	shadowedHomeParameters});
		// install Backstage uploadToHome
		merge(config.tasks,{
			uploadToHome: {text: "uploadToHome", tooltip: "Upload using '" + this.messages.homeParamsTiddler + "' tiddler", action: this.action}
		});
		config.backstageTasks.push("uploadToHome");

	}
};

config.macros.uploadToHome.initAtLoad();

//}}}
<?php
//{{{
/***
 * download.php - download an html file as an attachement.
 * version:1.1.1 - 2008/08/22 - BidiX@BidiX.info
 * source: http://tiddlywiki.bidix.info/#download.php
 * license: BSD open source license (http://tiddlywiki.bidix.info/#[[BSD open source license]])
 *
 * Simply put [[download|download.php?]] in your TiddlyWiki viewed over http to download it in one click*.
 *	* If it is named index.html
 * usage :
 *			http://host/path/to/download.php[?file=afile.html|?help]
 *				afile.html : for security reason, must be a file with an .html suffix
 *				?file=afile.html : if not specified index.html is used
 *				?help : display the "usage" message
 *
 *	each external javascript file is included in the downloaded file
 ***/

function display($msg) {
	?>
	<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
	<html>
		<head>
			<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
			<title>BidiX.info - TiddlyWiki - download script</title>
		</head>
		<body>
			<p>
			<p>download.php V 1.1.0
			<p>BidiX@BidiX.info
			<p>&nbsp;</p>
			<p>&nbsp;</p>
			<p>&nbsp;</p>
			<p align="center"><?=$msg?></p>
			<p align="center">Usage : http://<?=$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']?>[?file=<i>afile.html</i>]. If no file is specified uses index.html</p>
			<p align="center">for details see : <a href="http://TiddlyWiki.bidix.info/#download.php">TiddlyWiki.bidix.info/#download.php<a>.</p>
		</body>
	</html>
	<?php
	return;
}

/*
 * Recusrsively for each external javascript
 *		- Insert a comment : DOWNLOAD-INSERT-FILE
 *		- insert the content of the file
 */

function insertJSFileIn($content) {

	// if (preg_match ('<script\s+type=\"text\/javascript\"\s+src=\"')) {
	if (preg_match ('/<script\s+type=\"text\/javascript\"\s+src=\"/ms', $content)) {
		if (preg_match ('/^(.*?)<script\s+type=\"text\/javascript\"\s+src=\"(.+?)\"\s*>\s*<\/script>(.*)$/ms', $content,$matches)) {
			$front = $matches[1];
			$js = $matches[2];
			$tail = $matches[3];
			$jsContent = "<!--DOWNLOAD-INSERT-FILE:\"$js\"--><script type=\"text/javascript\">" .
				file_get_contents ($js) .
				"\n</script>";
			$tail = insertJSFileIn($tail);
			return($front.$jsContent.$tail);
		}
	}
	return $content;
}

/*
 * Main
 */

// help command
if (array_key_exists('help',$_GET)) {
	display('');
	exit;
}
// file command
$filename = $_GET['file'];
if ($filename == "") {
	$filename='index.html';
}
if (!preg_match('/\.html$/',$filename )) {
	display("The file $filename could not be downloaded. Only .html file are allowed.");
	exit;
}if (!is_file($filename)) {
	display("The file $filename could not be found.");
	exit;
}
$content = insertJSFileIn(file_get_contents ($filename));

//return the file
header('Pragma: private');
header('Cache-control: private, must-revalidate');
header('Content-type: text/html');
header('Content-Disposition: attachment; filename='.$filename);
echo($content);
//}}}
?>
<?php
//{{{
/***
 * [[proxy.php]] - access an url if the target host is allowed
 * version: 2.2.0 - 2007/08/03 - BidiX@BidiX.info
 * source: http://tiddlywiki.bidix.info/#proxy.php
 * license: BSD open source license (http://tiddlywiki.bidix.info/#[[BSD open source license]])
 * Copyright (c) BidiX@BidiX.info 2006-2007
 *
 * usage:
 * 		proxy.php?url=<hostAndParameters>
 *			return the corresponding url if the host is included in the <ALLOWED_SITE_FILENAME> file
 *			or is in the same domain
 *		proxy.php?list
 *			list all allowedHosts
 *		proxy.cgi[?help]
 *			Display an help page
 *
 * require:
 * 		<ALLOWED_SITE_FILENAME> a file located on the server containing a list af allowed hosts
 *			each host on a separate line.
 *     example :
 *				www.tiddlywiki.com
 *				tiddliwiki.bidix.info
 *				tiddlyspot.com
 ***/

$ALLOWED_SITE_FILENAME = '[[allowedsites.txt]]';
error_reporting(E_ERROR | E_PARSE);

function display($msg) {
	?>
	<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
	<html>
		<head>
			<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
			<title>BidiX.info - TiddlyWiki - proxy script</title>
		</head>
		<body>
			<p>
			<p>proxy.php V 2.2.0
			<p>BidiX@BidiX.info
			<p>&nbsp;</p>
			<p>&nbsp;</p>
			<p>&nbsp;</p>
			<p align="center"><?=$msg?></p>
			<p align="center">Usage : http://<?=$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']?>[?url=<i>URL</i>|help|list].</p>
			<p align="center">for details see : <a href="http://TiddlyWiki.bidix.info/#proxy.php">TiddlyWiki.bidix.info/#proxy.php<a>.</p>
		</body>
	</html>
	<?php
	return;
}

function domain($host) {
	// get last two segments of host name
	// See : http://fr2.php.net/manual/en/function.preg-match.php#id5827438
	preg_match('/[^.]+\.[^.]+$/', $host, $matches);
	return $matches[0];
}

function inMyDomain($host) {
	return (domain($_SERVER['HTTP_HOST']) == domain($host));
}

function isAllowed($host) {
	global $ALLOWED_SITE_FILENAME;
	// load allowed hosts
	$allowedHosts = array_map('rtrim',file($ALLOWED_SITE_FILENAME));
	if (!$allowedHosts) {
		echo("allowedSites file '$ALLOWED_SITE_FILENAME' is not found or empty.");
		exit;
	}
	return in_array($host, $allowedHosts);
}
/*
 * Main
 */

// help command

if (array_key_exists('help',$_GET)) {
	display('');
	exit;
}


// list command

if (array_key_exists('list',$_GET)) {
	echo "<h3>Hosts allowed through this proxy :</h3>\n<ul>\n";
	$allowedHosts = array_map('rtrim',file($ALLOWED_SITE_FILENAME));
	foreach ($allowedHosts as $host)
		echo("<li>$host</li>\n");
	echo "</ul>\n";
	exit;
}

// url command

//control url
$url = $_GET['url'];
if (!$url) {
	display('');
	exit;
}

if (substr($url, 0, 4) != 'http')
	$url = 'http://'.$url;
$urlArray = parse_url($url);
if (!$urlArray) {
	echo("URL '$url' is not well formed");
	exit;
}
$host = strtolower($urlArray['host']);

if (isset($urlArray['port']))
	$port = $urlArray['port'];
else
	$port = '80';
$file = $urlArray['path'];
if (isset($urlArray['query']))
	$file = $file.'?'.$urlArray['query'];
if (isset($urlArray['fragment']))
	$file = $file.'#'.$urlArray['fragment'];

// is $host allowed ?
if (!inMyDomain($host) && !isAllowed($host)) {
	echo("Host '$host' is not allowed.");
	exit;
}

// GET $file at $host:$port
// grab $headers and $content
$fp = fsockopen($host, $port, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)\n";
} else {
    $out = "GET $file HTTP/1.1\r\n";
    $out .= "Host: $host\r\n";
    $out .= "Connection: Close\r\n\r\n";

    fwrite($fp, $out);
    $fheader = 1;
    $headers = "";
	$content_type = "";
	$content = "";
    while (!feof($fp)) {
		$line = fgets($fp, 128);
		//Grab the headers
        if ($fheader == 1) {
            if ($line == "\r\n") {
                $fheader = 0;
            } else {
                //Assemble the headers
                $headers .= $line;
				//grab content_type
				if (preg_match("/Content-Type: (\w+\/\w+)(.*)/i", $line, $matches)) {
					$content_type = $line;
				}
            }
        } else {
            //Grab the page content
            $content .= $line;
        }
    }
    fclose($fp);
}

header($content_type);
echo ($content);
//}}}
<?php
/***
! User settings
Edit these lines according to your need
***/
//{{{
$AUTHENTICATE_USER = true;	// true | false
$USERS = array(
	'UserName1'=>'Password1',
	'UserName2'=>'Password2',
	'UserName3'=>'Password3'); // set usernames and strong passwords
$DEBUG = false;				// true | false
$CLEAN_BACKUP = true; 		// during backuping a file, remove overmuch backups
$FOLD_JS = true; 			// if javascript files have been expanded during download the fold them
error_reporting(E_ERROR | E_WARNING | E_PARSE);
//}}}
/***
!Code
No change needed under
***/
//{{{

/***
 * store.php - upload a file in this directory
 * version :1.6.1 - 2007/08/01 - BidiX@BidiX.info
 *
 * see :
 *	http://tiddlywiki.bidi.info/#UploadPlugin for usage
 *	http://www.php.net/manual/en/features.file-upload.php
 *		for details on uploading files
 * usage :
 *	POST
 *		UploadPlugin[backupDir=<backupdir>;user=<user>;password=<password>;uploadir=<uploaddir>;[debug=1];;]
 *		userfile <file>
 *	GET
 *
 * each external javascript file included by download.php is change by a reference (src=...)
 *
 * Revision history
 * V1.6.1 - 2007/08/01
 * Enhancement: Add javascript folding
 * V1.6.0 - 2007/05/17
 * Enhancement: Add backup management
 * V1.5.2 - 2007/02/13
 * Enhancement: Add optional debug option in client parameters
 * V1.5.1 - 2007/02/01
 * Enhancement: Check value of file_uploads in php.ini. Thanks to Didier Corbière
 * V1.5.0 - 2007/01/15
 * Correct: a bug in moving uploadFile in uploadDir thanks to DaniGutiérrez for reporting
 * Refactoring
 * V 1.4.3 - 2006/10/17
 * Test if $filename.lock exists for GroupAuthoring compatibility
 * return mtime, destfile and backupfile after the message line
 * V 1.4.2 - 2006/10/12
 *  add error_reporting(E_PARSE);
 * v 1.4.1 - 2006/03/15
 *	add chmo 0664 on the uploadedFile
 * v 1.4 - 2006/02/23
 * 	add uploaddir option :  a path for the uploaded file relative to the current directory
 *	backupdir is a relative path
 *	make recusively directories if necessary for backupDir and uploadDir
 * v 1.3 - 2006/02/17
 *	presence and value of user are checked with $USERS Array (thanks to PauloSoares)
 * v 1.2 - 2006/02/12
  *	POST
 *		UploadPlugin[backupDir=<backupdir>;user=<user>;password=<password>;]
 *		userfile <file>
*	if $AUTHENTICATE_USER
 *		presence and value of user and password are checked with
 *		$USER and $PASSWORD
 * v 1.1 - 2005/12/23
 *	POST  UploadPlugin[backupDir=<backupdir>]  userfile <file>
 * v 1.0 - 2005/12/12
 *	POST userfile <file>
 *
 * Copyright (c) BidiX@BidiX.info 2005-2007
 ***/
//}}}

//{{{

if ($_SERVER['REQUEST_METHOD'] == 'GET') {
	/*
	 * GET Request
	 */
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
		<title>BidiX.info - TiddlyWiki UploadPlugin - Store script</title>
	</head>
	<body>
		<p>
		<p>store.php V 1.6.1
		<p>BidiX@BidiX.info
		<p>&nbsp;</p>
		<p>&nbsp;</p>
		<p>&nbsp;</p>
		<p align="center">This page is designed to upload a <a href="http://www.tiddlywiki.com/">TiddlyWiki<a>.</p>
		<p align="center">for details see : <a href="http://TiddlyWiki.bidix.info/#HowToUpload">TiddlyWiki.bidix.info/#HowToUpload<a>.</p>
	</body>
</html>
<?php
exit;
}

/*
 * POST Request
 */

// Recursive mkdir
function mkdirs($dir) {
	if( is_null($dir) || $dir === "" ){
		return false;
	}
	if( is_dir($dir) || $dir === "/" ){
		return true;
	}
	if( mkdirs(dirname($dir)) ){
		return mkdir($dir);
	}
	return false;
}

function toExit() {
	global $DEBUG, $filename, $backupFilename, $options;
	if ($DEBUG) {
		echo ("\nHere is some debugging info : \n");
		echo("\$filename : $filename \n");
		echo("\$backupFilename : $backupFilename \n");
		print ("\$_FILES : \n");
		print_r($_FILES);
		print ("\$options : \n");
		print_r($options);
}
exit;
}

function ParseTWFileDate($s) {
	// parse date element
	preg_match ( '/^(\d\d\d\d)(\d\d)(\d\d)\.(\d\d)(\d\d)(\d\d)/', $s , $m );
	// make a date object
	$d = mktime($m[4], $m[5], $m[6], $m[2], $m[3], $m[1]);
	// get the week number
	$w = date("W",$d);

	return array(
		'year' => $m[1],
		'mon' => $m[2],
		'mday' => $m[3],
		'hours' => $m[4],
		'minutes' => $m[5],
		'seconds' => $m[6],
		'week' => $w);
}

function cleanFiles($dirname, $prefix) {
	$now = getdate();
	$now['week'] = date("W");

	$hours = Array();
	$mday = Array();
	$year = Array();

	$toDelete = Array();

	// need files recent first
	$files = Array();
	($dir = opendir($dirname)) || die ("can't open dir '$dirname'");
	while (false !== ($file = readdir($dir))) {
		if (preg_match("/^$prefix/", $file))
        array_push($files, $file);
    }
	$files = array_reverse($files);

	// decides for each file
	foreach ($files as $file) {
		$fileTime = ParseTWFileDate(substr($file,strpos($file, '.')+1,strrpos($file,'.') - strpos($file, '.') -1));
		if (($now['year'] == $fileTime['year']) &&
			($now['mon'] == $fileTime['mon']) &&
			($now['mday'] == $fileTime['mday']) &&
			($now['hours'] == $fileTime['hours']))
				continue;
		elseif (($now['year'] == $fileTime['year']) &&
			($now['mon'] == $fileTime['mon']) &&
			($now['mday'] == $fileTime['mday'])) {
				if (isset($hours[$fileTime['hours']]))
					array_push($toDelete, $file);
				else
					$hours[$fileTime['hours']] = true;
			}
		elseif 	(($now['year'] == $fileTime['year']) &&
			($now['mon'] == $fileTime['mon'])) {
				if (isset($mday[$fileTime['mday']]))
					array_push($toDelete, $file);
				else
					$mday[$fileTime['mday']] = true;
			}
		else {
			if (isset($year[$fileTime['year']][$fileTime['mon']]))
				array_push($toDelete, $file);
			else
				$year[$fileTime['year']][$fileTime['mon']] = true;
		}
	}
	return $toDelete;
}

function replaceJSContentIn($content) {
	if (preg_match ("/(.*?)<!--DOWNLOAD-INSERT-FILE:\"(.*?)\"--><script\s+type=\"text\/javascript\">(.*)/ms", $content,$matches)) {
		$front = $matches[1];
		$js = $matches[2];
		$tail = $matches[3];
		if (preg_match ("/<\/script>(.*)/ms", $tail,$matches2)) {
			$tail = $matches2[1];
		}
		$jsContent = "<script type=\"text/javascript\" src=\"$js\"></script>";
		$tail = replaceJSContentIn($tail);
		return($front.$jsContent.$tail);
	}
	else
		return $content;
}

// Check if file_uploads is active in php config
if (ini_get('file_uploads') != '1') {
   echo "Error : File upload is not active in php.ini\n";
   toExit();
}

// var definitions
$uploadDir = './';
$uploadDirError = false;
$backupError = false;
$optionStr = $_POST['UploadPlugin'];
$optionArr=explode(';',$optionStr);
$options = array();
$backupFilename = '';
$filename = $_FILES['userfile']['name'];
$destfile = $filename;

// get options
foreach($optionArr as $o) {
	list($key, $value) = split('=', $o);
	$options[$key] = $value;
}

// debug activated by client
if ($options['debug'] == 1) {
	$DEBUG = true;
}

// authenticate User
if (($AUTHENTICATE_USER)
	&& ((!$options['user']) || (!$options['password']) || ($USERS[$options['user']] != $options['password']))) {
	echo "Error : UserName or Password do not match \n";
	echo "UserName : [".$options['user']. "] Password : [". $options['password'] . "]\n";
	toExit();
}



// make uploadDir
if ($options['uploaddir']) {
	$uploadDir = $options['uploaddir'];
	// path control for uploadDir
    if (!(strpos($uploadDir, "../") === false)) {
        echo "Error: directory to upload specifies a parent folder";
        toExit();
	}
	if (! is_dir($uploadDir)) {
		mkdirs($uploadDir);
	}
	if (! is_dir($uploadDir)) {
		echo "UploadDirError : $uploadDirError - File NOT uploaded !\n";
		toExit();
	}
	if ($uploadDir{strlen($uploadDir)-1} != '/') {
		$uploadDir = $uploadDir . '/';
	}
}
$destfile = $uploadDir . $filename;

// backup existing file
if (file_exists($destfile) && ($options['backupDir'])) {
	if (! is_dir($options['backupDir'])) {
		mkdirs($options['backupDir']);
		if (! is_dir($options['backupDir'])) {
			$backupError = "backup mkdir error";
		}
	}
	$backupFilename = $options['backupDir'].'/'.substr($filename, 0, strrpos($filename, '.'))
				.date('.Ymd.His').substr($filename,strrpos($filename,'.'));
	rename($destfile, $backupFilename) or ($backupError = "rename error");
	// remove overmuch backup
	if ($CLEAN_BACKUP) {
		$toDelete = cleanFiles($options['backupDir'], substr($filename, 0, strrpos($filename, '.')));
		foreach ($toDelete as $file) {
			$f = $options['backupDir'].'/'.$file;
			if($DEBUG) {
				echo "delete : ".$options['backupDir'].'/'.$file."\n";
			}
			unlink($options['backupDir'].'/'.$file);
		}
	}
}

// move uploaded file to uploadDir
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $destfile)) {
	if ($FOLD_JS) {
		// rewrite the file to replace JS content
		$fileContent = file_get_contents ($destfile);
		$fileContent = replaceJSContentIn($fileContent);
		if (!$handle = fopen($destfile, 'w')) {
	         echo "Cannot open file ($destfile)";
	         exit;
	    }
	    if (fwrite($handle, $fileContent) === FALSE) {
	        echo "Cannot write to file ($destfile)";
	        exit;
	    }
	    fclose($handle);
	}

	chmod($destfile, 0644);
	if($DEBUG) {
		echo "Debug mode \n\n";
	}
	if (!$backupError) {
		echo "0 - File successfully loaded in " .$destfile. "\n";
	} else {
		echo "BackupError : $backupError - File successfully loaded in " .$destfile. "\n";
	}
	echo("destfile:$destfile \n");
	if (($backupFilename) && (!$backupError)) {
		echo "backupfile:$backupFilename\n";
	}
	$mtime = filemtime($destfile);
	echo("mtime:$mtime");
}
else {
	echo "Error : " . $_FILES['error']." - File NOT uploaded !\n";

}
toExit();
//}}}
?>
<?php
/***
! User settings
Edit these lines according to your need
***/
//{{{
$AUTHENTICATE_USER = true;	// true | false
$USERS = array(
	'UserName1'=>'Password1',
	'UserName2'=>'Password2',
	'UserName3'=>'Password3'); // set usernames and strong passwords
$DEBUG = false;				// true | false
$CLEAN_BACKUP = true; 		// during backuping a file, remove overmuch backups
error_reporting(E_ERROR | E_WARNING | E_PARSE);
//}}}
/***
!Code
No change needed under
***/
//{{{

/***
 * storeTiddler.php - upload a tiddler to a TiddlyWiki file in this directory
 * version: 1.2.1 - 2008-05-06 - BidiX@BidiX.info
 *
 * tiddler is POST as <FORM> with :
 *	FORM =
 *		title=<the title of the tiddler>
 *		tiddler=<result of externalizeTiddler() : the div in StoreArea format>
 *		[oldTitle=<the previous title of the tiddler>]
 *		[fileName=<tiddlyWiki filename>] (default: index.html)
 *		[backupDir=<backupdir>] (default: .)
 *		[user=<user>] (no default)
 *		[password=<password>] (no default)
 *		[uploadir=<uploaddir>] (default: .)
 *		[debug=1]] (default: false)
 * see :
 *	http://tiddlywiki.bidi.info/#UploadTiddlerPlugin for usage
 *  http://tiddlywiki.bidi.info/#UploadPlugin for parameter descriptions
 * usage :
 *	POST FORM
 *		Update <tiddler> in <fileName> TiddlyWiki
 *	GET
 *		Display a form for
 *
 * Revision history
 * V1.2.1 - 2008-05-06
 * bug correction : Filename is always 'index.html'. The fileName variable isn't used to initialize tiddlyWiki filename.
 * V1.2.0 - 2008-03-23
 * Exclusive lock to serialize rewrite of file
 * V1.1.0 - 2008/03/05
 * Delete tiddler with oldTitle
 * V1.0.0 - 2008/02/24
 * First public Version
 * V0.3.0 - 2008/02/23
 * minor adjustments
 * V0.2.0 - 2008/02/23
 * Correction bug on large regex
 * V0.1.0 - 2008/02/09
 * Initial: First working version
 * V0.0.1 - 2008/02/02
 * Initial: Proof Of Concept
 *
 * Copyright (c) BidiX@BidiX.info 2005-2008
 ***/
//}}}

//{{{

if ($_SERVER['REQUEST_METHOD'] == 'GET') {
	/*
	 * GET Request
	 */
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
		<title>BidiX.info - TiddlyWiki UploadTiddlerPlugin - Store script</title>
	</head>
	<body>
		<p>
		<p>storeTiddler.php V 1.2.0
		<p>BidiX@BidiX.info
		<p>&nbsp;</p>
		<p>&nbsp;</p>
		<p>&nbsp;</p>
		<p align="center">This page is designed to upload a <a href="http://www.tiddlywiki.com/#Tiddler">Tiddler<a>.</p>
		<p align="center">for details see : <a href="http://TiddlyWiki.bidix.info/#HowToUpload">TiddlyWiki.bidix.info/#HowToUpload<a>.</p>
		<hr>
		<form action="storeTiddler.php" method=POST>
			<center>
				<table>
					<tr>
						<td align=RIGHT>Title:</td>
						<td><input type=TEXT name="title" size=80></td>
					</tr>
					<tr>
						<td align=RIGHT>Tiddler (in StoreArea format):</td>
						<td><TEXTAREA NAME="tiddler" COLS=80 ROWS=10>
&lt;div title=&quot;New Tiddler&quot; modifier=&quot;BidiX&quot; created=&quot;200802161401&quot; tags=&quot;test&quot; changecount=&quot;1&quot;&gt;
&lt;pre&gt;Type the text for &#x27;New Tiddler&#x27;&lt;/pre&gt;
&lt;/div&gt;
						</TEXTAREA></td>
					</tr>
					<tr>
						<td align=RIGHT>Old Title:</td>
						<td><input type=TEXT name="oldTitle" size=80 value=''></td>
					</tr>
					<tr>
						<td align=RIGHT>fileName:</td>
						<td><input type=TEXT name="fileName" size=80></td>
					</tr>
					<tr>
						<td align=RIGHT>backupDir:</td>
						<td><input type=TEXT name="backupDir" size=80></td>
					</tr>
					<tr>
						<td align=RIGHT>user:</td>
						<td><input type=TEXT name="user" size=80></td>
					</tr>
					<tr>
						<td align=RIGHT>password:</td>
						<td><input type=TEXT name="password" size=80></td>
					</tr>
					<tr>
						<td align=RIGHT>uploadir:</td>
						<td><input type=TEXT name="uploadir" size=80></td>
					</tr>
					<tr>
						<td align=RIGHT>debug:</td>
						<td><input type=TEXT name="debug" size=80 value=1></td>
					</tr>
				</table>
				<input type=SUBMIT align="CENTER" value="Upload tiddler">
			</center>
		</form>
	</body>
</html>
<?php
exit;
}

/*
 * POST Request
 */

/*
 * Functions included from store.php
 */

	// Recursive mkdir
	function mkdirs($dir) {
		if( is_null($dir) || $dir === "" ){
			return false;
		}
		if( is_dir($dir) || $dir === "/" ){
			return true;
		}
		if( mkdirs(dirname($dir)) ){
			return mkdir($dir);
		}
		return false;
	}

	function toExit() {
		global $DEBUG, $filename, $backupFilename, $options;
		if ($DEBUG) {
			echo ("\nHere is some debugging info : \n");
			echo("\$filename : $filename \n");
			echo("\$backupFilename : $backupFilename \n");
			print ("\$_FILES : \n");
			print_r($_FILES);
			print ("\$options : \n");
			print_r($options);
	}
	exit;
	}

	function ParseTWFileDate($s) {
		// parse date element
		preg_match ( '/^(\d\d\d\d)(\d\d)(\d\d)\.(\d\d)(\d\d)(\d\d)/', $s , $m );
		// make a date object
		$d = mktime($m[4], $m[5], $m[6], $m[2], $m[3], $m[1]);
		// get the week number
		$w = date("W",$d);

		return array(
			'year' => $m[1],
			'mon' => $m[2],
			'mday' => $m[3],
			'hours' => $m[4],
			'minutes' => $m[5],
			'seconds' => $m[6],
			'week' => $w);
	}

	function cleanFiles($dirname, $prefix) {
		$now = getdate();
		$now['week'] = date("W");

		$hours = Array();
		$mday = Array();
		$year = Array();

		$toDelete = Array();

		// need files recent first
		$files = Array();
		($dir = opendir($dirname)) || die ("can't open dir '$dirname'");
		while (false !== ($file = readdir($dir))) {
			if (preg_match("/^$prefix/", $file))
	        array_push($files, $file);
	    }
		$files = array_reverse($files);

		// decides for each file
		foreach ($files as $file) {
			$fileTime = ParseTWFileDate(substr($file,strpos($file, '.')+1,strrpos($file,'.') - strpos($file, '.') -1));
			if (($now['year'] == $fileTime['year']) &&
				($now['mon'] == $fileTime['mon']) &&
				($now['mday'] == $fileTime['mday']) &&
				($now['hours'] == $fileTime['hours']))
					continue;
			elseif (($now['year'] == $fileTime['year']) &&
				($now['mon'] == $fileTime['mon']) &&
				($now['mday'] == $fileTime['mday'])) {
					if (isset($hours[$fileTime['hours']]))
						array_push($toDelete, $file);
					else
						$hours[$fileTime['hours']] = true;
				}
			elseif 	(($now['year'] == $fileTime['year']) &&
				($now['mon'] == $fileTime['mon'])) {
					if (isset($mday[$fileTime['mday']]))
						array_push($toDelete, $file);
					else
						$mday[$fileTime['mday']] = true;
				}
			else {
				if (isset($year[$fileTime['year']][$fileTime['mon']]))
					array_push($toDelete, $file);
				else
					$year[$fileTime['year']][$fileTime['mon']] = true;
			}
		}
		return $toDelete;
	}

/*
 * End Functions included from store.php
 */

/*
 * parse and print a TiddlyWiki file
 */

Function readTiddlyWiki($tw) {
	if (preg_match ("/(.*?<div id=\"storeArea\">.*?)(<div.*)/ms", $tw,$matches)) {
		$head = $matches[1];
		$h = $matches[2];
		$tiddlers = array();
		while (preg_match ("/(.*?)(<div title=\"(.*?)\".*?<\/div>)(.*)/ms", $h,$matches)) {
			$h=$matches[4];
			$tiddlers[$matches[3]] = $matches[2];
		}
		$tail = ltrim($h);
	}
	else {
		echo("The file '$file' isn't a valid TiddlyWiki");
		toExit();
	}
	return array($head, $tiddlers ,$tail);
}

Function writeTiddlyWiki($head,$tiddlers,$tail) {
	$content = $head;
	sort($tiddlers);
	foreach ($tiddlers as $t => $c) {
		$content .= $c . "\n";
	}
	$content .= $tail;
	return $content;
}

// var definitions
$uploadDir = './';
$uploadDirError = false;
$backupError = false;
$backupFilename = '';
$filename = "index.html";
$destfile = $filename;

$options = $_POST; // for store.php name compatibility

// debug activated by client
if ($options['debug'] == 1) {
	$DEBUG = true;
}


// authenticate User
if (($AUTHENTICATE_USER)
	&& ((!$options['user']) || (!$options['password']) || ($USERS[$options['user']] != $options['password']))) {
	echo "Error : UserName or Password do not match \n";
	echo "UserName : [".$options['user']. "] Password : [". $options['password'] . "]\n";
	toExit();
}


if ($options['fileName'])
	$filename = $options['fileName'];


// make uploadDir
if ($options['uploaddir']) {
	$uploadDir = $options['uploaddir'];
	// path control for uploadDir
    if (!(strpos($uploadDir, "../") === false)) {
        echo "Error: directory to upload specifies a parent folder";
        toExit();
	}
	if (! is_dir($uploadDir)) {
		mkdirs($uploadDir);
	}
	if (! is_dir($uploadDir)) {
		echo "UploadDirError : $uploadDirError - File NOT uploaded !\n";
		toExit();
	}
	if ($uploadDir{strlen($uploadDir)-1} != '/') {
		$uploadDir = $uploadDir . '/';
	}
}
$destfile = $uploadDir . $filename;

// backup existing file
if (file_exists($destfile) && ($options['backupDir'])) {
	if (! is_dir($options['backupDir'])) {
		mkdirs($options['backupDir']);
		if (! is_dir($options['backupDir'])) {
			$backupError = "backup mkdir error";
		}
	}
	$backupFilename = $options['backupDir'].'/'.substr($filename, 0, strrpos($filename, '.'))
				.date('.Ymd.His').substr($filename,strrpos($filename,'.'));
	copy($destfile, $backupFilename) or ($backupError = "rename error");
	// remove overmuch backup
	if ($CLEAN_BACKUP) {
		$toDelete = cleanFiles($options['backupDir'], substr($filename, 0, strrpos($filename, '.')));
		foreach ($toDelete as $file) {
			$f = $options['backupDir'].'/'.$file;
			if($DEBUG) {
				echo "delete : ".$options['backupDir'].'/'.$file."\n";
			}
			unlink($options['backupDir'].'/'.$file);
		}
	}
}


if (file_exists($destfile)) {
	$f = fopen($destfile,'r+');
	if (flock($f, LOCK_EX)) {
		while (!feof($f)) {
		    $contents .= fread($f, 8192);
		}
		list($head,$tiddlers,$tail) = readTiddlyWiki($contents);
		$title = $_POST['title'];
		$oldTitle = $_POST['oldTitle'];
		if ($oldTitle && ($title != $oldTitle)) {
			unset($tiddlers[$oldTitle]);
		}
		$tiddlers[$title] = stripslashes($_POST['tiddler']);
		$contents = writeTiddlyWiki($head,$tiddlers,$tail);
		if (!rewind($f)) {
			echo("rewind error");
			toExit();
		}
		if (!ftruncate($f, 0)) {
			echo("ftruncate error");
			toExit();
		};
		if (!fwrite($f, $contents)) {
			echo("fwrite error");
			toExit();
		}
		fclose($f);	// fclose also unlock the file
		if($DEBUG) {
			echo "Debug mode \n\n";
		}
		if (!$backupError) {
			echo "0 - Tiddler successfully updated in " .$destfile. "\n";
		} else {
			echo "BackupError : $backupError - Tiddler successfully updated in " .$destfile. "\n";
		}
		echo("destfile:$destfile \n");
		if (($backupFilename) && (!$backupError)) {
			echo "backupfile:$backupFilename\n";
		}
		$mtime = filemtime($destfile);
		echo("mtime:$mtime");

	}
	else {
		echo "Error : '" . $filename . "' couldn't be locked - File NOT updated !\n";
	}
}
else {
	echo "Error : '" . $filename . "' doesn't exist - File NOT updated !\n";
}
toExit();

?>
UploadPlugin uses the following sequence for finding parameters :
#''macro'' parameters
#''Options'' saved in cookies
#''Plugin'' default values
!Options used by UploadPlugin
| Option | Value | Default |
|Upload Username: |<<option txtUploadUserName>>| |
|Upload Password: |<<option pasUploadPassword>>| |
|Url of the UploadService script: |<<option txtUploadStoreUrl urlInput>>| store.php |
|Relative Directory where to store the file: |<<option txtUploadDir urlInput>>| . (the script directory) |
|Filename of the uploaded file: |<<option txtUploadFilename urlInput>>| index.html |
|Directory to backup file on webserver^^(1)^^: |<<option txtUploadBackupDir urlInput>>| //null// (none/no backup) |
|Log in UploadLog |<<option chkUploadLog>> Trace Upload| true |
|Maximum of lines in UploadLog |<<option txtUploadLogMaxLine>>| 10 |

^^(1)^^No backup if Backup Directory is empty, the previous file will be overwritten. Use a '.' to backup in the script directory.

<<upload>> with these options.

!Upload Macro parameters
{{{
<<upload [storeUrl [toFilename [backupDir [uploadDir [username]]]]]>>
 Optional positional parameters can be passed to overwrite UploadOptions.
}}}

!UploadToFile Macro Macro parameters
{{{
<<uploadTofile [filename [tiddlerTitle]]>>
 tiddlerTitle, filename: if omitted the title of the current tiddler
}}}

<<uploadToFile allowedsites.txt allowedsites.txt>>