I have improved the export to MyLifeOrganized, now I export:
- Task colors
- Flag state
- Completion state
- Comments in plain text and in HTML format! RTF / rich text comments are also exported in HTML
- Link or multiple links
- Task dependency or multiple dependencies
- First level task exported in bold, like in ToDoList
- Folders and projects: These are not available in ToDoList, but I set a criteria for setting those in MyLifeOrganized
- Fixed importance scale
- Fixed dates
Here it is the final code (that might apply to ToDoList 8.1 only, later versions might include Dan's improvements):
bool SupportsHtmlComments() const { return true; }
#include "stdafx.h"
#include "MLOImport.h"
#include "MLOExporter.h"
#include "..\shared\xmlfileex.h"
#include "..\shared\datehelper.h"
#include "..\shared\timehelper.h"
#include "..\shared\misc.h"
#include "..\3rdParty\T64Utils.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
CMLOExporter::CMLOExporter()
{
m_icon.Load(IDI_MYLIFEORGANISED);
}
CMLOExporter::~CMLOExporter()
{
}
void CMLOExporter::SetLocalizer(ITransText* )
{
}
IIMPORTEXPORT_RESULT CMLOExporter::Export(const ITaskList* pSrcTaskFile, LPCTSTR szDestFilePath, DWORD , IPreferences* , LPCTSTR )
{
const ITASKLISTBASE* pTasks = GetITLInterface<ITASKLISTBASE>(pSrcTaskFile, IID_TASKLISTBASE);
if (pTasks == NULL)
{
ASSERT(0);
return IIER_BADINTERFACE;
}
CXmlFile fileDest(_T("MyLifeOrganized-xml"));
fileDest.SetXmlHeader(DEFAULT_UTF8_HEADER);
fileDest.AddItem(_T("ver"), _T("1.2"));
CXmlItem* pXITasks = fileDest.AddItem(_T("TaskTree"));
if (!ExportTask(pTasks, pSrcTaskFile->GetFirstTask(), pXITasks, TRUE))
return IIER_SOMEFAILED;
ExportPlaces(pTasks, fileDest.Root());
if (!fileDest.Save(szDestFilePath, SFEF_UTF8WITHOUTBOM))
return IIER_BADFILE;
return IIER_SUCCESS;
}
IIMPORTEXPORT_RESULT CMLOExporter::Export(const IMultiTaskList* pSrcTaskFile, LPCTSTR szDestFilePath, DWORD , IPreferences* , LPCTSTR )
{
CXmlFile fileDest(_T("MyLifeOrganized-xml"));
fileDest.SetXmlHeader(DEFAULT_UTF8_HEADER);
fileDest.AddItem(_T("ver"), _T("1.2"));
CXmlItem* pXITasks = fileDest.AddItem(_T("TaskTree"));
for (int nTaskList = 0; nTaskList < pSrcTaskFile->GetTaskListCount(); nTaskList++)
{
const ITASKLISTBASE* pTasks = GetITLInterface<ITASKLISTBASE>(pSrcTaskFile->GetTaskList(nTaskList), IID_TASKLISTBASE);
if (pTasks == NULL)
{
ASSERT(0);
return IIER_BADINTERFACE;
}
if (!ExportTask(pTasks, pTasks->GetFirstTask(), pXITasks, TRUE))
return IIER_SOMEFAILED;
ExportPlaces(pTasks, fileDest.Root());
}
if (!fileDest.Save(szDestFilePath, SFEF_UTF8WITHOUTBOM))
return IIER_BADFILE;
return IIER_SUCCESS;
}
bool CMLOExporter::ExportTask(const ITASKLISTBASE* pSrcTaskFile, HTASKITEM hTask,
CXmlItem* pXIDestParent, BOOL bAndSiblings)
{
if (!hTask)
return true;
CXmlItem* pXIDestItem = pXIDestParent->AddItem(_T("TaskNode"));
if (!pXIDestItem)
return false;
pXIDestItem->AddItem(_T("Caption"), pSrcTaskFile->GetTaskTitle(hTask));
int nPriority = pSrcTaskFile->GetTaskPriority(hTask, FALSE);
int nImportance = max(nPriority, 0) * 20;
pXIDestItem->AddItem(_T("Importance"), nImportance, XIT_ELEMENT);
CString strIDD;
unsigned long nIDD = pSrcTaskFile->GetTaskID(hTask);
CString strTitle = pSrcTaskFile->GetTaskTitle(hTask);
unsigned long nTaskNameCode = strTitle.GetLength();
if (nTaskNameCode > 1)
nTaskNameCode *= int(strTitle[1]);
strIDD.Format(_T("{%08lu-0000-1111-0000-%012lu}"), nIDD, nTaskNameCode);
pXIDestItem->AddItem(_T("IDD"), strIDD, XIT_ELEMENT);
CString strNote;
int nDependencies = pSrcTaskFile->GetTaskDependencyCount(hTask);
if (nDependencies > 0)
{
CXmlItem* pXIDestDependenciesItem = pXIDestItem->AddItem(_T("Dependency"), _T(""), XIT_ELEMENT);
if (!pXIDestDependenciesItem)
return false;
CString strUID;
CString strDependencyCount;
for (int nDependency = 0; nDependency < nDependencies; ++nDependency)
{
LPCWSTR pStrDependency = pSrcTaskFile->GetTaskDependency(hTask, nDependency);
strDependencyCount.Format(_T("%d"), nDependency + 1);
strNote += _T("ToDoList Dependency (") + strDependencyCount + _T("): [ID " + CString(pStrDependency) + _T("] "));
unsigned long nID = _wtoi(pStrDependency);
HTASKITEM hTaskDependency = pSrcTaskFile->FindTask(nID);
if (hTaskDependency)
{
unsigned long nUID = pSrcTaskFile->GetTaskID(hTaskDependency);
CString strDependencyTitle = pSrcTaskFile->GetTaskTitle(hTaskDependency);
unsigned long nTaskDependencyNameCode = strDependencyTitle.GetLength();
if (nTaskDependencyNameCode > 1)
nTaskDependencyNameCode *= int(strDependencyTitle[1]);
strUID.Format(_T("{%08lu-0000-1111-0000-%012lu}"), nUID, nTaskDependencyNameCode);
pXIDestDependenciesItem->AddItem(_T("UID"), strUID, XIT_ELEMENT);
strNote += strDependencyTitle;
}
strNote += _T("\r\n\r\n");
}
}
time64_t tDate = T64Utils::T64_NULL;
if (pSrcTaskFile->GetTaskCreationDate64(hTask, tDate))
pXIDestItem->AddItem(_T("Created"), FormatDate(tDate), XIT_ELEMENT);
if (pSrcTaskFile->GetTaskDoneDate64(hTask, tDate))
pXIDestItem->AddItem(_T("CompletionDateTime"), FormatDate(tDate), XIT_ELEMENT);
if (pSrcTaskFile->GetTaskDueDate64(hTask, false, tDate))
pXIDestItem->AddItem(_T("DueDateTime"), FormatDate(tDate), XIT_ELEMENT);
TDC_UNITS nUnits;
double dTimeEst = pSrcTaskFile->GetTaskTimeEstimate(hTask, nUnits, FALSE);
if (dTimeEst > 0.0)
{
TH_UNITS nTHUnits = MapUnitsToTHUnits(nUnits);
pXIDestItem->AddItem(_T("EstimateMax"), CTimeHelper().Convert(dTimeEst, nTHUnits, THU_DAYS), XIT_ELEMENT);
}
CString strLink;
static const CString urlProtocol = _T("://");
static const CString fileProtocol = _T("file://");
int nLinks = pSrcTaskFile->GetTaskFileLinkCount(hTask);
if (nLinks > 1)
{
CString strLinkCount;
for (int nLinkCount = 0; nLinkCount < nLinks; ++nLinkCount)
{
strLink = pSrcTaskFile->GetTaskFileLink(hTask, nLinkCount);
strLinkCount.Format(_T("%d"), nLinkCount+1);
strNote += _T("ToDoList Link (") + strLinkCount + _T("): <");
if (strLink.Find(urlProtocol) < 0)
strNote += fileProtocol;
strNote += CString(strLink) + _T(">\r\n\r\n");
}
}
else
{
strLink = pSrcTaskFile->GetTaskFileLinkPath(hTask);
if (!Misc::IsEmpty(strLink))
{
strNote += _T("ToDoList Link: <");
if (strLink.Find(urlProtocol) < 0)
strNote += fileProtocol;
strNote += CString(strLink) + _T(">\r\n\r\n");
}
}
LPCTSTR szComments = pSrcTaskFile->GetTaskComments(hTask);
CString strHtmlComments = pSrcTaskFile->GetTaskAttribute(hTask, TDCA_HTMLCOMMENTS);
CString strCommentsType;
strHtmlComments.Trim();
bool bHasFormattedComments = !strHtmlComments.IsEmpty();
if (bHasFormattedComments)
{
bool bIsRTFContent = (strHtmlComments.Find(_T("<div class=WordSection1>")) >= 0);
if (bIsRTFContent)
{
strHtmlComments.Replace(_T("<div class=WordSection1>"), _T(""));
strHtmlComments.Replace(_T("</div>"), _T(""));
strHtmlComments.Replace(_T(" style='font-size:8.0pt;font-family:"), _T(""));
strHtmlComments.Replace(_T("\"Arial\",\"sans-serif\"'"), _T(""));
strHtmlComments.Replace(_T(" lang=EN-GB"), _T(""));
strHtmlComments.Replace(_T(" class=MsoNormal style='margin-top:0cm;margin-bottom:.0001pt;line-height:"), _T(""));
strHtmlComments.Replace(_T("normal;text-autospace:none'"), _T(""));
strHtmlComments.Replace(_T("<p\r\n>"), _T("<p>"));
strHtmlComments.Replace(_T("<p\n>"), _T("<p>"));
strHtmlComments.Replace(_T("<span\r\n>"), _T("<span>"));
strHtmlComments.Replace(_T("<span\n>"), _T("<span>"));
strCommentsType = _T("RTF"); }
else
strCommentsType = _T("HTML");
strNote += _T("ToDoList Comments (from ") + strCommentsType + _T("): *** To see format in notes, activate \"Use Markdown format in notes\" in the MyLifeOrganized options ***\r\n\r\n");
}
if ((!Misc::IsEmpty(strNote)) && (!Misc::IsEmpty(szComments)))
strNote += _T("\r\n\r\n");
if (!Misc::IsEmpty(szComments))
{
if (!strHtmlComments.IsEmpty())
strNote += _T("----- *** ToDoList Plain Text (HTML below) *** -----\r\n\r\n");
strNote += szComments;
}
if (!strHtmlComments.IsEmpty())
strNote += _T("\r\n\r\n\r\n----- *** ToDoList HTML (from ") + strCommentsType + _T(") *** (MyLifeOrganized supports only basic HTML formatting and Markdown) -----\r\n\r\n") + strHtmlComments;
if (!strNote.IsEmpty())
pXIDestItem->AddItem(_T("Note"), strNote, XIT_ELEMENT);
if ((pSrcTaskFile->GetFirstTask(hTask) != nullptr)) {
if (strNote.IsEmpty())
pXIDestItem->AddItem(_T("HideInToDoThisTask"), -1, XIT_ELEMENT); else
pXIDestItem->AddItem(_T("IsProject"), -1, XIT_ELEMENT); }
bool bTaskFlagged = pSrcTaskFile->IsTaskFlagged(hTask, false);
if (bTaskFlagged)
pXIDestItem->AddItem(_T("Flag"), _T("Red Flag"), XIT_ELEMENT);
CXmlItem* pXIDestCustomFormatItem = pXIDestItem->AddItem(_T("CustomFormat"), _T(""), XIT_ELEMENT);
if (!pXIDestCustomFormatItem)
return false;
bool bIsParentTask = pSrcTaskFile->IsTaskParent(hTask);
if (bIsParentTask)
pXIDestCustomFormatItem->AddItem(_T("Bold"), _T("1"), XIT_ELEMENT);
unsigned long unTaskColor = pSrcTaskFile->GetTaskColor(hTask);
CString strColor;
strColor.Format(_T("%lu"), unTaskColor);
pXIDestCustomFormatItem->AddItem(_T("FontColor"), strColor, XIT_ELEMENT);
pXIDestCustomFormatItem->AddItem(_T("SideBarColor"), strColor, XIT_ELEMENT);
if (!ExportTask(pSrcTaskFile, pSrcTaskFile->GetFirstTask(hTask), pXIDestItem, TRUE))
return false;
if (bAndSiblings)
{
HTASKITEM hSibling = pSrcTaskFile->GetNextTask(hTask);
while (hSibling)
{
if (!ExportTask(pSrcTaskFile, hSibling, pXIDestParent, FALSE))
return false;
hSibling = pSrcTaskFile->GetNextTask(hSibling);
}
}
return true;
}
CString CMLOExporter::FormatDate(time64_t tDate)
{
COleDateTime date = CDateHelper::GetDate(tDate);
return CDateHelper::FormatDate(date, DHFD_ISO | DHFD_TIME, 'T');
}
void CMLOExporter::BuildPlacesMap(const ITASKLISTBASE* pSrcTaskFile, HTASKITEM hTask,
CMapStringToString& mapPlaces, BOOL bAndSiblings)
{
if (!hTask)
return;
int nCat = pSrcTaskFile->GetTaskCategoryCount(hTask);
while (nCat--)
{
CString sCat = pSrcTaskFile->GetTaskCategory(hTask, nCat);
CString sCatUpper(sCat);
sCat.MakeUpper();
mapPlaces[sCatUpper] = sCat;
}
BuildPlacesMap(pSrcTaskFile, pSrcTaskFile->GetFirstTask(hTask), mapPlaces, TRUE);
if (bAndSiblings)
{
HTASKITEM hSibling = pSrcTaskFile->GetNextTask(hTask);
while (hSibling)
{
BuildPlacesMap(pSrcTaskFile, hSibling, mapPlaces, FALSE);
hSibling = pSrcTaskFile->GetNextTask(hSibling);
}
}
}
void CMLOExporter::ExportPlaces(const ITASKLISTBASE* pSrcTaskFile, CXmlItem* pDestPrj)
{
CMapStringToString mapPlaces;
BuildPlacesMap(pSrcTaskFile, pSrcTaskFile->GetFirstTask(), mapPlaces, TRUE);
if (mapPlaces.GetCount())
{
CXmlItem* pXIResources = pDestPrj->AddItem(_T("PlacesList"));
CString sPlace, sPlaceUpper;
POSITION pos = mapPlaces.GetStartPosition();
while (pos)
{
mapPlaces.GetNextAssoc(pos, sPlaceUpper, sPlace);
CXmlItem* pXIRes = pXIResources->AddItem(_T("TaskPlace"));
if (pXIRes)
pXIRes->AddItem(_T("Caption"), sPlace);
}
}
}
TH_UNITS CMLOExporter::MapUnitsToTHUnits(TDC_UNITS nUnits)
{
switch (nUnits)
{
case TDCU_MINS: return THU_MINS;
case TDCU_HOURS: return THU_HOURS;
case TDCU_DAYS: return THU_DAYS;
case TDCU_WEEKDAYS: return THU_WEEKDAYS;
case TDCU_WEEKS: return THU_WEEKS;
case TDCU_MONTHS: return THU_MONTHS;
case TDCU_YEARS: return THU_YEARS;
}
ASSERT(0);
return THU_NULL;
}
Hope it is useful for someone!
modified 10-Aug-22 17:41pm.
|