User:Jcranmer/Writing an Importer: Difference between revisions

copy edits
No edit summary
(copy edits)
Line 1: Line 1:
When it comes to import, there are two basic pieces: the import service and the import modules or importers. The basic flow of the system is like this: when the user opens the import dialog, he is presented with a choice of things to import (such as an address book). Once this is selected, the import service queries all importers to see which can import the given object. Once an importer is chosen, the file to import is selected (if need be), and then an import is run off of the main thread.
There are two components to an extension that imports data into Thunderbird:


The first step to writing an importer is to implement the basic interface: <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/import/public/nsIImportModule.idl nsIImportModule]</tt>. The <tt>name</tt> and <tt>description</tt> properties are the strings that the user sees (the name being what appears in the list box, the description the blurb beneath the box). The <tt>supports</tt> property is a comma-delineated list of import types that the module supports--for example, the address book or mail filters. The <tt>supportsUpgrade</tt> property is, as far as I can tell, completely pointless and always has been.
<ul>
<li>the import service</li>
<li>the import modules (or "importers")</li>
<ul>


The final piece of <tt>nsIImportModule</tt> is the <tt>GetImportInterface</tt> function, which will return the object that actually does the import. The caller passes in the type. For mail and address book imports, the caller returns an <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/import/public/nsIImportGeneric.idl nsIImportGeneric]</tt> wrapping the actual import implementation; for other imports, the regular import implementation is what is returned.
The import process works like this:


To inform the import service of your module, you need to register it with the category manager. The category is "<tt>mailnewsimport</tt>" and the value should be the same as your supports string. If you're writing C++ code, you can see example code from the import module: [http://mxr.mozilla.org/comm-central/mailnews/import/build/nsImportModule.cpp].
<ol>
<li>The user opens the import dialog and selects something to import (such as an address book, messages, etc).</li>
<li>The import service queries all importers to see which can import the given object.</li>  
<li>If necessary, the user is prompted to specify the file containing the data to be imported.</li>
<Li>The import runs in a new thread.</li>
</ul>


A key thing to note about the importer is that the actual import operates on a separate thread; the pre-import steps are performed on the UI thread. For this reason, the importer implementation needs to have thread-safe addref and care should be taken to make sure that the code properly synchronizes across threads. It is recommended that you implement importers in C++ and not JS for this reason. Running on a debug build while building an importer would be very helpful.
== Building an importer ==
 
The first step to writing an importer is to implement the basic interface: <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/import/public/nsIImportModule.idl nsIImportModule]</tt>. The <tt>name</tt> and <tt>description</tt> properties are the strings that the user sees. (The name is displayed in the list box, the description displays beneath the box). The <tt>supports</tt> property is a comma-delineated list of import types that the module supports (for example, the address book or mail filters). The <tt>supportsUpgrade</tt> property is, as far as I can tell, not used.
 
The final piece of <tt>nsIImportModule</tt> is the <tt>GetImportInterface</tt> function, which will return the object that actually does the import. The caller passes in the type. For mail and address book imports, the caller returns an instance of <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/import/public/nsIImportGeneric.idl nsIImportGeneric]</tt>, wrapping the actual import implementation. For imports of other types of data, the regular import implementation is returned.
 
To inform the import service of your module, register it with the category manager. The category is "<tt>mailnewsimport</tt>" and the value should be the same as the <tt>supports</tt> string. If you are writing C++ code, there is example code in the import module: [http://mxr.mozilla.org/comm-central/mailnews/import/build/nsImportModule.cpp].
 
An important thing to note about the importer is that the actual import operates on a separate thread. The pre-import steps are performed on the UI thread. For this reason, the importer implementation needs to have thread-safe addref. Also, make sure that the code properly synchronizes across threads. For this reason, we recommend that you implement importers in C++ and not JavaScript. Running on a debug build while building an importer would be very helpful.


== Importing an address book ==
== Importing an address book ==


The address book supports string is <tt>NS_IMPORT_ADDRESS_STR</tt>, <tt>"addressbook"</tt>. The importer needs to implement the <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/import/public/nsIImportAddressBooks.idl nsIImportAddressBooks]</tt> interface, although the <tt>GetImportInterface</tt> must return it wrapped in an <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/import/public/nsIImportGeneric.idl nsIImportGeneric]</tt>.
The address book <tt>supports</tt> string is <tt>NS_IMPORT_ADDRESS_STR</tt>, <tt>"addressbook"</tt>. The importer needs to implement the <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/import/public/nsIImportAddressBooks.idl nsIImportAddressBooks]</tt> interface, although the <tt>GetImportInterface</tt> must return it wrapped in an <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/import/public/nsIImportGeneric.idl nsIImportGeneric]</tt> instance.


