Copy a section of omnifocus tasks in taskpaper format with tags removed

Can someone help with a script to copy a section of OmniFocus tasks in TaskPaper format but with the OmniFocus tags removed?
Thank you

I am not familiar with OmniFocus and how it does the tags. If you share that with me I may be able to point to a previous script that takes tags out of task paper. I am not a programmer, but you may go and check the plugins and script page and see what is available.

1 Like

To test this in Script Editor, set the language selector at top left to JavaScript rather than AppleScript:

( If it seems to do what you need, you could assign a keystroke to it with something like Keyboard Maestro or FastScripts )

Expand disclosure triangle to view JS source
(() => {
    "use strict";

    // Copy selected OmniFocus tasks (with descendants)
    // as untagged Taskpaper string.

    // Rob Trew @2023
    // Ver 0.04

    // 0.04 Tidied
    // 0.03 Closed up redundant blank lines after
    //      noteless tasks.
    // 0.02 Added copying of OmniFocus notes, and
    //      also tested on OmniFocus 4 Trial version.

    ObjC.import("AppKit");

    // MAIN :: IO()
    const main = () => {
        const
            docWindow = Application("com.omnigroup.OmniFocus4")
            .defaultDocument.documentWindows.at(0);

        return either(
            alert(
                "Copy OmniFocus tasks as tagless TaskPaper"
            )
        )(
            copyText
        )(
            docWindow.exists()
                ? (() => {
                    const
                        selectedTrees = docWindow.content
                        .selectedTrees();

                    return 0 < selectedTrees.length
                        ? Right(
                            selectedTrees.flatMap(
                                tree => foldTree(
                                    taskPaperText
                                )(
                                    tree.value()
                                )
                            )
                            .join("\n")
                        )
                        : Left(
                            "No tasks selected in content panel."
                        );
                })()
                : Left("No windows open in OmnniFocus.")
        );
    };


    // taskPaperText :: OF Task -> [String] -> [String]
    const taskPaperText = task =>
        taskPaperChildren => {
            const note = task.note();

            return [
                // Task name bulleted,
                `- ${task.name()}`,

                // any note text indented,
                ...(
                    0 < note.length
                        ? note.split("\n")
                        .map(additionalTab)
                        : []
                ),

                // and all descendants indented.
                ...taskPaperChildren.flat()
                .map(additionalTab)
            ];
        };

    // additionalTab :: String -> String
    const additionalTab = s => `\t${s}`;


    // ----------------------- JXA -----------------------

    // alert :: String => String -> IO String
    const alert = title =>
        s => {
            const sa = Object.assign(
                Application("System Events"), {
                    includeStandardAdditions: true
                });

            return (
                sa.activate(),
                sa.displayDialog(s, {
                    withTitle: title,
                    buttons: ["OK"],
                    defaultButton: "OK"
                }),
                s
            );
        };


    // copyText :: String -> IO String
    const copyText = s => {
        const pb = $.NSPasteboard.generalPasteboard;

        return (
            pb.clearContents,
            pb.setStringForType(
                $(s),
                $.NSPasteboardTypeString
            ),
            s
        );
    };


    // -------------------- OMNIFOCUS --------------------

    // Definitions of `root` and `nest` specialised
    // for this application to OmniFocus.
    const root = x => x;

    const nest = x =>
        0 < x.numberOfTasks()
            ? x.tasks()
            : [];


    // --------------------- GENERIC ---------------------

    // Left :: a -> Either a b
    const Left = x => ({
        type: "Either",
        Left: x
    });


    // Right :: b -> Either a b
    const Right = x => ({
        type: "Either",
        Right: x
    });


    // either :: (a -> c) -> (b -> c) -> Either a b -> c
    const either = fl =>
    // Application of the function fl to the
    // contents of any Left value in e, or
    // the application of fr to its Right value.
        fr => e => "Left" in e
            ? fl(e.Left)
            : fr(e.Right);


    // foldTree :: (a -> [b] -> b) -> Tree a -> b
    const foldTree = f => {
        // The catamorphism on trees. A summary
        // value obtained by a depth-first fold.
        const go = tree => f(
            root(tree)
        )(
            nest(tree).map(go)
        );

        return go;
    };


    // MAIN
    return main();
})();

PS I’m not a user of OmniFocus. The version I tested this on is 3.15.4, which might not be up to date. Let me know if it doesn’t appear to copy the tasks selected in the OmniFocus content panel.

1 Like

Updated draft above to Ver 0.02

  • added copying of OmniFocus notes
  • tested on OmniFocus 4 trial version

The version above references:

Application("com.omnigroup.OmniFocus4")

for OmniFocus 3, you can edit this to:

Application("com.omnigroup.OmniFocus3")

@complexpoint Thank you for the script,
It’s much appreciated.and runs well with OmniFocus 4.0.2 (v174.5.2)
Stan

@Victor

3 Likes

Added to the scripts and themes page. Thank you @complexpoint

1 Like