config.options.chkHttpReadOnly = false;\n
SyncOutlookNotes [[Welcome to your tiddlyspot.com site!]] GettingStarted
/***\n|''Name:''|LegacyStrikeThroughPlugin|\n|''Description:''|Support for legacy (pre 2.1) strike through formatting|\n|''Version:''|1.0.1|\n|''Date:''|Jul 21, 2006|\n|''Source:''|http://www.tiddlywiki.com/#LegacyStrikeThroughPlugin|\n|''Author:''|MartinBudden (mjbudden (at) gmail (dot) com)|\n|''License:''|[[BSD open source license]]|\n|''CoreVersion:''|2.1.0|\n|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|\n\n***/\n\n//{{{\n\n// Ensure that the LegacyStrikeThrough Plugin is only installed once.\nif(!version.extensions.LegacyStrikeThroughPlugin)\n {\n version.extensions.LegacyStrikeThroughPlugin = true;\n\nconfig.formatters.push(\n{\n name: "legacyStrikeByChar",\n match: "==",\n termRegExp: /(==)/mg,\n element: "strike",\n handler: config.formatterHelpers.createElementAndWikify\n});\n\n} // end of "install only once"\n//}}}\n
<<SyncOutlookNotes>>
/***\n|Name|SyncOutlookNotes|\n|Source|http://syncoutlooknotes.tiddlyspot.com/#SyncOutlookNotes|\n|Version|1.6.0|\n|Author|Mike Miller mikem@3cats.us|\n|License|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|\n|~CoreVersion|2.1|\n|Type|plugin|\n|Requires|Internet Explorer, Outlook - see "Compatibility" below|\n|Overrides||\n|Description|Synchronizes your entire TiddlyWiki with the Notes in your Outlook program|\n\n!!!!!News\nScript is about as clean as it's going to get for a while. No major updates under development.\n\n!!!!!Usage\n<<<\n{{{<<syncOutlookNotes>>}}}\n\nThe SyncOutlookNotes macro is designed to allow you to keep your outlook notes completely in sync with a single TiddlyWiki. Basically, install the plugin and run it and it will automagically sync the two databased with each other. The "Macros" tag is used to identify the documentation and a LastSyncDate tiddler which is currently necessary for correct operation.\n\nIf you need to delete a tiddler/note, deleting it in both places works, or you can put the string TWDELETE at the beginning of a tiddler (2nd line in an outlook note) and it will be deleted on the next sync.\n\nThis is all fairly new code, so there are probably some siginificant bugs and issues with some usage models. See the SyncOutlookNotesToDo for some of these items. If you are interested in fixing the problems or taking care of some of the ToDo items, your help would certainly be welcome. Please contact mikem@computer.org.\n\nAlso, as this is new, there has not been a lot of testing done on different pairings of the application - please be sure to check the SyncOutlookNotesCompatibility chart and let Mike know if you have tried a combination that isn't already on the chart.\n\n<<<\n!!!!!Examples:\n<<<\n{{{<<<syncOutlookNotes verbose=1>>>}}}\nThis gives you a a nice little button to use. Click on it to synchronize: <<syncOutlookNotes verbose=1>>\n\n{{{<<<syncOutlookNotes>>>}}}\nIf you want to not get messages, call it without the 'verbose' switch. I strongly recommend using verbose until you are certain that it behaves the way you want <<syncOutlookNotes>>. It will always give one message at the end about updating the LastSyncDate so you know it ran correctly.\n\nYou can set up the macro to only sync tiddlers/notes with specific tags/categories or exclude specific tags/categories of tiddlers/notes. By default, the macro will sync everything. If you explicitly provide some tags/categories, then it will only sync those. You can exclude tiddlers/notes from being synced based on their tags/categories by adding a "not=tag" where "tag" is the tag/category that you don't want synced. The "not=" overrides everything else, so if you have a tiddler/note with two tags, one being explicitly synced and one not, the tiddler will *not* be synced. Examples:\n\n{{{<<<syncOutlookNotes not=NOSYNC>>>}}}\nSync everything that doesn't have the NOSYNC tag/category: <<syncOutlookNotes not=NOSYNC>>\n\n{{{<<<syncOutlookNotes Work Job>>>}}}\nSync all tiddler/notes with the tag/category "Work" or "Job": <<syncOutlookNotes Work>>\n\n{{{<<<syncOutlookNotes noNewOutlook=Personal>>>}}}\nSync all tiddlers/notes, but don't create Outlook notes for tiddlers that have the tag "Personal". This is useful for situations where you want the TiddlyWiki to contain more entries than the Outlook notes, but still want to be able to create and edit entries in Outlook. In particular, this is great for working around the 256-note scripting limitation that appears to exist in Outlook 2003: <<syncOutlookNotes noNewOutlook=Personal>>\n\n{{{<<<syncOutlookNotes verbose=1 GTD Personal not=systemConfig not=Log Work Macros not=Home>>>}}}\nSync the GTD, Personal, Work and Macros tag/categories unless they are also in one of the systemComfig Log or Home tag/categories (and be verbose): <<syncOutlookNotes verbose=1 GTD Personal not=systemConfig not=Log Work Macros not=Home>>\n\n<<<\n!!!!!Development ToDo List:\n<<<\nThere's a number of limitations and issues with this code. Some of which can be fixed. It would be nice to get these fixed soon (but nobody is working on them):\n* There seems to be a limit of slightly over 256 tiddlers/notes that can be synced. If the list gets larger than that, we apppear to hit some kind of limitation in Outlook that results in the hypercryptic message "One or more items in the folder you synchronized do not match." in the javascript console. I work around this problem using the 'noNewOutlook=' feature to automatically create an archive in TiddlyWiki that isn't synced back to the notes.\n* Get it working in FireFox (Mike is stumped)\n* The dates for creating/modifying the notes and tiddlers are set to the time of the sync instead of coming from the other side\n* Do we want a "Rename" function?\n* Get better error messages/return value checking for key functions (like calls to get the initial outlook objects). If you know how to do this, send Mike a pointer to basic docs.\nIssues that it's unclear if it is worth fixing or how to fix:\n* Changing just the tags doesn't update the "last modified time" in a tiddler, so it confuses the sync.\n* If you save something within 1 minute of doing a sync, the next sync might not recgonize that the item has changed. This is largely because how we store the LastSyncTime doesn't have a sub-minute granularity.\n* It would be nice to have a correctly functioning delete code instead of doing the 'TWDELETE' thing...\n* Should switch over to using the ServerAdaptorMechanism when that gets cleaned up and solid in TiddlyWiki 2.2 (or whenever).\n<<<\n!!!!!Installation\n<<<\nImport (or copy/paste) the following tiddlers into your document:\n''SyncOutlookNotes'' (tagged with <<tag systemConfig>>)\n<<<\n!!!!!Compatibility\n<<<\nWhat works:\n\nWe assume a Core TiddlyWiki install of 2.1 or later\n\n| |IE 6.0 |FireFox 2.x|\n|Outlook 2003 + XP SP2 |Yes - Was author's environment |No - issue with ActiveXObject Not Defined - Mike doesn't know how to fix this|\n|Outlook 2007 + XP SP2 |Yes - Author's current environment | |\n\nAnything not in the chart is unknown - please email Mike at mikem@3cats.us if you find things working or not working with particular versions.\n\nAlso, if you are getting an objectError before any of the alert boxes come up when running the script, you may be running into a security issue. McAfee Host Intrusion Prevention has a rule that is invoked if Microsoft Visual Studio 2005 is installed that prevents ActiveX objects in Internet Explorer from calling Office apps (it's a legitimate known security problem). This prevents the script from getting access to Office and it dies. You can tell if this is occuring by checking the console of the Host Intrusion Prevention tool and it will show things in the Activity Log panel that match up with your attempts to run the script.\n\nMy solution was to uninstall MS Visual Studio, but that's probably not a reasonable workaround for most people who have it installed. If you can edit the rules in McAfee's code that would be better. Due to IT policies in my work environment, that isn't possible for me.\n<<<\n!!!!!Revision History\n<<<\n''2008.01.02 [1.6.0]'' Added noNewOutlook= option.\n''2007.12.18 [1.5.1]'' Added some more stuff for verbose operation, documented the 256 entry limitation and fixed a few minor comment details.\n''2007.03.12 [1.5.0]'' Added options for selective syncing and fixed bug in verbose=1 behavior (didn't work)\n''2007.03.05 [1.4.0]'' Changed default behavior to not do as many alerts and added parameter to give old behavior\n''2007.03.09 [1.3.0]'' Changed behavior to create button, fixed bad bug in 1.2.1 that prevented tiddlers created from a new Outlook note from being saved.\n''2007.03.03 [1.2.1]'' Switched to using saveTiddler from set and now using the default username instead of Mike for modifier.\n''2007.03.02 [1.2.0]'' Eliminated O(n**2) behavior in sync and other performance improvements\n''2007.03.02 [1.1.2]'' Modified for performance improvements (fewer calls to Outlook) and updated docs slightly\n''2007.03.01 [1.1.1]'' Updated docs slightly\n''2007.03.01 [1.1.0]'' Added documentation\n''2007.02.28 [1.0.0]'' Created. Very, very messy\n<<<\n!!!!!Credits\n<<<\nThis feature developed by Mike Miller (http://3cats.us/)\n<<<\n!!!!!Code\n\n***/\n\n//{{{\n\nversion.extensions.syncOutlookNotes = {major: 1, minor: 6, revision: 0, date: new Date(2008,1,2)};\n\nconfig.macros.syncOutlookNotes = \n{\ntext: "sync to Outlook notes",\ntooltip: "automatically and bidirectionally sync notes in Outlook with the tiddlers in this document"\n};\n\nconfig.macros.syncOutlookNotes.handler = function(place, macroName, params, wikifier, paramString, tiddler){\n var moretext = "";\n for(var i=0;i<params.length;i++) {\n moretext += " " + params[i];\n }\n var btn = createTiddlyButton(place, this.text +moretext , this.tooltip, onClickSyncOutlookNotes);\n btn.params = params//.join(" ");\n};\n\nwindow.onClickSyncOutlookNotes=function(e)\n{\n/**\n * Outlook Constants\n * Thanks to: http://www.winscripter.com/WSH/MSOffice/93.aspx\n **/\n\n/* OlDefaultFolders Constants*/\nvar olFolderDeletedItems = 3;\nvar olFolderOutbox = 4;\nvar olFolderSentMail = 5;\nvar olFolderInbox = 6;\nvar olFolderCalendar = 9;\nvar olFolderContacts = 10;\nvar olFolderJournal = 11;\nvar olFolderNotes = 12;\nvar olFolderTasks = 13;\n\n/* OlItems Constants */\nvar olMailItem = 0;\nvar olAppointmentItem = 1;\nvar olContactItem = 2;\nvar olTaskItem = 3;\nvar olJournalItem = 4;\nvar olNoteItem = 5;\nvar olPostItem = 6;\n\n/* OlNoteColor Constants */\nvar olBlue = 0;\nvar olGreen = 1;\nvar olPink = 2;\nvar olYellow = 3;\nvar olWhite = 4;\n\n/* params are stashed in the button */\nif (!e) var e = window.event; var btn=resolveTarget(e);\n\n/* process the params */\nvar verbose = 0;\nvar syncList = new Array;\nvar noSyncList = new Array;\nvar noNewOutlook = new Array;\nvar notRE = /^not=(\sw+)$/;\nvar noNewOutlookRE = /^noNewOutlook=(\sw+)$/;\n\nvar params = btn.params//.split(" ");\nfor(var i=0;i < params.length;i++) {\n if(params[i] == "verbose=1") {\n verbose = 1;\n }else if ((tempMatchArray = notRE.exec(params[i])) != undefined) {\n noSyncList.push(tempMatchArray[1]);\n } else if ((tempMatchArray = noNewOutlookRE.exec(params[i])) != undefined) {\n noNewOutlook.push(tempMatchArray[1]);\n } else {\n syncList.push(params[i]);\n }\n}\n\nif(verbose == 1) {alert("parsed options:\sn\stverbose="+verbose+"\sn\stnoSyncList= "+ noSyncList.join(" ") + "\sn\stsyncList= " + syncList.join(" ") + "\sn\stnoNewOutlook= " + noNewOutlook.join(" "));}\n\nvar updateSyncDate = 1;\nvar nowDate = new Date();\nvar lastSyncDate = new Date(1); // just after the beginning of the epoch\nvar syncDateTiddler;// = store.getTiddler("LastSyncDate");\nif(store.getTiddler("LastSyncDate")) {\n if(verbose == 1) { alert("using LastSyncDate tiddler");}\n syncDateTiddler = store.getTiddler("LastSyncDate");\n lastSyncDate = syncDateTiddler.modified;\n} else {\n alert("Warning: LastSyncDate tiddler does not exist (OK if this is first sync) - creating");\n //var newT = store.createTiddler("LastSyncDate");\n store.saveTiddler("LastSyncDate", "LastSyncDate", "This tiddler's update time is when we synced last - so please don't update it manually as it may cause incorrect sync behavior.", config.options.txtUserName, lastSyncDate, "Macros");\n syncDateTiddler = store.getTiddler("LastSyncDate");\n}\n \nvar theApp = new ActiveXObject("Outlook.Application");\nvar theNameSpace = theApp.GetNamespace("MAPI");\ntheApp.ActiveExplorer.CurrentFolder = theNameSpace.GetDefaultFolder(olFolderNotes);\nvar theFolder = theApp.ActiveExplorer.CurrentFolder;\nvar theOItems = theFolder.Items;\nvar thecount = theFolder.Items.Count;\nvar theTList = store.getTiddlers("title");\nvar oTitleArray = new Array();\n\nif(verbose == 1) { alert("Moving Outlook items to TiddlyWiki (and sync of objects in both locations)"); }\nfor(var oi=1; oi<= theFolder.Items.Count; oi++) {\n var haveMatch = 0;\n // We first check if this is a delete operation as denoted by\n // having a TWDELETE at the of the beginning of the text\n\n var oNote = theFolder.Items(oi);\n var oNoteString = oNote.Body;\n oNoteString = oNoteString.substring(oNoteString.indexOf("\sn")+1, oNoteString.length);\n\n oTitle = oNote.Subject;\n //if(verbose == 1) { alert("Working Outlook item: " + oTitle); }\n oTitleArray.push(oTitle); // cache the outlook note title for use later\n oNoteCategories = oNote.Categories.split(", ");\n if(syncList.length >0) {\n var match=0;\n for(var synci=0;synci<syncList.length;synci++) {\n for(var ocatsi=0;ocatsi<oNoteCategories.length;ocatsi++) {\n if(syncList[synci] == oNoteCategories[ocatsi]) {\n match=1;\n }\n }\n }\n if(match==0) {continue;}\n } // end synclist check\n\n if(noSyncList.length >0) {\n var match=0;\n for(var synci=0;synci<noSyncList.length;synci++) {\n for(var ocatsi=0;ocatsi<oNoteCategories.length;ocatsi++) {\n if(noSyncList[synci] == oNoteCategories[ocatsi]) {\n match=1;\n }\n }\n }\n if(match==1){continue;}\n } // end noSyncList check\n\n var deleteRE = /^TWDELETE/;\n\n if(deleteRE.test(oNoteString)) {\n if(verbose==1){ alert("Deleting entry: " + oTitle); }\n oNote.Delete();\n store.removeTiddler(oTitle);\n continue; // skip the rest of processing the outlook object.\n }// end the if(delete) function \n\n for(var ti=0; ti<theTList.length;ti++) {\n var twNote = theTList[ti];\n if(twNote.title == oTitle) {\n haveMatch = 1;\n\n // perform sync operation\n\n // first, we figure out if they are different. There's some weridness\n // between the data in outlook and in the Wiki, so for simplicity\n // we strip all whitespace and compare those strings. This means\n // that whitespace changes won't be synced, even if they are relevent\n // to the wiki markup, which is unfortunate.\n // We also have to include the tags so that if the user changes the\n // tags, it updates both sides. This is tricky as the order might\n // not be preserved in outlook or tiddlywiki\n //\n // The body compare also has the nice side effect that for those of\n // us who have "delete everything unmodified after 6 months", we can\n // have a script to update the dates in outlook and it doesn't\n // propigate those new dates to the TiddlyWiki.\n //\n\n oNoteString = oNoteString + oNote.Categories.split(", ").sort().join(" ");\n var whiteRE = /\ss+/g;\n tNoteString = twNote.text + twNote.tags.sort().join(" ");\n var compOString = oNoteString.replace(whiteRE, "");\n var compNString = tNoteString.replace(whiteRE, "");\n if(compOString != compNString) {\n\n // OK - so the text is different, try and figure out which one to\n // use based on the last modified dates.\n if((twNote.modified > lastSyncDate) &&\n ( oNote.LastModificationTime > lastSyncDate)) {\n // both have changed and they don't match - we've got an issue\n alert("Both Outlook and TiddlyWiki have changed the note titled: "+twNote.title+ "\snScript is not doing anything (not intellegent enough). Manually merge, delete one of the two and resync\snDates:\sn\stLastSyncDate:\st" + lastSyncDate + "\sn\stTiddlyWiki:\st" + twNote.modified + "\sn\stOutlook:\st" + oNote.LastModificationTime);\n return;\n }else if (twNote.modified > lastSyncDate) {\n // just the wiki changed, move the body over\n if(verbose==1){alert("just the wiki changed for " + twNote.title);}\n var newBodyString = twNote.title;\n newBodyString = newBodyString + "\sn" + twNote.text;\n oNote.Body = newBodyString;\n oNote.Categories = twNote.tags.sort().join(", ");\n oNote.Save();\n }else if (oNote.LastModificationTime > lastSyncDate) {\n // just outlook changed, move the body over\n if(verbose==1){alert("just the Outlook note changed for " + twNote.title);}\n// var oNoteString = oNote.Body;\n// oNoteString = oNoteString.substring(oNoteString.indexOf("\sn")+1,oNoteString.length);\n oCatString = oNote.Categories;\n commasRE = /\s,\ss*/g;\n store.saveTiddler(oNote.Subject, oNote.Subject, oNoteString, config.options.txtUserName, nowDate, oCatString.replace(commasRE," "));\n } else {\n // dates not helpful - need user help\n alert("Sync dates are not newer, but text does not match for the note/tiddler titled: "+twNote.title+ "\snNot doing anything. Please manually merge if necessary, delete one of the two and resync\sn---\sn" + compNString + "\sn---\sn" + compOString + "\sn---\sn");\n return;\n } // end of the date checking if/else clause\n } // end of if(text different)\n break;\n } // end if(i/j titles match)\n} // end inner for loop\n\n if(haveMatch == 0) {\n if(verbose==1){alert("Creating new Tiddler for: " + oNote.Subject);}\n oCatString = oNote.Categories;\n commasRE = /\s,\ss*/g;\n store.saveTiddler(oNote.Subject, oNote.Subject, oNoteString, config.options.txtUserName, nowDate, oCatString.replace(commasRE," "));\n } // end if(no match)\nhaveMatch =0;\n} // end outer for loop\n\nif(verbose==1){alert("now syncing new TiddlyWiki objects to Outlook\sn"+oTitleArray.length);}\nfor(var ti=0; ti<theTList.length;ti++) {\n var haveMatch = 0;\n var twNote = theTList[ti];\n var twCats = twNote.tags;\n\n if(syncList.length >0) {\n var match=0;\n for(var synci=0;synci<syncList.length;synci++) {\n for(var catsi=0;catsi<twCats.length;catsi++) {\n if(syncList[synci] == twCats[catsi]) {\n match=1;\n }\n }\n }\n if(match==0) {continue;}\n } // end synclist check\n\n // check the noSyncList\n if(noSyncList.length >0) {\n var match=0\n for(var synci=0;synci<noSyncList.length;synci++) {\n for(var catsi=0;catsi<twCats.length;catsi++) {\n if(noSyncList[synci] == twCats[catsi]) {\n match=1;\n }\n }\n }\n if(match==1){continue;}\n } // end noSyncList check\n\n // check to see if we are skipping creation of Outlook objects for a particular tag type\n if(noNewOutlook.length >0) {\n var match=0\n for(var synci=0;synci<noNewOutlook.length;synci++) {\n for(var catsi=0;catsi<twCats.length;catsi++) {\n if(noNewOutlook[synci] == twCats[catsi]) {\n match=1;\n }\n }\n }\n if(match==1){continue;}\n } // end noSyncList check\n\n\n // We first check if this is a delete operation as denoted by\n // having a TWDELETE at the end of the beginning of the text\n var twTitle = twNote.title;\n var twText = twNote.text;\n var deleteRE = /^TWDELETE/;\n if(deleteRE.test(twText)) {\n if(verbose==1){alert("Deleting entry: " + twTitle);}\n store.removeTiddler(twTitle);\n for(var oi=1;oi<=theFolder.Items.Count; oi++) {\n if(twTitle == theFolder.Items(oi).Subject) {\n theFolder.Items(oi).Delete();\n break;\n } // if match titles\n } // end for loop\n continue; // skip the rest of processing the outlook object.\n } // end the if(delete) function \n\n if(oTitleArray.indexOf(twTitle) != -1) {\n haveMatch = 1;\n // we've already synced matching objects in the previous loop...\n }\n if(haveMatch == 0) {\n if(verbose==1){alert("creating new note with name: " + theTList[ti].title);}\n var newNote = theApp.CreateItem(olNoteItem);\n var newBodyString = theTList[ti].title;\n newBodyString = newBodyString + "\sn" + theTList[ti].text;\n newNote.Body = newBodyString;\n newNote.Categories = theTList[ti].tags.sort().join(", ");\n newNote.Save();\n }\n}\n\nalert('Cross-sync done, updating LastSyncDate');\nnowDate = new Date();\n// Add one minute because the date stored doesn't include seconds.\nvar msDate = nowDate.valueOf() + 60000;\nnowDate = new Date(msDate);\nstore.saveTiddler("LastSyncDate", "LastSyncDate", "This tiddler's update time is when we synced last - so please don't update it manually as it may cause incorrect sync behavior.", config.options.txtUserName, nowDate);\n\nsaveChanges();\n\nreturn true;\n}; // end handler: function()\n\n//}}}
What works:\n\nWe assume a TiddlyWiki install of 2.1.2 or later\n\n||IE 6.0|FireFox 2.x|\n|Outlook 2003 + XP SP2 |Yes - Mike's environment |No - issue with ActiveXObject Not Defined, Mike is looking into it|\n\nAnything not in the chart is unknown - please email Mike at mikem@3cats.us if you find things working or not working with particular versions.
config.macros.SyncOutlookNotes = \n{\n handler: function()\n {\n/**\n * Outlook Constants\n * http://www.winscripter.com\n * http://www.winscripter.com/WSH/MSOffice/93.aspx\n **/\n\n/* OlDefaultFolders Constants*/\nvar olFolderDeletedItems = 3;\nvar olFolderOutbox = 4;\nvar olFolderSentMail = 5;\nvar olFolderInbox = 6;\nvar olFolderCalendar = 9;\nvar olFolderContacts = 10;\nvar olFolderJournal = 11;\nvar olFolderNotes = 12;\nvar olFolderTasks = 13;\n\n/* OlFlagStatus Constants */\nvar olNoFlag = 0;\nvar olFlagComplete = 1;\nvar olFlagMarked = 2;\n\n/* OlFolderDisplayMode Constants */\nvar olFolderDisplayNormal = 0;\nvar olFolderDisplayFolderOnly = 1;\nvar olFolderDisplayNoNavigation = 2;\n\n/* OlImportance Constants */\nvar olImportanceLow = 0;\nvar olImportanceNormal = 1;\nvar olImportanceHigh = 2;\n\n/* OlInspectorClose Constants */\nvar olSave = 0;\nvar olDiscard = 1;\nvar olPromptForSave = 2;\n\n/* OlItems Constants */\nvar olMailItem = 0;\nvar olAppointmentItem = 1;\nvar olContactItem = 2;\nvar olTaskItem = 3;\nvar olJournalItem = 4;\nvar olNoteItem = 5;\nvar olPostItem = 6;\n\n/* OlJournalRecipientsType Constants */\nvar olAssociatedContact = 1; \n/* OlNoteColor Constants */\nvar olBlue = 0;\nvar olGreen = 1;\nvar olPink = 2;\nvar olYellow = 3;\nvar olWhite = 4;\n/* OlSaveAsType Constants */\nvar olTXT = 0;\nvar olRTF = 1;\nvar olTemplate = 2;\nvar olMSG = 3;\nvar olDoc = 4;\n\n/* OlSensitivity Constants */\nvar olNormal = 0;\nvar olPersonal = 1;\nvar olPrivate = 2;\nvar olConfidential = 3;\n\n/* OlUserPropertyType Constants */\nvar olText = 1;\nvar olNumber = 3;\nvar olDateTime = 5;\nvar olYesNo = 6;\nvar olDuration = 7;\nvar olKeywords = 11;\nvar olPercent = 12;\nvar olCurrency = 14;\nvar olFormula = 18;\nvar olCombination = 19;\n\nvar updateSyncDate = 1;\nvar lastSyncDate = new Date(1); // just after the beginning of the epoch\nvar syncDateTiddler;// = store.getTiddler("LastSyncDate");\nif(store.getTiddler("LastSyncDate")) {\n //alert("using LastSyncDate tiddler");\n syncDateTiddler = store.getTiddler("LastSyncDate");\n lastSyncDate = syncDateTiddler.modified;\n} else {\n alert("LastSyncDate tiddler does not exist - creating");\n var newT = store.createTiddler("LastSyncDate");\n var nowDate = new Date();\n newT.set("LastSyncDate", "This tiddler's update time is when we synced last - so please don't update it manually as it may cause incorrect sync behavior.", 'Mike', lastSyncDate, "Macros", nowDate);\n syncDateTiddler = store.getTiddler("LastSyncDate");\n}\n \nvar theApp = new ActiveXObject("Outlook.Application");\nvar theNameSpace = theApp.GetNamespace("MAPI");\ntheApp.ActiveExplorer.CurrentFolder = theNameSpace.GetDefaultFolder(olFolderNotes)\nvar theFolder = theApp.ActiveExplorer.CurrentFolder;\nvar theOItems = theFolder.Items;\nvar thecount = theFolder.Items.Count;\nvar theTList = store.getTiddlers("title");\n\nalert("Moving Outlook items to TiddlyWiki (and sync of objects in both locations)");\nfor(var oi=1; oi<= theFolder.Items.Count; oi++) {\n var haveMatch = 0;\n // We first check if this is a delete operation as denoted by\n // having a TWDELETE at the of the beginning of the text\n\n var oNoteString = theFolder.Items(oi).Body;\n oNoteString = oNoteString.substring(oNoteString.indexOf("\sn")+1,oNoteString.length);\n\n oTitle = theFolder.Items(oi).Subject;\n var deleteRE = /^TWDELETE/;\n\n if(deleteRE.test(oNoteString)) {\n alert("Deleting entry: " + oTitle);\n theFolder.Items(oi).Delete();\n store.removeTiddler(oTitle);\n continue; // skip the rest of processing the outlook object.\n }// end the if(delete) function \n\n for(var ti=0; ti<theTList.length;ti++) {\n if(theTList[ti].title == theFolder.Items(oi).Subject) {\n haveMatch = 1;\n\n // perform sync operation\n\n // first, we figure out if they are different. There's some weridness\n // between the data in outlook and in the Wiki, so for simplicity\n // we strip all whitespace and compare those strings. This means\n // that whitespace changes won't be synced, even if they are relevent\n // to the wiki markup, which is unfortunate.\n // We also have to include the tags so that if the user changes the\n // tags, it updates both sides. This is tricky as the order might\n // not be preserved in outlook or tiddlywiki\n //\n // The body compare also has the nice side effect that for those of\n // us who have "delete everything unmodified after 6 months", we can\n // have a script to update the dates in outlook and it doesn't\n // propigate those new dates to the TiddlyWiki.\n //\n\n oNoteString = oNoteString + theFolder.Items(oi).Categories.split(", ").sort().join(" ")\n var whiteRE = /\ss+/g;\n tNoteString = theTList[ti].text + theTList[ti].tags.sort().join(" ");\n var compOString = oNoteString.replace(whiteRE, "");\n var compNString = tNoteString.replace(whiteRE, "");\n if(compOString != compNString) {\n\n // OK - so the text is different, try and figure out which one to\n // use based on the last modified dates.\n if((theTList[ti].modified > lastSyncDate) &&\n ( theFolder.Items(oi).LastModificationTime > lastSyncDate)) {\n // both have changed and they don't match - we've got an issue\n alert("Both Outlook and TiddlyWiki have changed the note titled: "+theTList[ti].title+ "\snScript is not doing anything (not intellegent enough). Manually merge, delete one of the two and resync\snDates:\sn\stLastSyncDate:\st" + lastSyncDate + "\sn\stTiddlyWiki:\st" + theTList[ti].modified + "\sn\stOutlook:\st" + theFolder.Items(oi).LastModificationTime);\n return;\n }else if (theTList[ti].modified > lastSyncDate) {\n // just the wiki changed, move the body over\n alert("just the wiki changed for " + theTList[ti].title);\n var newBodyString = theTList[ti].title\n newBodyString = newBodyString + "\sn" + theTList[ti].text;\n theFolder.Items(oi).Body = newBodyString;\n theFolder.Items(oi).Categories = theTList[ti].tags.sort().join(", ");\n theFolder.Items(oi).Save();\n }else if (theFolder.Items(oi).LastModificationTime > lastSyncDate) {\n // just outlook changed, move the body over\n alert("just the Outlook note changed for " + theTList[ti].title);\n var oNoteString = theFolder.Items(oi).Body;\n oNoteString = oNoteString.substring(oNoteString.indexOf("\sn")+1,oNoteString.length);\n oCatString = theFolder.Items(oi).Categories;\n commasRE = /\s,\ss*/g;\n var nowDate = new Date();\n theTList[ti].set(theFolder.Items(oi).Subject, oNoteString, 'Mike', nowDate, oCatString.replace(commasRE," "));\n } else {\n // dates not helpful - need user help\n alert("Sync dates are not newer, but text does not match for the note/tiddler titled: "+theTList[ti].title+ "\snNot doing anything. Please manually merge if necessary, delete one of the two and resync\sn---\sn" + compNString + "\sn---\sn" + compOString + "\sn---\sn");\n return;\n } // end of the date checking if/else clause\n } // end of if(text different)\n break;\n } // end if(i/j titles match)\n} // end inner for loop\n\n if(haveMatch == 0) {\n alert("Creating new Tiddler for: " + theFolder.Items(oi).Subject);\n var newT = store.createTiddler(theFolder.Items(oi).Subject);\n var nowDate = new Date();\n var oNoteString = theFolder.Items(oi).Body;\n oNoteString = oNoteString.substring(oNoteString.indexOf("\sn")+1,oNoteString.length);\n oCatString = theFolder.Items(oi).Categories;\n commasRE = /\s,\ss*/g;\n newT.set(theFolder.Items(oi).Subject, oNoteString, 'Mike', nowDate, oCatString.replace(commasRE," "), nowDate);\n } // end if(no match)\nhaveMatch =0;\n} // end outer for loop\n\nalert("now syncing new TiddlyWiki objects to Outlook");\nfor(var ti=0; ti<theTList.length;ti++) {\n var haveMatch = 0;\n // We first check if this is a delete operation as denoted by\n // having a TWDELETE at the end of the beginning of the text\n var twTitle = theTList[ti].title;\n var twText = theTList[ti].text;\n var deleteRE = /^TWDELETE/;\n if(deleteRE.test(twText)) {\n alert("Deleting entry: " + twTitle);\n store.removeTiddler(twTitle);\n for(var oi=1;oi<=theFolder.Items.Count; oi++) {\n if(twTitle == theFolder.Items(oi).Subject) {\n theFolder.Items(oi).Delete();\n break;\n } // if match titles\n } // end for loop\n continue; // skip the rest of processing the outlook object.\n } // end the if(delete) function \n\n for(var oi=1;oi<=theFolder.Items.Count; oi++) {\n if(twTitle == theFolder.items(oi).subject) {\n haveMatch = 1;\n // we've already synced matching objects in the previous loop...\n break;\n }\n }\n if(haveMatch == 0) {\n alert("creating new note with name: " + theTList[ti].title);\n var newNote = theApp.CreateItem(olNoteItem);\n var newBodyString = theTList[ti].title\n newBodyString = newBodyString + "\sn" + theTList[ti].text;\n newNote.Body = newBodyString;\n newNote.Categories = theTList[ti].tags.sort().join(", ");\n newNote.Save();\n }\n}\n\n alert('Cross-sync done, updating LastSyncDate');\n var nowDate = new Date();\n // Add one minute because the date stored doesn't include seconds.\n var msDate = nowDate.valueOf() + 60000\n nowDate = new Date(msDate);\n syncDateTiddler.set("LastSyncDate", "This tiddler's update time is when we synced last - so please don't update it manually as it may cause incorrect sync behavior.", 'Mike', nowDate);\n }\n}\n
There's a number of interesting limitations-issues with this code. Some of which can be fixed:\n\n* Use the default author for tiddlers (unclear how to get to this)\n* Get it working in FireFox (Mike is currently stumped)\n* More efficient (fewer) calls to Outlook by not using the list all the time\n* Use hashes instead of the double-nested loop to reduce O(n**2) behavior\n* The dates for creating/modifying the notes and tiddlers are set to the time of the sync instead of coming from the other side\n* Would be nice to have a global "verbosity" switch in the script\n* Do we want a "Rename" function?\n* Reformat the code to be readable\nIssues:\n* Changing just the tags doesn't update the "last modified time" in a tiddler, so it confuses the sync.\n* If you save something within 1 minute of doing a sync, the next sync might not recgonize that the item has changed. This is largely because how we store the LastSyncTime doesn't have a sub-minute granularity.\n
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |\n| 28/2/2007 15:56:57 | YourName | [[/|http://syncoutlooknotes.tiddlyspot.com/]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 28/2/2007 16:4:13 | Mike | [[/|http://syncoutlooknotes.tiddlyspot.com/]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 28/2/2007 16:51:25 | Mike | [[/|http://syncoutlooknotes.tiddlyspot.com/]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 1/3/2007 16:57:59 | Mike | [[/|http://syncoutlooknotes.tiddlyspot.com/]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 1/3/2007 17:46:58 | Mike | [[/|http://syncoutlooknotes.tiddlyspot.com/]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 2/3/2007 15:17:18 | Mike | [[/|http://syncoutlooknotes.tiddlyspot.com/]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 2/3/2007 16:16:3 | Mike | [[/|http://syncoutlooknotes.tiddlyspot.com/]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 3/3/2007 14:23:58 | Mike | [[/|http://syncoutlooknotes.tiddlyspot.com/]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 3/3/2007 14:24:49 | Mike | [[/|http://syncoutlooknotes.tiddlyspot.com/]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 5/3/2007 22:16:54 | Mike | [[/|http://syncoutlooknotes.tiddlyspot.com/#SyncOutlookNotes]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 9/3/2007 10:9:14 | Mike | [[/|http://syncoutlooknotes.tiddlyspot.com/]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 12/3/2007 22:23:29 | Mike | [[/|http://syncoutlooknotes.tiddlyspot.com/]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 18/12/2007 17:14:0 | Mikem42 | [[/|http://syncoutlooknotes.tiddlyspot.com/#SyncOutlookNotes]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 2/1/2008 13:21:7 | Mikem42 | [[/|http://syncoutlooknotes.tiddlyspot.com/#SyncOutlookNotes]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 27/1/2009 15:56:15 | YourName | [[/|http://syncoutlooknotes.tiddlyspot.com/#SyncOutlookNotes]] | [[store.cgi|http://syncoutlooknotes.tiddlyspot.com/store.cgi]] | . | index.html | . |
/***\n|''Name:''|UploadPlugin|\n|''Description:''|Save to web a TiddlyWiki|\n|''Version:''|3.4.4|\n|''Date:''|Sep 30, 2006|\n|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|\n|''Documentation:''|http://tiddlywiki.bidix.info/#UploadDoc|\n|''Author:''|BidiX (BidiX (at) bidix (dot) info)|\n|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|\n|''~CoreVersion:''|2.0.0|\n|''Browser:''|Firefox 1.5; InternetExplorer 6.0; Safari|\n|''Include:''|config.lib.file; config.lib.log; config.lib.options; PasswordTweak|\n|''Require:''|[[UploadService|http://tiddlywiki.bidix.info/#UploadService]]|\n***/\n//{{{\nversion.extensions.UploadPlugin = {\n major: 3, minor: 4, revision: 4, \n date: new Date(2006,8,30),\n source: 'http://tiddlywiki.bidix.info/#UploadPlugin',\n documentation: 'http://tiddlywiki.bidix.info/#UploadDoc',\n author: 'BidiX (BidiX (at) bidix (dot) info',\n license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',\n coreVersion: '2.0.0',\n browser: 'Firefox 1.5; InternetExplorer 6.0; Safari'\n};\n//}}}\n\n////+++!![config.lib.file]\n\n//{{{\nif (!config.lib) config.lib = {};\nif (!config.lib.file) config.lib.file= {\n author: 'BidiX',\n version: {major: 0, minor: 1, revision: 0}, \n date: new Date(2006,3,9)\n};\nconfig.lib.file.dirname = function (filePath) {\n var lastpos;\n if ((lastpos = filePath.lastIndexOf("/")) != -1) {\n return filePath.substring(0, lastpos);\n } else {\n return filePath.substring(0, filePath.lastIndexOf("\s\s"));\n }\n};\nconfig.lib.file.basename = function (filePath) {\n var lastpos;\n if ((lastpos = filePath.lastIndexOf("#")) != -1) \n filePath = filePath.substring(0, lastpos);\n if ((lastpos = filePath.lastIndexOf("/")) != -1) {\n return filePath.substring(lastpos + 1);\n } else\n return filePath.substring(filePath.lastIndexOf("\s\s")+1);\n};\nwindow.basename = function() {return "@@deprecated@@";};\n//}}}\n////===\n\n////+++!![config.lib.log]\n\n//{{{\nif (!config.lib) config.lib = {};\nif (!config.lib.log) config.lib.log= {\n author: 'BidiX',\n version: {major: 0, minor: 1, revision: 1}, \n date: new Date(2006,8,19)\n};\nconfig.lib.Log = function(tiddlerTitle, logHeader) {\n if (version.major < 2)\n this.tiddler = store.tiddlers[tiddlerTitle];\n else\n this.tiddler = store.getTiddler(tiddlerTitle);\n if (!this.tiddler) {\n this.tiddler = new Tiddler();\n this.tiddler.title = tiddlerTitle;\n this.tiddler.text = "| !date | !user | !location |" + logHeader;\n this.tiddler.created = new Date();\n this.tiddler.modifier = config.options.txtUserName;\n this.tiddler.modified = new Date();\n if (version.major < 2)\n store.tiddlers[tiddlerTitle] = this.tiddler;\n else\n store.addTiddler(this.tiddler);\n }\n return this;\n};\n\nconfig.lib.Log.prototype.newLine = function (line) {\n var now = new Date();\n var newText = "| ";\n newText += now.getDate()+"/"+(now.getMonth()+1)+"/"+now.getFullYear() + " ";\n newText += now.getHours()+":"+now.getMinutes()+":"+now.getSeconds()+" | ";\n newText += config.options.txtUserName + " | ";\n var location = document.location.toString();\n var filename = config.lib.file.basename(location);\n if (!filename) filename = '/';\n newText += "[["+filename+"|"+location + "]] |";\n this.tiddler.text = this.tiddler.text + "\sn" + newText;\n this.addToLine(line);\n};\n\nconfig.lib.Log.prototype.addToLine = function (text) {\n this.tiddler.text = this.tiddler.text + text;\n this.tiddler.modifier = config.options.txtUserName;\n this.tiddler.modified = new Date();\n if (version.major < 2)\n store.tiddlers[this.tiddler.tittle] = this.tiddler;\n else {\n store.addTiddler(this.tiddler);\n story.refreshTiddler(this.tiddler.title);\n store.notify(this.tiddler.title, true);\n }\n if (version.major < 2)\n store.notifyAll(); \n};\n//}}}\n////===\n\n////+++!![config.lib.options]\n\n//{{{\nif (!config.lib) config.lib = {};\nif (!config.lib.options) config.lib.options = {\n author: 'BidiX',\n version: {major: 0, minor: 1, revision: 0}, \n date: new Date(2006,3,9)\n};\n\nconfig.lib.options.init = function (name, defaultValue) {\n if (!config.options[name]) {\n config.options[name] = defaultValue;\n saveOptionCookie(name);\n }\n};\n//}}}\n////===\n\n////+++!![PasswordTweak]\n\n//{{{\nversion.extensions.PasswordTweak = {\n major: 1, minor: 0, revision: 3, date: new Date(2006,8,30),\n type: 'tweak',\n source: 'http://tiddlywiki.bidix.info/#PasswordTweak'\n};\n//}}}\n/***\n!!config.macros.option\n***/\n//{{{\nconfig.macros.option.passwordCheckboxLabel = "Save this password on this computer";\nconfig.macros.option.passwordType = "password"; // password | text\n\nconfig.macros.option.onChangeOption = function(e)\n{\n var opt = this.getAttribute("option");\n var elementType,valueField;\n if(opt) {\n switch(opt.substr(0,3)) {\n case "txt":\n elementType = "input";\n valueField = "value";\n break;\n case "pas":\n elementType = "input";\n valueField = "value";\n break;\n case "chk":\n elementType = "input";\n valueField = "checked";\n break;\n }\n config.options[opt] = this[valueField];\n saveOptionCookie(opt);\n var nodes = document.getElementsByTagName(elementType);\n for(var t=0; t<nodes.length; t++) \n {\n var optNode = nodes[t].getAttribute("option");\n if (opt == optNode) \n nodes[t][valueField] = this[valueField];\n }\n }\n return(true);\n};\n\nconfig.macros.option.handler = function(place,macroName,params)\n{\n var opt = params[0];\n if(config.options[opt] === undefined) {\n return;}\n var c;\n switch(opt.substr(0,3)) {\n case "txt":\n c = document.createElement("input");\n c.onkeyup = this.onChangeOption;\n c.setAttribute ("option",opt);\n c.className = "txtOptionInput "+opt;\n place.appendChild(c);\n c.value = config.options[opt];\n break;\n case "pas":\n // input password\n c = document.createElement ("input");\n c.setAttribute("type",config.macros.option.passwordType);\n c.onkeyup = this.onChangeOption;\n c.setAttribute("option",opt);\n c.className = "pasOptionInput "+opt;\n place.appendChild(c);\n c.value = config.options[opt];\n // checkbox link with this password "save this password on this computer"\n c = document.createElement("input");\n c.setAttribute("type","checkbox");\n c.onclick = this.onChangeOption;\n c.setAttribute("option","chk"+opt);\n c.className = "chkOptionInput "+opt;\n place.appendChild(c);\n c.checked = config.options["chk"+opt];\n // text savePasswordCheckboxLabel\n place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));\n break;\n case "chk":\n c = document.createElement("input");\n c.setAttribute("type","checkbox");\n c.onclick = this.onChangeOption;\n c.setAttribute("option",opt);\n c.className = "chkOptionInput "+opt;\n place.appendChild(c);\n c.checked = config.options[opt];\n break;\n }\n};\n//}}}\n/***\n!! Option cookie stuff\n***/\n//{{{\nwindow.loadOptionsCookie_orig_PasswordTweak = window.loadOptionsCookie;\nwindow.loadOptionsCookie = function()\n{\n var cookies = document.cookie.split(";");\n for(var c=0; c<cookies.length; c++) {\n var p = cookies[c].indexOf("=");\n if(p != -1) {\n var name = cookies[c].substr(0,p).trim();\n var value = cookies[c].substr(p+1).trim();\n switch(name.substr(0,3)) {\n case "txt":\n config.options[name] = unescape(value);\n break;\n case "pas":\n config.options[name] = unescape(value);\n break;\n case "chk":\n config.options[name] = value == "true";\n break;\n }\n }\n }\n};\n\nwindow.saveOptionCookie_orig_PasswordTweak = window.saveOptionCookie;\nwindow.saveOptionCookie = function(name)\n{\n var c = name + "=";\n switch(name.substr(0,3)) {\n case "txt":\n c += escape(config.options[name].toString());\n break;\n case "chk":\n c += config.options[name] ? "true" : "false";\n // is there an option link with this chk ?\n if (config.options[name.substr(3)]) {\n saveOptionCookie(name.substr(3));\n }\n break;\n case "pas":\n if (config.options["chk"+name]) {\n c += escape(config.options[name].toString());\n } else {\n c += "";\n }\n break;\n }\n c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";\n document.cookie = c;\n};\n//}}}\n/***\n!! Initializations\n***/\n//{{{\n// define config.options.pasPassword\nif (!config.options.pasPassword) {\n config.options.pasPassword = 'defaultPassword';\n window.saveOptionCookie('pasPassword');\n}\n// since loadCookies is first called befor password definition\n// we need to reload cookies\nwindow.loadOptionsCookie();\n//}}}\n////===\n\n////+++!![config.macros.upload]\n\n//{{{\nconfig.macros.upload = {\n accessKey: "U",\n formName: "UploadPlugin",\n contentType: "text/html;charset=UTF-8",\n defaultStoreScript: "store.php"\n};\n\n// only this two configs need to be translated\nconfig.macros.upload.messages = {\n aboutToUpload: "About to upload TiddlyWiki to %0",\n backupFileStored: "Previous file backuped in %0",\n crossDomain: "Certainly a cross-domain isue: access to an other site isn't allowed",\n errorDownloading: "Error downloading",\n errorUploadingContent: "Error uploading content",\n fileLocked: "Files is locked: You are not allowed to Upload",\n fileNotFound: "file to upload not found",\n fileNotUploaded: "File %0 NOT uploaded",\n mainFileUploaded: "Main TiddlyWiki file uploaded to %0",\n passwordEmpty: "Unable to upload, your password is empty",\n urlParamMissing: "url param missing",\n rssFileNotUploaded: "RssFile %0 NOT uploaded",\n rssFileUploaded: "Rss File uploaded to %0"\n};\n\nconfig.macros.upload.label = {\n promptOption: "Save and Upload this TiddlyWiki with UploadOptions",\n promptParamMacro: "Save and Upload this TiddlyWiki in %0",\n saveLabel: "save to web", \n saveToDisk: "save to disk",\n uploadLabel: "upload" \n};\n\nconfig.macros.upload.handler = function(place,macroName,params){\n // parameters initialization\n var storeUrl = params[0];\n var toFilename = params[1];\n var backupDir = params[2];\n var uploadDir = params[3];\n var username = params[4];\n var password; // for security reason no password as macro parameter\n var label;\n if (document.location.toString().substr(0,4) == "http")\n label = this.label.saveLabel;\n else\n label = this.label.uploadLabel;\n var prompt;\n if (storeUrl) {\n prompt = this.label.promptParamMacro.toString().format([this.toDirUrl(storeUrl, uploadDir, username)]);\n }\n else {\n prompt = this.label.promptOption;\n }\n createTiddlyButton(place, label, prompt, \n function () {\n config.macros.upload.upload(storeUrl, toFilename, uploadDir, backupDir, username, password); \n return false;}, \n null, null, this.accessKey);\n};\nconfig.macros.upload.UploadLog = function() {\n return new config.lib.Log('UploadLog', " !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |" );\n};\nconfig.macros.upload.UploadLog.prototype = config.lib.Log.prototype;\nconfig.macros.upload.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir, backupDir) {\n var line = " [[" + config.lib.file.basename(storeUrl) + "|" + storeUrl + "]] | ";\n line += uploadDir + " | " + toFilename + " | " + backupDir + " |";\n this.newLine(line);\n};\nconfig.macros.upload.UploadLog.prototype.endUpload = function() {\n this.addToLine(" Ok |");\n};\nconfig.macros.upload.basename = config.lib.file.basename;\nconfig.macros.upload.dirname = config.lib.file.dirname;\nconfig.macros.upload.toRootUrl = function (storeUrl, username)\n{\n return root = (this.dirname(storeUrl)?this.dirname(storeUrl):this.dirname(document.location.toString()));\n}\nconfig.macros.upload.toDirUrl = function (storeUrl, uploadDir, username)\n{\n var root = this.toRootUrl(storeUrl, username);\n if (uploadDir && uploadDir != '.')\n root = root + '/' + uploadDir;\n return root;\n}\nconfig.macros.upload.toFileUrl = function (storeUrl, toFilename, uploadDir, username)\n{\n return this.toDirUrl(storeUrl, uploadDir, username) + '/' + toFilename;\n}\nconfig.macros.upload.upload = function(storeUrl, toFilename, uploadDir, backupDir, username, password)\n{\n // parameters initialization\n storeUrl = (storeUrl ? storeUrl : config.options.txtUploadStoreUrl);\n toFilename = (toFilename ? toFilename : config.options.txtUploadFilename);\n backupDir = (backupDir ? backupDir : config.options.txtUploadBackupDir);\n uploadDir = (uploadDir ? uploadDir : config.options.txtUploadDir);\n username = (username ? username : config.options.txtUploadUserName);\n password = config.options.pasUploadPassword; // for security reason no password as macro parameter\n if (!password || password === '') {\n alert(config.macros.upload.messages.passwordEmpty);\n return;\n }\n if (storeUrl === '') {\n storeUrl = config.macros.upload.defaultStoreScript;\n }\n if (config.lib.file.dirname(storeUrl) === '') {\n storeUrl = config.lib.file.dirname(document.location.toString())+'/'+storeUrl;\n }\n if (toFilename === '') {\n toFilename = config.lib.file.basename(document.location.toString());\n }\n\n clearMessage();\n // only for forcing the message to display\n if (version.major < 2)\n store.notifyAll();\n if (!storeUrl) {\n alert(config.macros.upload.messages.urlParamMissing);\n return;\n }\n // Check that file is not locked\n if (window.BidiX && BidiX.GroupAuthoring && BidiX.GroupAuthoring.lock) {\n if (BidiX.GroupAuthoring.lock.isLocked() && !BidiX.GroupAuthoring.lock.isMyLock()) {\n alert(config.macros.upload.messages.fileLocked);\n return;\n }\n }\n \n var log = new this.UploadLog();\n log.startUpload(storeUrl, toFilename, uploadDir, backupDir);\n if (document.location.toString().substr(0,5) == "file:") {\n saveChanges();\n }\n var toDir = config.macros.upload.toDirUrl(storeUrl, toFilename, uploadDir, username);\n displayMessage(config.macros.upload.messages.aboutToUpload.format([toDir]), toDir);\n this.uploadChanges(storeUrl, toFilename, uploadDir, backupDir, username, password);\n if(config.options.chkGenerateAnRssFeed) {\n //var rssContent = convertUnicodeToUTF8(generateRss());\n var rssContent = generateRss();\n var rssPath = toFilename.substr(0,toFilename.lastIndexOf(".")) + ".xml";\n this.uploadContent(rssContent, storeUrl, rssPath, uploadDir, '', username, password, \n function (responseText) {\n if (responseText.substring(0,1) != '0') {\n displayMessage(config.macros.upload.messages.rssFileNotUploaded.format([rssPath]));\n }\n else {\n var toFileUrl = config.macros.upload.toFileUrl(storeUrl, rssPath, uploadDir, username);\n displayMessage(config.macros.upload.messages.rssFileUploaded.format(\n [toFileUrl]), toFileUrl);\n }\n // for debugging store.php uncomment last line\n //DEBUG alert(responseText);\n });\n }\n return;\n};\n\nconfig.macros.upload.uploadChanges = function(storeUrl, toFilename, uploadDir, backupDir, \n username, password) {\n var original;\n if (document.location.toString().substr(0,4) == "http") {\n original = this.download(storeUrl, toFilename, uploadDir, backupDir, username, password);\n return;\n }\n else {\n // standard way : Local file\n \n original = loadFile(getLocalPath(document.location.toString()));\n if(window.Components) {\n // it's a mozilla browser\n try {\n netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");\n var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]\n .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);\n converter.charset = "UTF-8";\n original = converter.ConvertToUnicode(original);\n }\n catch(e) {\n }\n }\n }\n //DEBUG alert(original);\n this.uploadChangesFrom(original, storeUrl, toFilename, uploadDir, backupDir, \n username, password);\n};\n\nconfig.macros.upload.uploadChangesFrom = function(original, storeUrl, toFilename, uploadDir, backupDir, \n username, password) {\n var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it\n var endSaveArea = '</d' + 'iv>';\n // Locate the storeArea div's\n var posOpeningDiv = original.indexOf(startSaveArea);\n var posClosingDiv = original.lastIndexOf(endSaveArea);\n if((posOpeningDiv == -1) || (posClosingDiv == -1))\n {\n alert(config.messages.invalidFileError.format([document.location.toString()]));\n return;\n }\n var revised = original.substr(0,posOpeningDiv + startSaveArea.length) + \n allTiddlersAsHtml() + "\sn\st\st" +\n original.substr(posClosingDiv);\n var newSiteTitle;\n if(version.major < 2){\n newSiteTitle = (getElementText("siteTitle") + " - " + getElementText("siteSubtitle")).htmlEncode();\n } else {\n newSiteTitle = (wikifyPlain ("SiteTitle") + " - " + wikifyPlain ("SiteSubtitle")).htmlEncode();\n }\n\n revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");\n revised = revised.replaceChunk("<!--PRE-HEAD-START--"+">","<!--PRE-HEAD-END--"+">","\sn" + store.getTiddlerText("MarkupPreHead","") + "\sn");\n revised = revised.replaceChunk("<!--POST-HEAD-START--"+">","<!--POST-HEAD-END--"+">","\sn" + store.getTiddlerText("MarkupPostHead","") + "\sn");\n revised = revised.replaceChunk("<!--PRE-BODY-START--"+">","<!--PRE-BODY-END--"+">","\sn" + store.getTiddlerText("MarkupPreBody","") + "\sn");\n revised = revised.replaceChunk("<!--POST-BODY-START--"+">","<!--POST-BODY-END--"+">","\sn" + store.getTiddlerText("MarkupPostBody","") + "\sn");\n\n var response = this.uploadContent(revised, storeUrl, toFilename, uploadDir, backupDir, \n username, password, function (responseText) {\n if (responseText.substring(0,1) != '0') {\n alert(responseText);\n displayMessage(config.macros.upload.messages.fileNotUploaded.format([getLocalPath(document.location.toString())]));\n }\n else {\n if (uploadDir !== '') {\n toFilename = uploadDir + "/" + config.macros.upload.basename(toFilename);\n } else {\n toFilename = config.macros.upload.basename(toFilename);\n }\n var toFileUrl = config.macros.upload.toFileUrl(storeUrl, toFilename, uploadDir, username);\n if (responseText.indexOf("destfile:") > 0) {\n var destfile = responseText.substring(responseText.indexOf("destfile:")+9, \n responseText.indexOf("\sn", responseText.indexOf("destfile:")));\n toFileUrl = config.macros.upload.toRootUrl(storeUrl, username) + '/' + destfile;\n }\n else {\n toFileUrl = config.macros.upload.toFileUrl(storeUrl, toFilename, uploadDir, username);\n }\n displayMessage(config.macros.upload.messages.mainFileUploaded.format(\n [toFileUrl]), toFileUrl);\n if (backupDir && responseText.indexOf("backupfile:") > 0) {\n var backupFile = responseText.substring(responseText.indexOf("backupfile:")+11, \n responseText.indexOf("\sn", responseText.indexOf("backupfile:")));\n toBackupUrl = config.macros.upload.toRootUrl(storeUrl, username) + '/' + backupFile;\n displayMessage(config.macros.upload.messages.backupFileStored.format(\n [toBackupUrl]), toBackupUrl);\n }\n var log = new config.macros.upload.UploadLog();\n log.endUpload();\n store.setDirty(false);\n // erase local lock\n if (window.BidiX && BidiX.GroupAuthoring && BidiX.GroupAuthoring.lock) {\n BidiX.GroupAuthoring.lock.eraseLock();\n // change mtime with new mtime after upload\n var mtime = responseText.substr(responseText.indexOf("mtime:")+6);\n BidiX.GroupAuthoring.lock.mtime = mtime;\n }\n \n \n }\n // for debugging store.php uncomment last line\n //DEBUG alert(responseText);\n }\n );\n};\n\nconfig.macros.upload.uploadContent = function(content, storeUrl, toFilename, uploadDir, backupDir, \n username, password, callbackFn) {\n var boundary = "---------------------------"+"AaB03x"; \n var request;\n try {\n request = new XMLHttpRequest();\n } \n catch (e) { \n request = new ActiveXObject("Msxml2.XMLHTTP"); \n }\n if (window.netscape){\n try {\n if (document.location.toString().substr(0,4) != "http") {\n netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');}\n }\n catch (e) {}\n } \n //DEBUG alert("user["+config.options.txtUploadUserName+"] password[" + config.options.pasUploadPassword + "]");\n // compose headers data\n var sheader = "";\n sheader += "--" + boundary + "\sr\snContent-disposition: form-data; name=\s"";\n sheader += config.macros.upload.formName +"\s"\sr\sn\sr\sn";\n sheader += "backupDir="+backupDir\n +";user=" + username \n +";password=" + password\n +";uploaddir=" + uploadDir;\n // add lock attributes to sheader\n if (window.BidiX && BidiX.GroupAuthoring && BidiX.GroupAuthoring.lock) {\n var l = BidiX.GroupAuthoring.lock.myLock;\n sheader += ";lockuser=" + l.user\n + ";mtime=" + l.mtime\n + ";locktime=" + l.locktime;\n }\n sheader += ";;\sr\sn"; \n sheader += "\sr\sn" + "--" + boundary + "\sr\sn";\n sheader += "Content-disposition: form-data; name=\s"userfile\s"; filename=\s""+toFilename+"\s"\sr\sn";\n sheader += "Content-Type: " + config.macros.upload.contentType + "\sr\sn";\n sheader += "Content-Length: " + content.length + "\sr\sn\sr\sn";\n // compose trailer data\n var strailer = new String();\n strailer = "\sr\sn--" + boundary + "--\sr\sn";\n //strailer = "--" + boundary + "--\sr\sn";\n var data;\n data = sheader + content + strailer;\n //request.open("POST", storeUrl, true, username, password);\n try {\n request.open("POST", storeUrl, true); \n }\n catch(e) {\n alert(config.macros.upload.messages.crossDomain + "\snError:" +e);\n exit;\n }\n request.onreadystatechange = function () {\n if (request.readyState == 4) {\n if (request.status == 200)\n callbackFn(request.responseText);\n else\n alert(config.macros.upload.messages.errorUploadingContent + "\snStatus: "+request.status.statusText);\n }\n };\n request.setRequestHeader("Content-Length",data.length);\n request.setRequestHeader("Content-Type","multipart/form-data; boundary="+boundary);\n request.send(data); \n};\n\n\nconfig.macros.upload.download = function(uploadUrl, uploadToFilename, uploadDir, uploadBackupDir, \n username, password) {\n var request;\n try {\n request = new XMLHttpRequest();\n } \n catch (e) { \n request = new ActiveXObject("Msxml2.XMLHTTP"); \n }\n try {\n if (uploadUrl.substr(0,4) == "http") {\n netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");\n }\n else {\n netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");\n }\n } catch (e) { }\n //request.open("GET", document.location.toString(), true, username, password);\n try {\n request.open("GET", document.location.toString(), true);\n }\n catch(e) {\n alert(config.macros.upload.messages.crossDomain + "\snError:" +e);\n exit;\n }\n \n request.onreadystatechange = function () {\n if (request.readyState == 4) {\n if(request.status == 200) {\n config.macros.upload.uploadChangesFrom(request.responseText, uploadUrl, \n uploadToFilename, uploadDir, uploadBackupDir, username, password);\n }\n else\n alert(config.macros.upload.messages.errorDownloading.format(\n [document.location.toString()]) + "\snStatus: "+request.status.statusText);\n }\n };\n request.send(null);\n};\n\n//}}}\n////===\n\n////+++!![Initializations]\n\n//{{{\nconfig.lib.options.init('txtUploadStoreUrl','store.php');\nconfig.lib.options.init('txtUploadFilename','');\nconfig.lib.options.init('txtUploadDir','');\nconfig.lib.options.init('txtUploadBackupDir','');\nconfig.lib.options.init('txtUploadUserName',config.options.txtUserName);\nconfig.lib.options.init('pasUploadPassword','');\nsetStylesheet(\n ".pasOptionInput {width: 11em;}\sn"+\n ".txtOptionInput.txtUploadStoreUrl {width: 25em;}\sn"+\n ".txtOptionInput.txtUploadFilename {width: 25em;}\sn"+\n ".txtOptionInput.txtUploadDir {width: 25em;}\sn"+\n ".txtOptionInput.txtUploadBackupDir {width: 25em;}\sn"+\n "",\n "UploadOptionsStyles");\nconfig.shadowTiddlers.UploadDoc = "[[Full Documentation|http://tiddlywiki.bidix.info/l#UploadDoc ]]\sn"; \nconfig.options.chkAutoSave = false; saveOptionCookie('chkAutoSave');\n\n//}}}\n////===\n\n////+++!![Core Hijacking]\n\n//{{{\nconfig.macros.saveChanges.label_orig_UploadPlugin = config.macros.saveChanges.label;\nconfig.macros.saveChanges.label = config.macros.upload.label.saveToDisk;\n\nconfig.macros.saveChanges.handler_orig_UploadPlugin = config.macros.saveChanges.handler;\n\nconfig.macros.saveChanges.handler = function(place)\n{\n if ((!readOnly) && (document.location.toString().substr(0,4) != "http"))\n createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);\n};\n\n//}}}\n////===\n
This document is a ~TiddlyWiki from tiddlyspot.com. A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.\n\n@@font-weight:bold;font-size:1.3em;color:#444; //What now?// &nbsp;&nbsp;@@ Before you can save any changes, you need to enter your password in the form below. Then configure privacy and other site settings at your [[control panel|http://syncoutlooknotes.tiddlyspot.com/controlpanel]] (your control panel username is //syncoutlooknotes//).\n<<tiddler tiddlyspotControls>>\n@@font-weight:bold;font-size:1.3em;color:#444; //Working online// &nbsp;&nbsp;@@ You can edit this ~TiddlyWiki right now, and save your changes using the "save to web" button in the column on the right.\n\n@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// &nbsp;&nbsp;@@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick. You can make changes and save them locally without being connected to the Internet. When you're ready to sync up again, just click "upload" and your ~TiddlyWiki will be saved back to tiddlyspot.com.\n\n@@font-weight:bold;font-size:1.3em;color:#444; //Help!// &nbsp;&nbsp;@@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]]. Also visit [[TiddlyWiki Guides|http://tiddlywikiguides.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help. If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].\n\n@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// &nbsp;&nbsp;@@ We hope you like using your tiddlyspot.com site. Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions.
| tiddlyspot password:|<<option pasUploadPassword>>|\n| site management:|<<upload http://syncoutlooknotes.tiddlyspot.com/store.cgi index.html . . syncoutlooknotes>>//(requires tiddlyspot password)//<<br>>[[control panel|http://syncoutlooknotes.tiddlyspot.com/controlpanel]], [[download (go offline)|http://syncoutlooknotes.tiddlyspot.com/download]]|\n| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[announcements|http://announce.tiddlyspot.com/]], [[blog|http://tiddlyspot.com/blog/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|