The first step in importing the address book is to get the file to import. If <tt>GetAutoFind</tt> returns false, the user will be presented a dialog with a file to import. If it's true, the user is sent straight to the import step. The description isn't terribly important in either case--it doesn't seem to be used anymore.
The first step in importing an address book is to get the file to import. If <tt>GetAutoFind</tt> returns false, the user will be presented a dialog with a file to import. If it is true, the user is sent to the import step. (The description is not important in either case - it doesn't seem to be used anymore.)


If <tt>GetAutoFind</tt> returned false, the next step is to select the file. <tt>GetDefaultLocation</tt> is then queried, to get the default file, if it's been found, and if the user can set the location. If the default file is not null, that is treated as the selected file; if it is null, a file picker is opened. If the address book supports multiple, a directory is selectable; if it does not, a file itself is selectable. If the user cannot set the location, this is treated as an error. The found status is completely and utterly pointless.
If <tt>GetAutoFind</tt> returned false, the next step is to select the file. <tt>GetDefaultLocation</tt> is then queried to get the default file (if it has been found, and if the user can set the location). If the default file is not null, it is treated as the selected file; if it is null, a file picker is opened. If the address book supports multiple, a directory is selectable; if it does not, a single file is selectable. If the user cannot set the location, it is treated as an error. The found status is not used.


The next step is to build the field map, if necessary. This is determined via <tt>GetNeedsFieldMap</tt>; the location parameter is the address book (file or directory) that we will be importing, determined from the past step. If <tt>GetAutoFind</tt> returned true, this function will not be called.
The next step is to build the field map, if necessary. This is determined via <tt>GetNeedsFieldMap</tt>. The location parameter is the address book (file or directory) that we will be importing, determined in the past step. If <tt>GetAutoFind</tt> returned true, this function will not be called.


''Information on the the field map is still under construction''
''Information on the the field map is still under construction''


After building the field map, the import can begin. <tt>FindAddressBooks</tt> is called with the location parameter as selected (it is null if <tt>GetAutoFind</tt> returned true). The return value here is an <tt>[http://mxr.mozilla.org/comm-central/source/mozilla/xpcom/ds/nsISupportsArray.idl nsISupportsArray]</tt> of <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/import/public/nsIImportABDescriptor.idl nsIImportABDescriptor]</tt>s, an array of information to coalesce into address books. Implementations can choose to use the default implementation by calling <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/import/public/nsIImportService.idl nsIImportService::CreateNewABDescriptor]</tt> or provide their own. The important parameters are <tt>import</tt> (whether or not the representative address book is imported), <tt>size</tt> (representative of the total amount to process), and <tt>preferredName</tt> (the name of the new address book).
After building the field map, the import begins. <tt>FindAddressBooks</tt> is called with the location parameter as selected (it is null if <tt>GetAutoFind</tt> returned true). The return value here is an <tt>[http://mxr.mozilla.org/comm-central/source/mozilla/xpcom/ds/nsISupportsArray.idl nsISupportsArray]</tt> of <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/import/public/nsIImportABDescriptor.idl nsIImportABDescriptor]</tt>s, an array of information to coalesce into address books. You can choose to use the default implementation by calling <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/import/public/nsIImportService.idl nsIImportService::CreateNewABDescriptor]</tt> or provide your own. The important parameters are <tt>import</tt> (whether or not the representative address book is imported), <tt>size</tt> (representing the total amount to process) and <tt>preferredName</tt> (the name of the new address book).


At this point, <tt>ImportAddressBook</tt> is called. The parameters here are your descriptor (unmolested), the database to import to (which is already a proxy object), the field map, a copy of the (proxied) <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/addrbook/public/nsIAbLDIFService.idl nsIAbLDIFService]</tt>, and a parameter as to whether or not an address location is the home (for NS 4.x import; it's false for everyone else). The output are strings for the error and success logs, as well as whether or not the fatal errors. The log strings will be shown to the users. Fatal errors will stop import immediately.
At this point, <tt>ImportAddressBook</tt> is called. The parameters here are the descriptor (unmolested), the database to import to (which is already a proxy object), the field map, a copy of the (proxied) <tt>[http://mxr.mozilla.org/comm-central/source/mailnews/addrbook/public/nsIAbLDIFService.idl nsIAbLDIFService]</tt>, and a parameter that indicates whether or not an address location is the home (true for Netscape 4.x import; false otherwise). The outputs are strings for the error and success logs, as well as whether or not there were fatal errors. The log strings will be shown to the users. Fatal errors will stop the import immediately.
== Importing mail ==
== Importing mail ==
This section is not yet finished.
This section is not yet finished.
Confirmed users
371

edits