Exporting and Importing all Timing Projects

Timing now offers a proper built-in sync solution, so in many cases that option is preferable. However, you can still use these scripts to export a "master set" of projects, then import those projects on all your teammates' Macs. For more information, see this article.

Simply copy one of the following script into a new "Script Editor" document, select "JavaScript" as the script's language, and press run (see this article for more detailed instructions).
After pasting them, consider saving the scripts to disk for future re-use. You could even save them to iCloud Drive (or e.g. Dropbox or Google Drive) to have them available on all your Macs.
Feel free to customize the scripts for your use cases.

Note: We do not take responsibility for any data loss incurred by running these scripts.
Make sure to back up your data (e.g. by copying the directories mentioned here to a different location) before running these scripts.

Exporting All Timing Projects to a JSON File

// Copyright (c) 2018 timingapp.com / Daniel Alm. All rights reserved.
// This script is licensed only to extend the functionality of Timing. Redistribution and any other uses are not allowed without prior permission from us.
var helper = Application("TimingHelper");
if (!helper.scriptingSupportAvailable()) { throw "Scripting support requires a Timing Expert license. Please contact support via https://timingapp.com/contact to upgrade."; }
var app = Application.currentApplication();
app.includeStandardAdditions = true;

var projectHierarchyPath = app.chooseFileName({ withPrompt: "Select which file to write the Project hierarchy to.", defaultName: "TimingProjects.json" }).toString();

$.NSFileManager.defaultManager.createDirectoryAtPathWithIntermediateDirectoriesAttributesError($(projectHierarchyPath).stringByDeletingLastPathComponent.stringByStandardizingPath, true, $(), $());

var projectCount = 0;
function enumerateProjects(projects) {
	return projects.map(function(project) { projectCount += 1; return { name: project.name(), color: project.color(), productivityRating: project.productivityRating(), ruleData: project.ruleData(), children: enumerateProjects(project.projects()) }; });
}

var fullProjectHierarchy = enumerateProjects(helper.rootProjects());

var str = $(JSON.stringify(fullProjectHierarchy, null, 4));
str.writeToFileAtomicallyEncodingError($(projectHierarchyPath).stringByStandardizingPath, true, $.NSUTF8StringEncoding, $());
"Wrote " + projectCount + " projects to '" + projectHierarchyPath + "'.";

Importing a List of Projects from a JSON File into Timing

var importRules = true;
var overwriteExistingRules = true;

// Copyright (c) 2018 timingapp.com / Daniel Alm. All rights reserved.
// This script is licensed only to extend the functionality of Timing. Redistribution and any other uses are not allowed without prior permission from us.
var helper = Application("TimingHelper");
if (!helper.scriptingSupportAvailable()) { throw "Scripting support requires a Timing Expert license. Please contact support via https://timingapp.com/contact to upgrade."; }
var app = Application.currentApplication();
app.includeStandardAdditions = true;

app.displayDialog("Before proceeding, please make sure to back up your Timing database.\n\nSee https://timingapp.com/help/faq#data on which folders you need to back up.");

var projectHierarchyPath = app.chooseFile({ withPrompt: "Select which file to read the Project hierarchy from.", ofType: ["public.json"] }).toString();

var projectCount = 0;
function importProjects(projects, parent) {
	var existingChildren = parent ? parent.projects() : helper.rootProjects()
	for (var project of projects) {
		if (!project.name) continue;
		projectCount += 1;
		
		var newProject = existingChildren.filter(function(childProject) { return childProject.name() == project.name; })[0];
		var args = { name: project.name };
		if (project.color) { args.color = project.color; }
		if (project.productivityRating) { args.productivityRating = project.productivityRating; }
		if (importRules
			&& project.ruleData
			&& (overwriteExistingRules || !newProject || !newProject.ruleData())) { args.ruleData = project.ruleData; }
		if (!newProject) {
			if (parent) {
				args.parentProject = parent;
			}
			newProject = helper.createProject(args);
		} else {
			helper.updateProject(newProject, args);
		}
		
		importProjects(project.children, newProject);
	}
}

var fullProjectHierarchy = $.NSString.stringWithContentsOfFileEncodingError(
	$(projectHierarchyPath).stringByStandardizingPath,
	$.NSUTF8StringEncoding,
	$()
).js;

importProjects(JSON.parse(fullProjectHierarchy), null);
"Imported " + projectCount + " projects from '" + projectHierarchyPath + "'.";

Importing a List of Projects from a Text File into Timing

The following script imports a list of project names from a plain text file.
Each project should be on a separate line.
If you'd like to import projects hierarchically, use the project's full "path" in one line, separated by " ▸ ".
Example: Client A ▸ Project X ▸ Subproject Y

// Copyright (c) 2018 timingapp.com / Daniel Alm. All rights reserved.
// This script is licensed only to extend the functionality of Timing. Redistribution and any other uses are not allowed without prior permission from us.
var helper = Application("TimingHelper");
if (!helper.scriptingSupportAvailable()) { throw "Scripting support requires a Timing Expert license. Please contact support via https://timingapp.com/contact to upgrade."; }
var app = Application.currentApplication();
app.includeStandardAdditions = true;

app.displayDialog("Before proceeding, please make sure to back up your Timing database.\n\nSee https://timingapp.com/help/faq#data on which folders you need to back up.");

function createProjectWithNameChainElements(projectNameChainElements) {
	var currentProject = null;
	var currentSearchSpace = helper.rootProjects();
	for (var projectName of projectNameChainElements) {
		if (!projectName) continue;
		
		var newProject = currentSearchSpace.filter(function(project) { return project.name() == projectName; })[0];
		if (!newProject) {
			var arguments = { name: projectName };
			if (currentProject) {
				arguments["parentProject"] = currentProject;
			}
			newProject = helper.createProject(arguments);
		}
		currentProject = newProject;
		currentSearchSpace = currentProject.projects();
	}
	return currentProject;
}

function createProjectWithNameChain(projectNameChain) {
	return createProjectWithNameChainElements(projectNameChain.split(" ▸ "));
}

var path = app.chooseFile({ withPrompt: "Select which file to read the project list from.", ofType: ["public.text"] });

var projectNameChains = $.NSString.stringWithContentsOfFileEncodingError(
	$(path.toString()).stringByStandardizingPath,
	$.NSUTF8StringEncoding,
	$()
).js.split("\n");

var projectCount = 0;
for (var projectNameChain of projectNameChains) {
	if (projectNameChain) {
		projectCount += 1;
		createProjectWithNameChain(projectNameChain);
	}
}
"Imported " + projectCount + " projects from '" + path + "'.";