From 13f6dd5a6d5e43f6fc42884d6f2009d969170755 Mon Sep 17 00:00:00 2001 From: Eric Oulashin <eric.oulashin@gmail.com> Date: Sat, 25 Feb 2023 14:23:08 -0800 Subject: [PATCH] ddfilelister: Now supports being used as a loadable module for Scan Dirs and List Files (applicable for Synchronet 3.20+) --- xtrn/ddfilelister/ddfilelister.js | 470 ++++++++++++++++++------- xtrn/ddfilelister/readme.txt | 42 ++- xtrn/ddfilelister/revision_history.txt | 4 +- 3 files changed, 388 insertions(+), 128 deletions(-) diff --git a/xtrn/ddfilelister/ddfilelister.js b/xtrn/ddfilelister/ddfilelister.js index 838afbb726..465e2610a6 100644 --- a/xtrn/ddfilelister/ddfilelister.js +++ b/xtrn/ddfilelister/ddfilelister.js @@ -58,6 +58,9 @@ * scrolls through the file list/search results. Also, * used lfexpand() to ensure the extended description has * CRLF endings, useful for splitting it into multiple lines properly. + * 2023-02-25 Eric Oulashin Version 2.09 + * Now supports being used as a loadable module for + * Scan Dirs and List Files */ "use strict"; @@ -84,39 +87,42 @@ else } -// If the user's terminal doesn't support ANSI, then just call the standard Synchronet -// file list function and exit now -if (!console.term_supports(USER_ANSI)) -{ - bbs.list_files(); - exit(); -} +/* +Configured in SCFG->System->Loadable Modules: +Scan Dirs: User scans one or more directories for (e.g. new) files +List Files: User lists files within a file directory +View File Info: User views detailed information on files in a directory +This addresses/fixes feature request #521 for Nightfox + +Will need to document the mode argument bit values on the wiki, but +it's the usual suspects: FL_* for scandirs and listfiles and FI_* for +fileinfo. The scandirs_mod will be passed an extra bool (0/1) arg that +indicates whether or not the user is scanning *all* directories. +*/ -// This script requires Synchronet version 3.19 or higher. -// If the Synchronet version is below the minimum, then just call the standard -// Synchronet file list and exit. +// This script requires Synchronet version 3.19 or newer. +// If the Synchronet version is below the minimum, then exit. if (system.version_num < 31900) { if (user.is_sysop) { var message = "\x01n\x01h\x01y\x01i* Warning:\x01n\x01h\x01w Digital Distortion File Lister " + "requires version \x01g3.19\x01w or\r\n" - + "higher of Synchronet. This BBS is using version \x01g" + system.version + + "newer of Synchronet. This BBS is using version \x01g" + system.version + "\x01w.\x01n"; console.crlf(); console.print(message); console.crlf(); console.pause(); } - bbs.list_files(); exit(); } // Lister version information -var LISTER_VERSION = "2.08"; -var LISTER_DATE = "2023-01-18"; +var LISTER_VERSION = "2.09"; +var LISTER_DATE = "2023-02-25"; /////////////////////////////////////////////////////////////////////////////// @@ -194,13 +200,21 @@ var FILE_MOVE = 6; // Sysop action var FILE_DELETE = 7; // Sysop action // Search/list modes -var MODE_LIST_CURDIR = 1; +var MODE_LIST_DIR = 1; var MODE_SEARCH_FILENAME = 2; var MODE_SEARCH_DESCRIPTION = 3; var MODE_NEW_FILE_SEARCH = 4; +// Sort orders (not included in FileBase.SORT) +var SORT_FL_ULTIME = 50; // Sort by upload time +var SORT_FL_DLTIME = 51; // Sort by download time + // The searc/list mode for the current run -var gScriptMode = MODE_LIST_CURDIR; // Default +var gScriptMode = MODE_LIST_DIR; // Default +var gListBehavior = FL_NONE; // From sbbsdefs.js + +// The directory internal code to list +var gDirCode = bbs.curdir_code; @@ -222,17 +236,38 @@ var gPauseAfterViewingFile = true; /////////////////////////////////////////////////////////////////////////////// // Script execution code +// The filename pattern to match +var gFilespec = "*"; + // The sort order to use for the file list var gFileSortOrder = FileBase.SORT.NATURAL; // Natural sort order, same as DATE_A (import date ascending) var gSearchVerbose = false; +// When called as a lodable module, one of the options is to scan all dirs +var gScanAllDirs = false; + // Read the configuration file and set the settings readConfigFile(); // Parse command-line arguments (which sets program options) parseArgs(argv); +// If the user's terminal doesn't support ANSI, then just call the standard Synchronet +// file list function and exit now +if (!console.term_supports(USER_ANSI)) +{ + var exitCode = 0; + if (gScriptMode == MODE_SEARCH_FILENAME || gScriptMode == MODE_SEARCH_DESCRIPTION || gScriptMode == MODE_NEW_FILE_SEARCH) + { + //if (user.is_sysop) console.print("\x01n\r\nScan dirs\r\n"); // Temporary + bbs.scan_dirs(gListBehavior, gScanAllDirs); + } + else + exitCode = bbs.list_files(gDirCode, gFilespec, gListBehavior); + exit(exitCode); +} + // This array will contain file metadata objects var gFileList = []; @@ -249,8 +284,13 @@ if (gFileList.length == 0) { console.crlf(); console.print("\x01n\x01c"); - if (gScriptMode == MODE_LIST_CURDIR) - console.print("There are no files in the current directory."); + if (gScriptMode == MODE_LIST_DIR) + { + if (gFilespec == "*" || gFilespec == "*.*") + console.print("There are no files in the directory."); + else + console.print("No files in the directory were found matching " + gFilespec); + } else console.print("No files were found."); console.print("\x01n"); @@ -262,7 +302,8 @@ if (gFileList.length == 0) // Clear the screen and display the header lines console.clear("\x01n"); -displayFileLibAndDirHeader(); +if ((gListBehavior & FL_NO_HDR) != FL_NO_HDR) + displayFileLibAndDirHeader(); // Construct and display the menu/command bar at the bottom of the screen var fileMenuBar = new DDFileMenuBar({ x: 1, y: console.screen_rows }); fileMenuBar.writePromptLine(); @@ -322,16 +363,19 @@ while (continueDoingFileList) continueDoingFileList = false; else { - if (actionRetObj.reDrawHeaderTextOnly) - { - console.print("\x01n"); - displayFileLibAndDirHeader(true); // Will move the cursor where it needs to be - } - else if (actionRetObj.reDrawListerHeader) + if ((gListBehavior & FL_NO_HDR) != FL_NO_HDR) { - console.print("\x01n"); - console.gotoxy(1, 1); - displayFileLibAndDirHeader(); + if (actionRetObj.reDrawHeaderTextOnly) + { + console.print("\x01n"); + displayFileLibAndDirHeader(true); // Will move the cursor where it needs to be + } + else if (actionRetObj.reDrawListerHeader) + { + console.print("\x01n"); + console.gotoxy(1, 1); + displayFileLibAndDirHeader(); + } } if (actionRetObj.reDrawCmdBar) // Could call fileMenuBar.constructPromptText(); if needed fileMenuBar.writePromptLine(); @@ -537,8 +581,7 @@ function showFileInfo(pFileList, pFileListMenu) // extended information. Get a metadata object with extended information so we // can display the extended description. // The metadata object in pFileList should have a dirCode added by this script. - // If not, assume the user's current directory. - var dirCode = bbs.curdir_code; + var dirCode = gDirCode; if (pFileList[pFileListMenu.selectedItemIdx].hasOwnProperty("dirCode")) dirCode = pFileList[pFileListMenu.selectedItemIdx].dirCode; var fileMetadata = null; @@ -558,7 +601,7 @@ function showFileInfo(pFileList, pFileListMenu) // Note: File size can also be retrieved by calling a FileBase's get_size(fileMetadata.name) // TODO: Shouldn't need the max length here fileInfoStr += "Size: " + gColors.fileSize + getFileSizeStr(fileMetadata.size, 99999) + "\x01n\x01w\r\n"; - fileInfoStr += "Timestamp: " + gColors.fileTimestamp + strftime("%Y-%m-%d %H:%M:%S", fileMetadata.time) + "\x01n\x01w\r\n" + fileInfoStr += "Timestamp: " + gColors.fileTimestamp + strftime("%Y-%m-%d %H:%M:%S", fileMetadata.time) + "\x01n\x01w\r\n"; fileInfoStr += "\r\n"; // File library/directory information @@ -993,15 +1036,15 @@ function displayHelpScreen() console.crlf(); // If listing files in a directory, display information about the current file directory. - if (gScriptMode == MODE_LIST_CURDIR) + if (gScriptMode == MODE_LIST_DIR) { - var libIdx = file_area.dir[bbs.curdir_code].lib_index; - var dirIdx = file_area.dir[bbs.curdir_code].index; + var libIdx = file_area.dir[gDirCode].lib_index; + var dirIdx = file_area.dir[gDirCode].index; console.print("\x01n\x01cCurrent file library: \x01g" + file_area.lib_list[libIdx].description); console.crlf(); - console.print("\x01cCurrent file directory: \x01g" + file_area.dir[bbs.curdir_code].description); + console.print("\x01cCurrent file directory: \x01g" + file_area.dir[gDirCode].description); console.crlf(); - console.print("\x01cThere are \x01g" + file_area.dir[bbs.curdir_code].files + " \x01cfiles in this directory."); + console.print("\x01cThere are \x01g" + file_area.dir[gDirCode].files + " \x01cfiles in this directory."); } else if (gScriptMode == MODE_SEARCH_FILENAME) console.print("\x01n\x01cCurrently performing a filename search"); @@ -1167,7 +1210,7 @@ function chooseFilebaseAndMoveFileToOtherFilebase(pFileList, pFileListMenu) // If we're listing files in the user's current directory, then remove // the file info object from the file list array. Otherwise, update // the metadata object in the list. - if (gScriptMode == MODE_LIST_CURDIR) + if (gScriptMode == MODE_LIST_DIR) { pFileList.splice(fileIdx, 1); // Subtract 1 from the remaining indexes in the fileIndexes array @@ -1207,7 +1250,7 @@ function chooseFilebaseAndMoveFileToOtherFilebase(pFileList, pFileListMenu) // set allSameDir to false so the header lines will now say "various". // However, if not all files were in the same directory, check to see if they // are now, and if so, we'll need to re-draw the header lines. - if (gScriptMode != MODE_LIST_CURDIR && typeof(pFileList.allSameDir) == "boolean") + if (gScriptMode != MODE_LIST_DIR && typeof(pFileList.allSameDir) == "boolean") { if (pFileList.allSameDir) { @@ -1235,7 +1278,7 @@ function chooseFilebaseAndMoveFileToOtherFilebase(pFileList, pFileListMenu) } // After moving the files, if there are no more files (in the directory or otherwise), // say so and exit now. - if (gScriptMode == MODE_LIST_CURDIR && file_area.dir[bbs.curdir_code].files == 0) + if (gScriptMode == MODE_LIST_DIR && file_area.dir[gDirCode].files == 0) { displayMsg("There are no more files in the directory.", false); retObj.exitNow = true; @@ -1354,7 +1397,7 @@ function confirmAndRemoveFilesFromFilebase(pFileList, pFileListMenu) } if (removeFileSucceeded) { - if (gScriptMode == MODE_LIST_CURDIR) + if (gScriptMode == MODE_LIST_DIR) numFilesRemaining = filebase.files; } filebase.close(); @@ -1430,7 +1473,7 @@ function confirmAndRemoveFilesFromFilebase(pFileList, pFileListMenu) } else { - if (gScriptMode == MODE_LIST_CURDIR) + if (gScriptMode == MODE_LIST_DIR) displayMsg("The directory now has no files.", false, true); else displayMsg("There are no more files to show.", false, true); @@ -1994,6 +2037,10 @@ function doFrameInputLoop(pFrame, pScrollbar, pFrameContentStr, pAdditionalQuitK // pDirCodeOverride: Optional string: If this is valid, this will be used for the library & directory name function displayFileLibAndDirHeader(pTextOnly, pDirCodeOverride) { + // If the behavior flags include no header, then just return immediately + if ((gListBehavior & FL_NO_HDR) == FL_NO_HDR) + return; + var textOnly = (typeof(pTextOnly) === "boolean" ? pTextOnly : false); // Determine if this is the first time this function has been called. If so, @@ -2013,8 +2060,8 @@ function displayFileLibAndDirHeader(pTextOnly, pDirCodeOverride) var libDesc = ""; var dirDesc = ""; var dirCode = ""; - if (gScriptMode == MODE_LIST_CURDIR) - dirCode = bbs.curdir_code; + if (gScriptMode == MODE_LIST_DIR) + dirCode = gDirCode; else if (typeof(gFileList.allSameDir) === "boolean" && gFileList.allSameDir && gFileList.length > 0) dirCode = gFileList[0].dirCode; if (dirCode.length > 0) @@ -2183,9 +2230,12 @@ function createFileListMenu(pQuitKeys) var allSameDir = (typeof(gFileList.allSameDir) === "boolean" ? gFileList.allSameDir : false); if (isDoingFileSearch() && !allSameDir && gFileList[pIdx].dirCode != this.lastFileDirCode) { - var originalCurPos = console.getxy(); - displayFileLibAndDirHeader(true, gFileList[pIdx].dirCode); - console.gotoxy(originalCurPos); + if ((gListBehavior & FL_NO_HDR) != FL_NO_HDR) + { + var originalCurPos = console.getxy(); + displayFileLibAndDirHeader(true, gFileList[pIdx].dirCode); + console.gotoxy(originalCurPos); + } } this.lastFileDirCode = gFileList[pIdx].dirCode; @@ -2945,6 +2995,10 @@ function readConfigFile() gFileSortOrder = FileBase.SORT.DATE_A; else if (valueUpper == "DATE_D") gFileSortOrder = FileBase.SORT.DATE_D; + else if (valueUpper == "ULTIME") + gFileSortOrder = SORT_FL_ULTIME; + else if (valueUpper == "DLTIME") + gFileSortOrder = SORT_FL_DLTIME; else // Default gFileSortOrder = FileBase.SORT.NATURAL; } @@ -3131,59 +3185,139 @@ function shortenFilename(pFilename, pMaxLen, pFillWidth) // value will be a boolean. Otherwise, the value will be a string. // // Parameters: -// pArgArr: An array of strings containing values in the format -arg=val -function parseArgs(pArgArr) +// argv: An array of strings containing values in the format -arg=val +// +// Return value: Boolean: Whether or not this script was run as a loadable +// module (depending on the arguments used). +function parseArgs(argv) { // Default program options - gScriptMode = MODE_LIST_CURDIR; + gScriptMode = MODE_LIST_DIR; - // Sanity checking for pArgArr - Make sure it's an array - if ((typeof(pArgArr) != "object") || (typeof(pArgArr.length) != "number")) - return; + // Sanity checking for argv - Make sure it's an array + if ((typeof(argv) != "object") || (typeof(argv.length) != "number")) + return false; - // Go through pArgArr looking for strings in the format -arg=val and parse them - // into objects in the argVals array. - var equalsIdx = 0; - var argName = ""; - var argVal = ""; - var argValUpper = ""; // For case-insensitive matching - for (var i = 0; i < pArgArr.length; ++i) + // This script accepts arguments in -arg=val format; See if there are any of those. + // If so, process arguments with that assumption; otherwise, we'll check for args + // passed when Synchronet runs this as a loadable module. + var scriptRanAsLoadableModule = false; + var anySettingEqualsVal = false; + for (var i = 0; i < argv.length && !anySettingEqualsVal; ++i) + anySettingEqualsVal = (typeof(argv[i]) === "string" && argv[i].length > 0 && argv[i].charAt(0) == "-" && argv[i].indexOf("=") > 1); + if (anySettingEqualsVal) { - // We're looking for strings that start with "-", except strings that are - // only "-". - if ((typeof(pArgArr[i]) != "string") || (pArgArr[i].length == 0) || - (pArgArr[i].charAt(0) != "-") || (pArgArr[i] == "-")) + // Go through argv looking for strings in the format -arg=val and parse them + // into objects in the argVals array. + var equalsIdx = 0; + var argName = ""; + var argVal = ""; + var argValUpper = ""; // For case-insensitive matching + for (var i = 0; i < argv.length; ++i) { - continue; + // We're looking for strings that start with "-", except strings that are + // only "-". + if (typeof(argv[i]) !== "string" || argv[i].length == 0 || argv[i].charAt(0) != "-" || argv[i] == "-") + continue; + + // Look for an = and if found, split the string on the = + equalsIdx = argv[i].indexOf("="); + // If a = is found, then split on it and add the argument name & value + // to the array. Otherwise (if the = is not found), then treat the + // argument as a boolean and set it to true (to enable an option). + if (equalsIdx > -1) + { + argName = argv[i].substring(1, equalsIdx).toUpperCase(); + argVal = argv[i].substr(equalsIdx+1); + argValUpper = argVal.toUpperCase(); + if (argName === "MODE") + { + if (argValUpper === "SEARCH_FILENAME") + gScriptMode = MODE_SEARCH_FILENAME; + else if (argValUpper === "SEARCH_DESCRIPTION") + gScriptMode = MODE_SEARCH_DESCRIPTION; + else if (argValUpper === "NEW_FILE_SEARCH") + gScriptMode = MODE_NEW_FILE_SEARCH; + else if (argValUpper === "LIST_CURDIR") + gScriptMode = MODE_LIST_DIR; + } + } + // Nothing to do if an = was not found } + } + else + { + // Check for arguments as if this was run by Synchronet as a loadable module + // (for either Scan Dirs or List Files) - // Look for an = and if found, split the string on the = - equalsIdx = pArgArr[i].indexOf("="); - // If a = is found, then split on it and add the argument name & value - // to the array. Otherwise (if the = is not found), then treat the - // argument as a boolean and set it to true (to enable an option). - if (equalsIdx > -1) + /* + // bbs.list_files() & bbs.scan_dirs() + //******************************************** + var FL_NONE =0; // No special behavior + var FL_ULTIME =(1<<0); // List files by upload time + var FL_DLTIME =(1<<1); // List files by download time + var FL_NO_HDR =(1<<2); // Don't list directory header + var FL_FINDDESC =(1<<3); // Find text in description + var FL_EXFIND =(1<<4); // Find text in description - extended info + var FL_VIEW =(1<<5); // View ZIP/ARC/GIF etc. info + */ + + // There must be either 2 or 3 arguments + if (argv.length < 2) + return false; + // The 2nd argument is the mode/behavior bits in either case + var FLBehavior = parseInt(argv[1]); + if (isNaN(FLBehavior)) + return false; + else + gListBehavior = FLBehavior; + scriptRanAsLoadableModule = true; + + // Default gScriptmode to MODE_LIST_DIR; for FLBehavior as FL_NONE, no special behavior + gScriptMode = MODE_LIST_DIR; + + // 2 args - Scanning/searching + if (argv.length == 2) { - argName = pArgArr[i].substring(1, equalsIdx).toUpperCase(); - argVal = pArgArr[i].substr(equalsIdx+1); - argValUpper = argVal.toUpperCase(); - if (argName === "MODE") + // - 0: Bool (scanning all directories): 0/1 + // - 1: FL_ mode value + gScanAllDirs = (argv[0] == "1"); + if ((FLBehavior & FL_ULTIME) == FL_ULTIME) + gScriptMode = MODE_NEW_FILE_SEARCH; + else if ((FLBehavior & FL_FINDDESC) == FL_FINDDESC || (FLBehavior & FL_EXFIND) == FL_EXFIND) + gScriptMode = MODE_SEARCH_DESCRIPTION; + if ((FLBehavior & FL_VIEW) == FL_VIEW) { - if (argValUpper === "SEARCH_FILENAME") - gScriptMode = MODE_SEARCH_FILENAME; - else if (argValUpper === "SEARCH_DESCRIPTION") - gScriptMode = MODE_SEARCH_DESCRIPTION; - else if (argValUpper === "NEW_FILE_SEARCH") - gScriptMode = MODE_NEW_FILE_SEARCH; - else if (argValUpper === "LIST_CURDIR") - gScriptMode = MODE_LIST_CURDIR; + // View ZIP/ARC/GIF etc. info + // TODO: Not sure what to do with this } } - else // An equals sign (=) was not found. Add as a boolean set to true to enable the option. + // 3 args - Listing + else if (argv.length >= 3) //==3 { - // Nothing to be done here for this script + // - 0: Directory internal code + // - 1: FL_ mode value + // - 2: Filespec (i.e., *, *.zip, etc.) + if (!file_area.dir.hasOwnProperty(argv[0])) + return false; + gDirCode = argv[0]; + gFilespec = argv[2]; + if ((FLBehavior & FL_VIEW) == FL_VIEW) + { + // View ZIP/ARC/GIF etc. info + // TODO: Not sure what to do with this + } } + + // Options that apply to both searching and listing + if ((FLBehavior & FL_ULTIME) == FL_ULTIME) + gFileSortOrder = SORT_FL_ULTIME; + else if ((FLBehavior & FL_DLTIME) == FL_DLTIME) + gFileSortOrder = SORT_FL_DLTIME; + } + + return scriptRanAsLoadableModule; } // Populates the file list (gFileList). @@ -3205,19 +3339,19 @@ function populateFileList(pSearchMode) var allSameDir = true; // Do the things for list or search, depending on the specified mode - if (pSearchMode == MODE_LIST_CURDIR) // This is the default + if (pSearchMode == MODE_LIST_DIR) // This is the default { - var filebase = new FileBase(bbs.curdir_code); + var filebase = new FileBase(gDirCode); if (filebase.open()) { // If there are no files in the filebase, then say so and exit now. if (filebase.files == 0) { filebase.close(); - var libIdx = file_area.dir[bbs.curdir_code].lib_index; + var libIdx = file_area.dir[gDirCode].lib_index; console.crlf(); console.print("\x01n\x01cThere are no files in \x01h" + file_area.lib_list[libIdx].description + "\x01n\x01c - \x01h" + - file_area.dir[bbs.curdir_code].description + "\x01n"); + file_area.dir[gDirCode].description + "\x01n"); console.crlf(); console.pause(); retObj.exitNow = true; @@ -3227,7 +3361,16 @@ function populateFileList(pSearchMode) // Get a list of file data var fileDetail = (extendedDescEnabled() ? FileBase.DETAIL.EXTENDED : FileBase.DETAIL.NORM); - gFileList = filebase.get_list("*", fileDetail, 0, true, gFileSortOrder); + if (gFileSortOrder == SORT_FL_ULTIME || gFileSortOrder == SORT_FL_DLTIME) + { + gFileList = filebase.get_list(gFilespec, fileDetail, 0, true, FileBase.SORT.NATURAL); + if (gFileSortOrder == SORT_FL_ULTIME) + gFileList.sort(fileInfoSortULTime); + else if (gFileSortOrder == SORT_FL_DLTIME) + gFileList.sort(fileInfoSortDLTime); + } + else + gFileList = filebase.get_list(gFilespec, fileDetail, 0, true, gFileSortOrder); filebase.close(); // Add a dirCode property to the file metadata objects (for consistency, // as file search results may contain files from multiple directories). @@ -3235,7 +3378,7 @@ function populateFileList(pSearchMode) // from the end. for (var i = 0; i < gFileList.length; ++i) { - gFileList[i].dirCode = bbs.curdir_code; + gFileList[i].dirCode = gDirCode; if (gFileList[i].hasOwnProperty("extdesc") && /\r\n$/.test(gFileList[i].extdesc)) { gFileList[i].extdesc = gFileList[i].extdesc.substr(0, gFileList[i].extdesc.length-2); @@ -3247,7 +3390,7 @@ function populateFileList(pSearchMode) else { console.crlf(); - console.print("\x01n\x01h\x01yUnable to open \x01w" + file_area.dir[bbs.curdir_code].description + "\x01n"); + console.print("\x01n\x01h\x01yUnable to open \x01w" + file_area.dir[gDirCode].description + "\x01n"); console.crlf(); console.pause(); retObj.exitNow = true; @@ -3259,12 +3402,18 @@ function populateFileList(pSearchMode) { var lastDirCode = ""; - // Prompt the user for directory, library, or all - console.print("\x01n"); - console.crlf(); - console.mnemonics(bbs.text(DirLibOrAll)); + // If not searching all already, prompt the user for directory, library, or all var validInputOptions = "DLA"; - var userInputDLA = console.getkeys(validInputOptions, -1, K_UPPER); + var userInputDLA = ""; + if (gScanAllDirs) + userInputDLA = "A"; + else + { + console.print("\x01n"); + console.crlf(); + console.mnemonics(bbs.text(DirLibOrAll)); + userInputDLA = console.getkeys(validInputOptions, -1, K_UPPER); + } var userFilespec = ""; if (userInputDLA.length > 0 && validInputOptions.indexOf(userInputDLA) > -1) { @@ -3286,14 +3435,20 @@ function populateFileList(pSearchMode) { var lastDirCode = ""; - // Prompt the user for directory, library, or all - console.print("\x01n"); - console.crlf(); - //console.print("\r\n\x01c\x01hFind Text in File Descriptions (no wildcards)\x01n\r\n"); - console.mnemonics(bbs.text(DirLibOrAll)); - console.print("\x01n"); + // If not saerching all already, prompt the user for directory, library, or all var validInputOptions = "DLA"; - var userInputDLA = console.getkeys(validInputOptions, -1, K_UPPER); + var userInputDLA = ""; + if (gScanAllDirs) + userInputDLA = "A"; + else + { + console.print("\x01n"); + console.crlf(); + //console.print("\r\n\x01c\x01hFind Text in File Descriptions (no wildcards)\x01n\r\n"); + console.mnemonics(bbs.text(DirLibOrAll)); + console.print("\x01n"); + userInputDLA = console.getkeys(validInputOptions, -1, K_UPPER); + } var searchDescription = ""; if (userInputDLA.length > 0 && validInputOptions.indexOf(userInputDLA) > -1) { @@ -3344,14 +3499,20 @@ function populateFileList(pSearchMode) between successive sessions. */ - // Prompt the user for directory, library, or all - console.print("\x01n"); - console.crlf(); - console.mnemonics(bbs.text(DirLibOrAll)); - var validInputOptions = "DLA"; - var userInputDLA = console.getkeys(validInputOptions, -1, K_UPPER); - console.print("\x01n"); - console.crlf(); + // If not searching all dirs already, prompt the user for directory, library, or all + var userInputDLA = ""; + if (gScanAllDirs) + userInputDLA = "A"; + else + { + console.print("\x01n"); + console.crlf(); + console.mnemonics(bbs.text(DirLibOrAll)); + var validInputOptions = "DLA"; + userInputDLA = console.getkeys(validInputOptions, -1, K_UPPER); + console.print("\x01n"); + console.crlf(); + } if (userInputDLA == "D" || userInputDLA == "L" || userInputDLA == "A") { console.print("\x01n\x01cSearching for files uploaded after \x01h" + system.timestr(bbs.new_file_time) + "\x01n"); @@ -3375,7 +3536,7 @@ function populateFileList(pSearchMode) return retObj; } - if (pSearchMode != MODE_LIST_CURDIR) + if (pSearchMode != MODE_LIST_DIR) gFileList.allSameDir = allSameDir; if (dirErrors.length > 0) @@ -3448,7 +3609,17 @@ function searchDirWithFilespec(pDirCode, pFilespec) if (filebase.open()) { var fileDetail = (extendedDescEnabled() ? FileBase.DETAIL.EXTENDED : FileBase.DETAIL.NORM); - var fileList = filebase.get_list(pFilespec, fileDetail, 0, true, gFileSortOrder); + var fileList; + if (gFileSortOrder == SORT_FL_ULTIME || gFileSortOrder == SORT_FL_DLTIME) + { + fileList = filebase.get_list(pFilespec, fileDetail, 0, true, FileBase.SORT.NATURAL); + if (gFileSortOrder == SORT_FL_ULTIME) + fileList.sort(fileInfoSortULTime); + else if (gFileSortOrder == SORT_FL_DLTIME) + fileList.sort(fileInfoSortDLTime); + } + else + fileList = filebase.get_list(pFilespec, fileDetail, 0, true, gFileSortOrder); retObj.foundFiles = (fileList.length > 0); filebase.close(); for (var i = 0; i < fileList.length; ++i) @@ -3491,7 +3662,18 @@ function searchDirWithDescUpper(pDirCode, pDescUpper) var filebase = new FileBase(pDirCode); if (filebase.open()) { - var fileList = filebase.get_list("*", FileBase.DETAIL.EXTENDED, 0, true, gFileSortOrder); + //var fileList = filebase.get_list(gFilespec, FileBase.DETAIL.EXTENDED, 0, true, gFileSortOrder); + var fileList; + if (gFileSortOrder == SORT_FL_ULTIME || gFileSortOrder == SORT_FL_DLTIME) + { + fileList = filebase.get_list(gFilespec, FileBase.DETAIL.EXTENDED, 0, true, FileBase.SORT.NATURAL); + if (gFileSortOrder == SORT_FL_ULTIME) + fileList.sort(fileInfoSortULTime); + else if (gFileSortOrder == SORT_FL_DLTIME) + fileList.sort(fileInfoSortDLTime); + } + else + fileList = filebase.get_list(gFilespec, FileBase.DETAIL.EXTENDED, 0, true, gFileSortOrder); filebase.close(); for (var i = 0; i < fileList.length; ++i) { @@ -3550,7 +3732,17 @@ function searchDirNewFiles(pDirCode, pSinceTime) if (filebase.open()) { var fileDetail = (extendedDescEnabled() ? FileBase.DETAIL.EXTENDED : FileBase.DETAIL.NORM); - var fileList = filebase.get_list("*", fileDetail, 0, true, gFileSortOrder); + var fileList; + if (gFileSortOrder == SORT_FL_ULTIME || gFileSortOrder == SORT_FL_DLTIME) + { + fileList = filebase.get_list(gFilespec, fileDetail, 0, true, FileBase.SORT.NATURAL); + if (gFileSortOrder == SORT_FL_ULTIME) + fileList.sort(fileInfoSortULTime); + else if (gFileSortOrder == SORT_FL_DLTIME) + fileList.sort(fileInfoSortDLTime); + } + else + fileList = filebase.get_list(gFilespec, fileDetail, 0, true, gFileSortOrder); filebase.close(); for (var i = 0; i < fileList.length; ++i) { @@ -3610,7 +3802,7 @@ function searchDirGroupOrAll(pSearchOption, pDirSearchFn) if (searchOptionUpper == "D") { // Current directory - var searchRetObj = pDirSearchFn(bbs.curdir_code); + var searchRetObj = pDirSearchFn(gDirCode); retObj.allSameDir = true; for (var i = 0; i < searchRetObj.dirErrors.length; ++i) retObj.errors.push(searchRetObj.dirErrors[i]); @@ -3618,7 +3810,7 @@ function searchDirGroupOrAll(pSearchOption, pDirSearchFn) else if (searchOptionUpper == "L") { // Current file library - var libIdx = file_area.dir[bbs.curdir_code].lib_index; + var libIdx = file_area.dir[gDirCode].lib_index; for (var dirIdx = 0; dirIdx < file_area.lib_list[libIdx].dir_list.length; ++dirIdx) { if (file_area.lib_list[libIdx].dir_list[dirIdx].can_access) // And can_download? @@ -3670,7 +3862,8 @@ function searchDirGroupOrAll(pSearchOption, pDirSearchFn) } else { - retObj.errors.push("Invalid search option" + (pSearchOption.length > 0 ? ": " + pSearchOption : "")); + //retObj.errors.push("Invalid search option" + (pSearchOption.length > 0 ? ": " + pSearchOption : "")); + retObj.errors.push("Aborted"); } return retObj; @@ -3867,4 +4060,43 @@ function refreshScreenMainContent(pUpperLeftX, pUpperLeftY, pWidth, pHeight, pSe function isDoingFileSearch() { return (gScriptMode == MODE_SEARCH_FILENAME || gScriptMode == MODE_SEARCH_DESCRIPTION || gScriptMode == MODE_NEW_FILE_SEARCH); -} \ No newline at end of file +} + +// Custom file info sort function for upload time +function fileInfoSortULTime(pA, pB) +{ + if (pA.hasOwnProperty("added") && pB.hasOwnProperty("added")) + { + if (pA.added < pB.added) + return -1; + else if (pA.added > pB.added) + return 1; + else + return 0; + } + else + { + if (pA.time < pB.time) + return -1; + else if (pA.time > pB.time) + return 1; + else + return 0; + } +} + +// Custom file info sort function for download time +function fileInfoSortDLTime(pA, pB) +{ + if (pA.hasOwnProperty("last_downloaded") && pB.hasOwnProperty("last_downloaded")) + { + if (pA.last_downloaded < pB.last_downloaded) + return -1; + else if (pA.last_downloaded > pB.last_downloaded) + return 1; + else + return 0; + } + else + return 0; +} diff --git a/xtrn/ddfilelister/readme.txt b/xtrn/ddfilelister/readme.txt index c52c45e76c..5e5aea7785 100644 --- a/xtrn/ddfilelister/readme.txt +++ b/xtrn/ddfilelister/readme.txt @@ -1,13 +1,13 @@ Digital Distortion File Lister - Version 2.08 - Release date: 2023-01-18 + Version 2.09 + Release date: 2023-02-25 by Eric Oulashin Sysop of Digital Distortion BBS internet address: digitaldistortionbbs.com - Alternate address: digdist.bbsindex.com + Alternate address: digdist.synchro.net Email: eric.oulashin@gmail.com @@ -19,6 +19,7 @@ Contents 1. Disclaimer 2. Introduction 3. Installation & Setup + - Loadable Module setup - Command shell setup - Background: Running JavaScript scripts in Synchronet 4. Configuration file & color/text theme configuration file @@ -105,17 +106,42 @@ where the .js script is located. So, if you desire, you could place ddfilelister.js in sbbs/exec and the .cfg file sin your sbbs/ctrl directory, for example. +One way to install ddfilelister.js is to run it directly from your command +shell. Also, as of Synchronet 3.20 (built on February 25, 2023 and newer), +there are a couple of loadable module options in SCFG for file operations; +ddfilelister.js can be used as a loadable module for Scan Dirs and List Files. +The advantage of having ddfilelister.js set up as a loadable module is that +ddfilelister.js will be used from standard command shells and using the standard +file list/search commands/functions. + +Loadable Module setup +--------------------- +Note: This only works in Synchronet 3.20 (built from February 25, 2020) and +newer. + +As a loadable module, ddfilelister.js works for the Scan Dirs and List Files +options. These options are available in SCFG > System > Loadable Modules. +If you have ddfilelister.js in your mods directory or other standard directory, +you can specify the settings as follows: + Scan Dirs ddfilelister.js + List Files ddfilelister.js + +If you keep ddfilelister.js in sbbs/xtrn/ddfilelister, you can specify it as +follows: + Scan Dirs ../xtrn/ddfilelister/ddfilelister.js + List Files ../xtrn/ddfilelister/ddfilelister.js + + Command shell setup ------------------- -Running the file lister involves simply having the BBS run a command to run -ddfilelister.js. No command-line parameters are required. The command to run -ddfilelister.js can be placed in a command shell for a command key, or in +To running the file lister in a command shell, you simply need to add a command +to your command shell run ddfilelister.js. To list the files in the user's +current file directory, no command-line parameters are required. The command to +run ddfilelister.js can be placed in a command shell for a command key, or in SCFG > External Programs > Online Programs (Doors) in one of your program groups, and then you could have your command shell run the lister via the internal program code you configured in SCFG. -Installing into a command shell is described in section 3, "Installation & -Setup". Background: Running JavaScript scripts in Synchronet ---------------------------------------------------- diff --git a/xtrn/ddfilelister/revision_history.txt b/xtrn/ddfilelister/revision_history.txt index e73b206df6..688287ba6f 100644 --- a/xtrn/ddfilelister/revision_history.txt +++ b/xtrn/ddfilelister/revision_history.txt @@ -5,10 +5,12 @@ Revision History (change log) ============================= Version Date Description ------- ---- ----------- +2.09 2023-02-25 Now supports being used as a loadable module for Scan Dirs + and List Files (applicable for Synchronet 3.20+) 2.08 2023-01-18 When doing a file search in multiple directories, the file library & directory is now shown in the header as the user scrolls through the file list/search results. - Also, improved searching with extended descriptions to + Also, improved searching with extended descriptions to ensure all lines of the description are displayed. 2.07 2022-12-02 In a file's extended description, added the number of times downloaded and date/time last downloaded. Also, -- GitLab