diff --git a/boot/boot.js b/boot/boot.js index e5f6c775e..260604f95 100644 --- a/boot/boot.js +++ b/boot/boot.js @@ -1547,6 +1547,9 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) { case "basename": value = path.basename(filename,path.extname(filename)); break; + case "basename-uri-decoded": + value = decodeURIComponent(path.basename(filename,path.extname(filename))); + break; case "extname": value = path.extname(filename); break; @@ -1931,6 +1934,7 @@ $tw.boot.startup = function(options) { $tw.utils.registerFileType("application/enex+xml","utf8",".enex"); $tw.utils.registerFileType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","base64",".xlsx"); $tw.utils.registerFileType("application/x-bibtex","utf8",".bib"); + $tw.utils.registerFileType("application/epub+zip","base64",".epub"); // Create the wiki store for the app $tw.wiki = new $tw.Wiki(); // Install built in tiddler fields modules diff --git a/editions/tw5.com/tiddlers/nodejs/tiddlywiki.files_Files.tid b/editions/tw5.com/tiddlers/nodejs/tiddlywiki.files_Files.tid index 3df16e7c6..bbb08ceea 100644 --- a/editions/tw5.com/tiddlers/nodejs/tiddlywiki.files_Files.tid +++ b/editions/tw5.com/tiddlers/nodejs/tiddlywiki.files_Files.tid @@ -24,6 +24,7 @@ Each field can be specified as either a ''string'' or ''array'' value to be assi * ''source'' - (optional) a string specifying the source value for the field. If not specified, the existing value is used ** //filename// the filename of the file containing the tiddler ** //basename// the filename of the file containing the tiddler without any extension +** //basename-uri-decoded// the filename of the file containing the tiddler without any extension, with [[URI decoding|https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent]] applied ** //extname// the extension of the filename of the file containing the tiddler ** //created// the creation date/time of the file containing the tiddler ** //modified// the modification date/time of the file containing the tiddler @@ -61,13 +62,14 @@ There are also several examples of `tiddlywiki.files` files in the main [[Tiddly !! Importing a folder of PDFs -This example retrieves all the files with the extension `.pdf` from a folder specified by a relative path. Each tiddler is given the following fields: +This example retrieves all the files with the extension `.pdf` from a folder specified by a relative path. Each tiddler is set up for LazyLoading with the following fields: -* ''title'' - set to the filename of the PDF file +* ''title'' - set to the URI decoded base filename of the PDF file. [[URI decoding|https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent]] allows characters like "/" to be included in titles by URI encoding them as "%2F" * ''created'' - set to the creation date/time of the PDF file * ''modified'' - set to the modification date/time of the PDF file * ''type'' - set to `application/pdf` * ''tags'' - set to `$:/tags/AttachedFile` +* ''text'' - set to an empty string * ''_canonical_uri'' - set to the string "pdfs/" concatenated with the filename ``` @@ -78,16 +80,15 @@ This example retrieves all the files with the extension `.pdf` from a folder spe "filesRegExp": "^.*\\.pdf$", "isTiddlerFile": false, "fields": { - "title": {"source": "filename"}, + "title": {"source": "basename-uri-decoded"}, "created": {"source": "created"}, "modified": {"source": "modified"}, "type": "application/pdf", "tags": ["$:/tags/AttachedFile"], + "text": "", "_canonical_uri": {"source": "filename", "prefix": "pdfs/"} } } ] } ``` - -The approach of re-using the filename of the PDF as the \ No newline at end of file diff --git a/plugins/tiddlywiki/xlsx-utils/controls.tid b/plugins/tiddlywiki/xlsx-utils/controls.tid index 5a92b432e..339237062 100644 --- a/plugins/tiddlywiki/xlsx-utils/controls.tid +++ b/plugins/tiddlywiki/xlsx-utils/controls.tid @@ -130,6 +130,8 @@ suffixed <$reveal state="""$(field)$!!import-field-source""" type="match" text="constant" default="column" tag="span"> <$edit-text tiddler=<> field="import-field-value" tag="input" placeholder="constant" default=""/> +<$checkbox tiddler=<> field="import-field-skip-tiddler-if-blank" checked="yes" unchecked="no" default="no"> +Skip this tiddler when field blank
Title: <$tiddler tiddler=<>> @@ -192,6 +194,13 @@ Title:
  • +Row type: +<$select tiddler=<> field="import-row-type" default="by-field"> + + + +
  • +
  • <$button class="tc-btn-invisible"> <$action-createtiddler $basetitle="$:/_ExcelImporter/ImportSpecifiers/Field" $savetitle="$:/temp/newtiddler" import-spec-role="field" import-field-name="fieldname" import-field-type="string" import-field-source="column" import-field-column="Column Name" /> <$action-listops $tiddler=<> $subfilter="[{$:/temp/newtiddler}] +[putfirst[]]"/> diff --git a/plugins/tiddlywiki/xlsx-utils/deserializer.js b/plugins/tiddlywiki/xlsx-utils/deserializer.js index 452126a63..94a4249dc 100644 --- a/plugins/tiddlywiki/xlsx-utils/deserializer.js +++ b/plugins/tiddlywiki/xlsx-utils/deserializer.js @@ -23,7 +23,7 @@ exports["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] = f text: text }); // Return the output tiddlers - return importer.importTiddlers(); + return importer.getResults(); }; })(); diff --git a/plugins/tiddlywiki/xlsx-utils/importer.js b/plugins/tiddlywiki/xlsx-utils/importer.js index 7a595c603..6bd2f0946 100644 --- a/plugins/tiddlywiki/xlsx-utils/importer.js +++ b/plugins/tiddlywiki/xlsx-utils/importer.js @@ -21,103 +21,155 @@ var XLSXImporter = function(options) { this.filename = options.filename; this.text = options.text; this.importSpec = options.importSpec || $tw.wiki.getTiddlerText(DEFAULT_IMPORT_SPEC_TITLE); + this.logger = new $tw.utils.Logger("xlsx-utils"); + this.results = []; + if(JSZip) { + this.processWorkbook(); + } }; -XLSXImporter.prototype.importTiddlers = function() { - var self = this, - results = [], - workbook; - // Check for the JSZIP plugin - if(!JSZip) { - return results; - } +XLSXImporter.prototype.getResults = function() { + return this.results; +}; + +XLSXImporter.prototype.processWorkbook = function() { // Read the workbook if(this.filename) { - workbook = XLSX.readFile(this.filename); + this.workbook = XLSX.readFile(this.filename); } else if(this.text) { - workbook = XLSX.read(this.text,{type:"base64"}); + this.workbook = XLSX.read(this.text,{type:"base64"}); } // Read the root import specification - var rootImportSpec = $tw.wiki.getTiddler(this.importSpec); - if(rootImportSpec) { + this.rootImportSpec = $tw.wiki.getTiddler(this.importSpec); + if(this.rootImportSpec) { // Iterate through the sheets specified in the list field - $tw.utils.each(rootImportSpec.fields.list || [],function(sheetImportSpecTitle) { - // Get the sheet import specifier - var sheetImportSpec = $tw.wiki.getTiddler(sheetImportSpecTitle); - if(sheetImportSpec) { - var sheetName = sheetImportSpec.fields["import-sheet-name"], - sheet = workbook.Sheets[sheetName]; - // Get the size of the sheet - var sheetSize = self.measureSheet(sheet); - // Read the column names from the first row - var columnsByName = self.findColumns(sheet,sheetSize); - // Iterate through the rows - for(var row=sheetSize.startRow+1; row<=sheetSize.endRow; row++) { - // Iterate through the row import specifiers - $tw.utils.each(sheetImportSpec.fields.list || [],function(rowImportSpecTitle) { - var rowImportSpec = $tw.wiki.getTiddler(rowImportSpecTitle); - if(rowImportSpec) { - var tiddlerFields = {}; - // Iterate through the fields for the row - $tw.utils.each(rowImportSpec.fields.list || [],function(fieldImportSpecTitle) { - var fieldImportSpec = $tw.wiki.getTiddler(fieldImportSpecTitle); - if(fieldImportSpec) { - var fieldName = fieldImportSpec.fields["import-field-name"], - value; - switch(fieldImportSpec.fields["import-field-source"]) { - case "column": - var columnName = fieldImportSpec.fields["import-field-column"], - cell = sheet[XLSX.utils.encode_cell({c: columnsByName[columnName], r: row})]; - if(cell) { - switch(fieldImportSpec.fields["import-field-type"] || "string") { - case "date": - if(cell.t === "n") { - value = $tw.utils.stringifyDate(new Date((cell.v - (25567 + 2)) * 86400 * 1000)); - } - break; - case "string": - // Intentional fall-through - default: - value = cell.w; - break; - } - } - break; - case "constant": - value = fieldImportSpec.fields["import-field-value"] - break; - } - if(fieldImportSpec.fields["import-field-prefix"]) { - value = fieldImportSpec.fields["import-field-prefix"] + value; - } - if(fieldImportSpec.fields["import-field-suffix"]) { - value = value + fieldImportSpec.fields["import-field-suffix"]; - } - if(fieldImportSpec.fields["import-field-replace-blank"] && (value || "").trim() === "") { - value = fieldImportSpec.fields["import-field-replace-blank"]; - } - switch(fieldImportSpec.fields["import-field-list-op"] || "none") { - case "none": - tiddlerFields[fieldName] = value; - break; - case "append": - var list = $tw.utils.parseStringArray(tiddlerFields[fieldName] || ""); - $tw.utils.pushTop(list,value) - tiddlerFields[fieldName] = list; - break; - } - } - }); - results.push(tiddlerFields); - } - }); - } - } - }); + $tw.utils.each(this.rootImportSpec.fields.list || [],this.processSheet.bind(this)); } - return results; }; +XLSXImporter.prototype.processSheet = function(sheetImportSpecTitle) { + // Get the sheet import specifier + this.sheetImportSpec = $tw.wiki.getTiddler(sheetImportSpecTitle); + if(this.sheetImportSpec) { + this.sheetName = this.sheetImportSpec.fields["import-sheet-name"]; + this.sheet = this.workbook.Sheets[this.sheetName]; + if(!this.sheet) { + this.logger.alert("Missing sheet '" + this.sheetName + "'"); + } else { + // Get the size of the sheet + this.sheetSize = this.measureSheet(this.sheet); + // Read the column names from the first row + this.columnsByName = this.findColumns(this.sheet,this.sheetSize); + // Iterate through the rows + for(this.row=this.sheetSize.startRow+1; this.row<=this.sheetSize.endRow; this.row++) { + // Iterate through the row import specifiers + $tw.utils.each(this.sheetImportSpec.fields.list || [],this.processRow.bind(this)); + } + } + } +}; + +XLSXImporter.prototype.processRow = function(rowImportSpecTitle) { + this.rowImportSpec = $tw.wiki.getTiddler(rowImportSpecTitle); + if(this.rowImportSpec) { + this.tiddlerFields = {}; + this.skipTiddler = false; + // Determine the type of row + this.rowType = this.rowImportSpec.fields["import-row-type"] || "by-field"; + switch(this.rowType) { + case "by-column": + this.processRowByColumn(); + break; + case "by-field": + this.processRowByField(); + break; + } + // Save the tiddler if not skipped + if(!this.skipTiddler) { + if(!this.tiddlerFields.title) { + this.logger.alert("Missing title field for " + JSON.stringify(this.tiddlerFields)); + } + this.results.push(this.tiddlerFields); + } + } +}; + +XLSXImporter.prototype.processRowByColumn = function() { + var self = this; + // Iterate through the columns for the row + $tw.utils.each(this.columnsByName,function(index,name) { + var cell = self.sheet[XLSX.utils.encode_cell({c: self.columnsByName[name], r: self.row})]; + name = name.toLowerCase(); + if(cell && cell.w && $tw.utils.isValidFieldName(name)) { + self.tiddlerFields[name] = cell.w; + } + }); + // Skip the tiddler entirely if it doesn't have a title + if(!this.tiddlerFields.title) { + this.skipTiddler = true; + } +}; + +XLSXImporter.prototype.processRowByField = function() { + // Iterate through the fields for the row + $tw.utils.each(this.rowImportSpec.fields.list || [],this.processField.bind(this)); +}; + +XLSXImporter.prototype.processField = function(fieldImportSpecTitle) { + var fieldImportSpec = $tw.wiki.getTiddler(fieldImportSpecTitle); + if(fieldImportSpec) { + var fieldName = fieldImportSpec.fields["import-field-name"], + value; + switch(fieldImportSpec.fields["import-field-source"]) { + case "column": + var columnName = fieldImportSpec.fields["import-field-column"], + cell = this.sheet[XLSX.utils.encode_cell({c: this.columnsByName[columnName], r: this.row})]; + if(cell) { + switch(fieldImportSpec.fields["import-field-type"] || "string") { + case "date": + if(cell.t === "n") { + value = $tw.utils.stringifyDate(new Date((cell.v - (25567 + 2)) * 86400 * 1000)); + } + break; + case "string": + // Intentional fall-through + default: + value = cell.w; + break; + } + } + break; + case "constant": + value = fieldImportSpec.fields["import-field-value"] + break; + } + if((value || "").trim() === "") { + if((fieldImportSpec.fields["import-field-skip-tiddler-if-blank"] || "").trim().toLowerCase() === "yes") { + this.skipTiddler = true; + } + if(fieldImportSpec.fields["import-field-replace-blank"]) { + value = fieldImportSpec.fields["import-field-replace-blank"]; + } + } + if(fieldImportSpec.fields["import-field-prefix"]) { + value = fieldImportSpec.fields["import-field-prefix"] + value; + } + if(fieldImportSpec.fields["import-field-suffix"]) { + value = value + fieldImportSpec.fields["import-field-suffix"]; + } + switch(fieldImportSpec.fields["import-field-list-op"] || "none") { + case "none": + this.tiddlerFields[fieldName] = value; + break; + case "append": + var list = $tw.utils.parseStringArray(this.tiddlerFields[fieldName] || ""); + $tw.utils.pushTop(list,value) + this.tiddlerFields[fieldName] = list; + break; + } + } +} + XLSXImporter.prototype.measureSheet = function(sheet) { var sheetRange = XLSX.utils.decode_range(sheet["!ref"]); return { diff --git a/plugins/tiddlywiki/xlsx-utils/xlsx-import-command.js b/plugins/tiddlywiki/xlsx-utils/xlsx-import-command.js index 9300ffd43..091778ce7 100644 --- a/plugins/tiddlywiki/xlsx-utils/xlsx-import-command.js +++ b/plugins/tiddlywiki/xlsx-utils/xlsx-import-command.js @@ -36,7 +36,7 @@ Command.prototype.execute = function() { filename: filename, importSpec: importSpec }); - $tw.wiki.addTiddlers(importer.importTiddlers()); + $tw.wiki.addTiddlers(importer.getResults()); return null; }; diff --git a/themes/tiddlywiki/heavier/base.tid b/themes/tiddlywiki/heavier/base.tid index 20f0d59ab..885664fba 100644 --- a/themes/tiddlywiki/heavier/base.tid +++ b/themes/tiddlywiki/heavier/base.tid @@ -25,11 +25,11 @@ html body .tc-site-title, html body .tc-titlebar, html body .tc-subtitle, html body .tc-tiddler-missing .tc-title, -html body .tc-tab-buttons button { +html body .tc-tab-buttons button, +html body .tc-tiddler-frame .tc-tiddler-body { font-weight: 500; } -html body .tc-view-field-name, -html body .tc-tiddler-frame .tc-tiddler-body { +html body .tc-view-field-name { font-weight: 400; }