Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save magma-chili/29d4f2d1c7a70fef3574c4670f2e3aeb to your computer and use it in GitHub Desktop.
Save magma-chili/29d4f2d1c7a70fef3574c4670f2e3aeb to your computer and use it in GitHub Desktop.
πŸ› οΈ Transfer folder contents to a different folder πŸ“βž‘οΈπŸ“ for Obsidian + Templater

<%* /*

*/
// #region HELPER VARIABLES
/*################
  HELPER VARIABLES
  ################*/

const noticeTimeout = 3000;
// #endregion HELPER VARIABLES

// #region HELPER FUNCTIONS
/*################
  HELPER FUNCTIONS
  ################*/

// DISPLAY A CONSOLE ERROR AND A NOTICE
const noticeError = function (message, timeout = noticeTimeout) {
  let templateMessage = `${tp.config.template_file.name}: ${message}`;
  console.error(templateMessage);
  new Notice(templateMessage, timeout);
};

// GET TFILES AND TFOLDERS
const getTFilesStartingWithPath = function (path) {
    return app.vault.getFiles().filter((t) => RegExp(`^${path}`).exec(t.path));
  },
  getTFoldersStartingWithPath = function (path) {
    return Array.from(
      new Set(Object.values(app.vault.fileMap).filter((t) => RegExp(`^${path}`).exec(t.path) && t.children))
    );
  };

// SORT TFILES AND TFOLDERS
const sortTItemsByPathLength = function (inputObject, sortBy = 'asc') {
    switch (sortBy) {
      case 'asc':
        return Array.from(inputObject).sort((a, b) => a.path.length - b.path.length);
      case 'desc':
        return Array.from(inputObject).sort((a, b) => b.path.length - a.path.length);
    }
  },
  sortTItemsByPathDepthAlphabetical = function (inputObject) {
    const sortTItemsByPathAlphabetical = function (inputObject) {
        return Array.from(inputObject).sort((a, b) => a.path.localeCompare(b.path, 'en', { sensitivity: 'base' }));
      },
      alphabeticallySortedArray = sortTItemsByPathAlphabetical(inputObject),
      depthSortedArray = Array.from(alphabeticallySortedArray).sort(
        (a, b) => a.path.split('/').length - b.path.split('/').length 
      );
    return [...depthSortedArray.filter((t) => t.path === '/'), ...depthSortedArray.filter((t) => t.path !== '/')];
  };

// #endregion HELPER FUNCTIONS

// #region MOVE CHILD ITEMS OF sourceFolder TO destinationFolder
/*#####################################################
  MOVE CHILD ITEMS OF sourceFolder TO destinationFolder
  #####################################################*/
const moveFolderContents = async function (sourceFolder, destinationFolder) {
  if (sourceFolder.path === destinationFolder.path) {
    noticeError(`The source folder cannot be the same as the destination folder: '${sourceFolder.path}'`);
    return;
  }

  // GET CURRENT SUBFOLDERS AND APPROXIMATE NEW SUBFOLDER OF DESTINATION
  const sourceFolderSubFolders = getTFoldersStartingWithPath(sourceFolder.path).filter(
    (t) => t.path !== sourceFolder.path
  );
  const destinationNewSubFoldersPaths = sortTItemsByPathLength(
    sourceFolderSubFolders.filter((t) => t.path !== sourceFolder.path),
    'asc'
  ).map((t) => t.path.replace(RegExp(`^${sourceFolder.path}`), destinationFolder.path));

  // CREATE NEW SUBFOLDERS IN destinationFolder
  for await (const destinationNewSubFolderPath of destinationNewSubFoldersPaths) {
    try {
      if (!!(await app.vault.exists('destinationNewSubFolderPath'))) {
        console.log(`Creating '${destinationNewSubFolderPath}'.`);
        await app.vault.createFolder(destinationNewSubFolderPath);
      }
    } catch (error) {
      noticeError(`Could not create '${destinationNewSubFolderPath}': ${error}`);
    }
  }

  // MOVE SUBFILES FROM sourceFolder TO destinationFolder
  const sourceFolderSubFiles = getTFilesStartingWithPath(sourceFolder.path),
    destinationNewSubFilesObjects = sortTItemsByPathLength(
      sourceFolderSubFiles.filter((t) => t.path !== sourceFolder.path),
      'desc'
    ).map((t) => {
      return {
        // Need to remove extension from destination path because Templater automatically appends it from the TFile object.
        destinationPath: t.path
          .replace(RegExp(`^${sourceFolder.path}`), destinationFolder.path)
          .replace(RegExp(`\\.${t.extension}$`), ''),
        sourceTFile: t,
      };
    });

  for await (const destinationNewSubFileObject of destinationNewSubFilesObjects) {
    const { destinationPath, sourceTFile } = destinationNewSubFileObject;
    try {
      console.log(`Moving '${sourceTFile.path}' to '${destinationPath}'.`);
      await tp.file.move(destinationPath, sourceTFile);
    } catch (error) {
      noticeError(`Could not move '${sourceTFile.path}' to '${destinationPath}': ${error}`);
    }
  }

  // REMOVE PRIOR SUBFOLDERS IF THEY ARE EMPTY
  for await (const sourceFolderSubFolder of sourceFolderSubFolders) {
    const { children, path } = sourceFolderSubFolder;
    try {
      if (children.length === 0) {
        console.log(`Removing '${path}'.`);
        await app.fileManager.trashFile(sourceFolderSubFolder);
      } else {
        noticeError(`Could not remove '${path}': folder is not empty.`);
      }
    } catch (error) {
      noticeError(`Could not remove '${path}': ${error}`);
    }
  }
};
// #endregion MOVE CHILD ITEMS OF sourceFolder TO destinationFolder

// #region main
/*#####################################################################
  PROMPT USER FOR SOURCE AND DESTINATION FOLDER TEMPLATER SUGGESTER API
  #####################################################################*/

const vaultFolders = getTFoldersStartingWithPath(''),
  sortedVaultFolders = sortTItemsByPathDepthAlphabetical(vaultFolders),
  sortedVaultFoldersPaths = sortedVaultFolders.map((t) => t.path),
  sourceFolder = await tp.system.suggester(
    sortedVaultFoldersPaths,
    sortedVaultFolders,
    true,
    "Select the folder's contents you would like to move to a different folder."
  ),
  destinationFolder = await tp.system.suggester(
    sortedVaultFoldersPaths,
    sortedVaultFolders,
    true,
    `Select the folder you would like to move the contents of '${sourceFolder.name}' to.`
  );

/*###########################################
  CREATE NEW FOLDERS, MOVE FILES, AND CLEANUP
  #########################################*/
await moveFolderContents(sourceFolder, destinationFolder);
// #endregion main

/*

*/ _%>

@magma-chili
Copy link
Author

2023-04-05.19-18-40.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment