Skip to content

Commit 9d8df6b

Browse files
author
ahanniga
committed
Added file upload plugin ImportToExternalFile by saq
1 parent 0c1d2b7 commit 9d8df6b

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"tiddlers": {
3+
"$:/config/sq/OverwriteBinaryFiles": {
4+
"title": "$:/config/sq/OverwriteBinaryFiles",
5+
"created": "20210331123857536",
6+
"modified": "20210331125534389",
7+
"type": "text/vnd.tiddlywiki",
8+
"text": "yes"
9+
},
10+
"$:/config/sq/SaveAsExternalFile": {
11+
"title": "$:/config/sq/SaveAsExternalFile",
12+
"created": "20210330153139725",
13+
"modified": "20210331194622196",
14+
"type": "text/vnd.tiddlywiki",
15+
"text": "yes"
16+
},
17+
"$:/plugins/sq/ImportToExternalFile/Buttons/externalize": {
18+
"title": "$:/plugins/sq/ImportToExternalFile/Buttons/externalize",
19+
"caption": "{{$:/plugins/sq/ImportToExternalFile/images/save-to-file}} Save as external file",
20+
"description": "Save as external file",
21+
"tags": "$:/tags/ViewToolbar",
22+
"list-after": "$:/core/ui/Buttons/more-tiddler-actions",
23+
"text": "\\whitespace trim\n<$button message=\"tm-externalise-tiddler\" tooltip=\"Save as external file\" aria-label=\"Save as external file\" class=<<tv-config-toolbar-class>>>\n<$list filter=\"[<tv-config-toolbar-icons>match[yes]]\">\n{{$:/plugins/sq/ImportToExternalFile/images/save-to-file}}\n</$list>\n<$list filter=\"[<tv-config-toolbar-text>match[yes]]\">\n<span class=\"tc-btn-text\">\n<$text text=\"Save as external file\"/>\n</span>\n</$list>\n</$button>"
24+
},
25+
"$:/plugins/sq/ImportToExternalFile/images/save-to-file": {
26+
"title": "$:/plugins/sq/ImportToExternalFile/images/save-to-file",
27+
"type": "text/vnd.tiddlywiki",
28+
"text": "<svg width=\"22pt\" height=\"22pt\" class=\"tc-image-button\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.5a.5.5 0 0 1 1 0V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0zm-.354 15.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 14.293V11h-1v3.293l-2.146-2.147a.5.5 0 0 0-.708.708l3 3z\"/></svg>"
29+
},
30+
"$:/plugins/sq/ImportToExternalFile/server-route-upload.js": {
31+
"title": "$:/plugins/sq/ImportToExternalFile/server-route-upload.js",
32+
"text": "/*\\\ntitle: $:/plugins/sq/ImportToExternalFile/server-route-upload.js\ntype: application/javascript\nmodule-type: route\n\nPOST /^\\/api\\/upload/\n\nUpload media\n\n\\*/\n(function() {\n\n/*jslint node: true, browser: true */\n/*global $tw: false */\n\"use strict\";\n\nexports.method = \"POST\";\n\nexports.path = new RegExp('^\\/api\\/upload');\nexports.bodyFormat = \"stream\";\n\n\tconst fs = require('fs')\n\tconst path = require('path')\n\tconst buffer = require('buffer')\n\nexports.handler = function(request,response,state) {\n\n\t\n\tlet body = ''\n\t\n\trequest.on('data', function(chunk){\n\t\tbody += chunk;\n\t\t// We limit the size of an upload to 10mb for now.\n\t\tif(body.length > 10e6) {\n\t\t\tresponse.writeHead(413, {'Content-Type': 'text/plain'}).end();\n\t\t\trequest.connection.destroy();\n\t\t}\n\t});\n\t\n\trequest.on('end', function() {\n\t\ttry {\n\t\t\tlet bodyData = JSON.parse(body)\n\n\t\t\tconst filesPath = path.resolve($tw.boot.wikiTiddlersPath, \"../files\");\n\n //config option overwrite existing?\n\t\n /* \n var xfilepath = $tw.utils.generateTiddlerFilepath(bodyData.tiddler.fields.title,{\n directory: filesPath\n });\n //var ext = path.extname(originalpath);\n //xfilepath = xfilepath.substring(0,xfilepath.length - ext.length);\n \n \n // 1) try to increment filename before extension.\n // 2) don't encode / in file path so can specify a subdir\n xfilepath = path.join(filesPath, bodyData.tiddler.fields.title); //with this tiddler titles like images/filename work but only if directory exists\n console.log(xfilepath);\n\t\t\t*/\n var xfilepath = generateBinaryFilePath(bodyData.tiddler.fields.title);\n\t\t\tconsole.log(xfilepath);\n\t\t\t$tw.utils.createDirectory(filesPath);\n\t\t\tconst buf = Buffer.from(bodyData.tiddler.fields.text,'base64');\n\t\t\t//const filename = path.join(filesPath, bodyData.tiddler.fields.title);\n\t\t\tconst filename = xfilepath;\n fs.writeFile(path.join(xfilepath), buf, function(error) {\n\t\t\t\tif (error) {\n\t\t\t\t\tconsole.log(error);\n\t\t\t\t\tthrow error;\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(\"External file saved: \" + filename);\n\t\t\t\t\tresponse.setHeader('Content-Type', 'application/json');\n\t\t\t\t\tresponse.end(JSON.stringify( {\n \"success\": \"saved \" + bodyData.tiddler.fields.title,\n \"status\": 200,\n //\"_canonical_uri\":\t \"files/\" + bodyData.tiddler.fields.title,\n \"_canonical_uri\": path.relative(path.resolve($tw.boot.wikiTiddlersPath,\"..\"),xfilepath),\n\t\t\t\t\t\t\t\"tiddler\": bodyData.tiddler.fields.title\n }));\n //state.wiki.addTiddler(bodyData.tiddler.fields,{_canonical_uri : bodyData.tiddler.fields.title, text:\"\"},state.wiki.getModificationFields());\n\t\t\t\t\t\t//return true;\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tconsole.log('Error parsing or writing uploaded file', e, {'level': 2});\n\t\t\tresponse.writeHead(400);\n\t\t\tresponse.end();\n\t\t}\n\t})\n};\n\nvar generateBinaryFilePath = function(title,options) {\n const filesDirPath = path.resolve($tw.boot.wikiTiddlersPath,\"../files\");\n\t// Remove any forward or backward slashes so we don't create directories\n\tvar filepath = title.replace(/\\/|\\\\/g,\"_\");\n\t// Replace any Windows control codes\n\tfilepath = filepath.replace(/^(con|prn|aux|nul|com[0-9]|lpt[0-9])$/i,\"_$1_\");\n\t// Replace any leading spaces with the same number of underscores\n\tfilepath = filepath.replace(/^ +/,function (u) { return u.replace(/ /g, \"_\")});\n\t//If the path does not start with \".\" or \"..\" && a path seperator, then\t\n\tif(!/^\\.{1,2}[/\\\\]/g.test(filepath)) {\n\t\t// Don't let the filename start with any dots because such files are invisible on *nix\n\t\tfilepath = filepath.replace(/^\\.+/g,function (u) { return u.replace(/\\./g, \"_\")});\n\t}\n\t// Replace any Unicode control codes\n\tfilepath = filepath.replace(/[\\x00-\\x1f\\x80-\\x9f]/g,\"_\");\n\t// Replace any characters that can't be used in cross-platform filenames\n\tfilepath = $tw.utils.transliterate(filepath.replace(/<|>|~|\\:|\\\"|\\||\\?|\\*|\\^/g,\"_\"));\n\tvar extension = path.extname(title);\n\t//remove extension from filepath\n\tfilepath = filepath.substring(0,filepath.length - extension.length);\n\t// Replace any dots or spaces at the end of the extension with the same number of underscores\t\n\textension = extension.replace(/[\\. ]+$/, function (u) { return u.replace(/[\\. ]/g, \"_\")});\n\t// Truncate the extension if it is too long\n\tif(extension.length > 32) {\n\t\textension = extension.substr(0,32);\n\t}\n\t// Truncate the filename if it is too long\n\tif(filepath.length > 200) {\n\t\tfilepath = filepath.substr(0,200);\n\t}\n\t// If the resulting filename is blank (eg because the title is just punctuation)\n\tif(!filepath || /^_+$/g.test(filepath)) {\n\t\t// ...then just use the character codes of the title\n\t\tfilepath = \"\";\t\n\t\t$tw.utils.each(title.split(\"\"),function(char) {\n\t\t\tif(filepath) {\n\t\t\t\tfilepath += \"-\";\n\t\t\t}\n\t\t\tfilepath += char.charCodeAt(0).toString();\n\t\t});\n\t}\n\t\n\t\n\tvar overwrite = $tw.wiki.getTextReference(\"!!text\",\"yes\",\"$:/config/sq/OverwriteBinaryFiles\") === \"yes\" ? true : false;\n\tif(overwrite) {\n\t\treturn path.resolve(filesDirPath,filepath + extension);\n\t}\n\t\n\tvar fullPath,\n\t\tcount = 0;\n\tdo {\n\t\tfullPath = path.resolve(filesDirPath,filepath + (count ? \"_\" + count : \"\") + extension);\n\t\tcount++;\n\t} while(fs.existsSync(fullPath));\t\n\treturn fullPath;\n}\n\n\n}());",
33+
"type": "application/javascript",
34+
"module-type": "route"
35+
},
36+
"$:/plugins/sq/ImportToExternalFile/startup.js": {
37+
"title": "$:/plugins/sq/ImportToExternalFile/startup.js",
38+
"text": "/*\\\ntitle: $:/plugins/sq/ImportToExternalFile/startup.js\ntype: application/javascript\nmodule-type: startup\n\nThis adds a hook for the \"th-importing-tiddler\"\n\n\\*/\n(function () {\n\n\t/*jslint node: true, browser: true */\n\t/*global $tw: false */\n\t\"use strict\";\n\n\t// Export name and synchronous status\n\texports.name = \"sq-server-images\";\n\texports.platforms = [\"browser\"];\n\texports.after = [\"render\"];\n\texports.synchronous = true;\n\n\texports.startup = function() {\n\n\t// Add the hook to the wiki in the browser\n\t$tw.hooks.addHook(\"th-importing-tiddler\", function(tiddler) {\n\t\t\n\t\tvar saveAsExternal = $tw.wiki.getTextReference(\"!!text\",\"yes\",\"$:/config/sq/SaveAsExternalFile\") === \"yes\" ? true : false;\n\t\tif(!saveAsExternal) {\n\t\t\treturn tiddler;\n\t\t}\n\t\treturn externaliseTiddler(tiddler);\n\n\t});\n\t\n\t$tw.rootWidget.addEventListener(\"tm-externalise-tiddler\",function(event){\n\t\tvar tiddler = $tw.wiki.getTiddler(event.tiddlerTitle);\n\t\tif(tiddler) {\n\t\t\texternaliseTiddler(tiddler);\n\t\t}\n\t});\n\t\n}\n\nfunction updateProgress(e) {\n\t// TODO make this work in different browsers\n\t/*\n\tif (e.lengthComputable) {\n\tvar percentComplete = e.loaded/e.total*100;\n\t} else {\n\tvar percentComplete = -1;\n\t}\n\tconsole.log(percentComplete);\n\t*/\n}\nfunction transferComplete(e) {\n\tconsole.log('Complete!!',e);\n}\nfunction transferFailed(e) {\n\tconsole.log('Failed due to Network issues!');\n}\nfunction transferCanceled(e) {\n\tconsole.log('Cancelled!')\n}\n\n// https://github.com/Jermolene/TiddlyWiki5/blob/14a28b77796461c9167898793ab9851e029e0354/core/modules/utils/dom/http.js\n\nvar externaliseTiddler = function(tiddler) {\n\t// Figure out if the thing being imported is something that should be\n\t// saved on the server.\n\tvar mediaTypes = ['image/gif', 'image/x-icon', 'image/jpeg', 'image/jpeg', 'image/png', 'image/svg+xml', 'application/pdf', 'application/zip', 'application/font-woff', 'application/x-font-ttf', 'audio/ogg', 'video/mp4', 'audio/mp3', 'audio/mp4'];\n\tif (mediaTypes.indexOf(tiddler.fields.type) > -1 && !tiddler.fields._canonical_uri) {\n\t\t// Check if this is set up to use HTTP post or websockets to save the\n\t\t// image on the server.\n\t\tvar request = new XMLHttpRequest();\n\t\trequest.upload.addEventListener('progress', updateProgress);\n\t\trequest.upload.addEventListener('load', transferComplete);\n\t\trequest.upload.addEventListener('error', transferFailed);\n\t\trequest.upload.addEventListener('abort', transferCanceled);\n\n\t\trequest.onreadystatechange = function() {\n\t\t\tif (this.readyState == 4) {\n\t\t\t\tif(this.status == 200) {\n\t\t\t\t\tconsole.log(this.response);\n\t\t\t\t\tvar json = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tjson = JSON.parse(this.response);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tconsole.log(\"Error/XMLHttpRequest when parsing JSON\" + \": \" + this.status);\n\t\t\t\t\t}\n\t\t\t\t\tif(json && json.tiddler) {\n\t\t\t\t\t\tconsole.log(json);\n\t\t\t\t\t\tvar tiddler = $tw.wiki.getTiddler(json.tiddler);\n\t\t\t\t\t\t$tw.wiki.addTiddler(new $tw.Tiddler(tiddler,{_canonical_uri: json._canonical_uri, text:\"\"}));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// if we are here the request did not return a success codePointAt\n\t\t\t\tconsole.log(\"Error/XMLHttpRequest\" + \": \" + this.status);\n\t\t\t};\n\t\t}\n\t\tvar uploadURL = '/api/upload';\n\t\trequest.open('POST', uploadURL, true);\n\n\t\tvar thing = {\n\t\t\ttiddler: tiddler\n\t\t}\n\t//\trequest.upload.addEventListener('load', transferComplete);\n\t\trequest.setRequestHeader(\"X-Requested-With\",\"TiddlyWiki\");\n\t\trequest.send(JSON.stringify(thing));\n\n\t\t// Change the tiddler fields and stuff\n\t\tvar fields = {};\n\t\tvar uri = '/files/'+tiddler.fields.title;\n\t\t//Use tw.utils.generateTiddlerFilePath //remove / etc from title\n\t\t//https://github.com/Jermolene/TiddlyWiki5/blob/master/core/modules/utils/filesystem.js#L321\n\t\tfields.title = tiddler.fields.title;\n\t\tfields.type = tiddler.fields.type;\n\t\tfields._canonical_uri = uri;\n\t\t//return new $tw.Tiddler(fields);\n\t\treturn tiddler;\n\t} else {\n\t\treturn tiddler;\n\t}\t\n}\n\n})();",
39+
"type": "application/javascript",
40+
"module-type": "startup"
41+
},
42+
"$:/plugins/sq/ImportToExternalFile/ViewTemplateImport": {
43+
"title": "$:/plugins/sq/ImportToExternalFile/ViewTemplateImport",
44+
"list-after": "$:/core/ui/ViewTemplate/import",
45+
"tags": "$:/tags/ViewTemplate",
46+
"text": "<$list filter=\"[all[current]field:plugin-type[import]]\" variable=\"null\">\n\n<$checkbox tiddler=\"$:/config/sq/SaveAsExternalFile\" field=\"text\" checked=\"yes\" unchecked=\"no\" default=\"yes\"> Import binary files as external attachments</$checkbox>\n\n<$list filter=\"[{$:/config/sq/SaveAsExternalFile}match[yes]]\" variable=\"null\">\n<$checkbox tiddler=\"$:/config/sq/OverwriteBinaryFiles\" field=\"text\" checked=\"yes\" unchecked=\"no\" default=\"yes\"> Overwrite binary files with the same name</$checkbox>\n</$list>\n\n</$list>"
47+
},
48+
"$:/config/ViewToolbarButtons/Visibility/$:/plugins/sq/ImportToExternalFile/Buttons/externalize": {
49+
"title": "$:/config/ViewToolbarButtons/Visibility/$:/plugins/sq/ImportToExternalFile/Buttons/externalize",
50+
"text": "hide"
51+
}
52+
}
53+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
author: Saq Imtiaz
2+
core-version: >=5.1.23
3+
dependents:
4+
description: Save binary files to the files folder on node.js
5+
list:
6+
plugin-type: plugin
7+
source:
8+
title: $:/plugins/sq/ImportToExternalFile
9+
type: application/json
10+
version: 0.1.32

0 commit comments

Comments
 (0)