/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Calendar code. * * The Initial Developer of the Original Code is * ArentJan Banck . * Portions created by the Initial Developer are Copyright (C) 2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): ArentJan Banck * Steve Hampton * Eric Belhaire * Jussi Kukkonen * Michiel van Leeuwen * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // File constants copied from file-utils.js const MODE_RDONLY = 0x01; const MODE_WRONLY = 0x02; const MODE_RDWR = 0x04; const MODE_CREATE = 0x08; const MODE_APPEND = 0x10; const MODE_TRUNCATE = 0x20; const MODE_SYNC = 0x40; const MODE_EXCL = 0x80; var gItems = new Array(); /** * loadEventsFromFile * shows a file dialog, reads the selected file(s) and tries to parse events from it. * * @param aCalendar (optional) If specified, the items will be imported directly * into the calendar */ function loadEventsFromFile(aCalendar) { const nsIFilePicker = Components.interfaces.nsIFilePicker; var fp = Components.classes["@mozilla.org/filepicker;1"] .createInstance(nsIFilePicker); fp.init(window, getCalStringBundle().GetStringFromName("Open"), nsIFilePicker.modeOpen); fp.defaultExtension = "ics"; // Get a list of exporters var contractids = new Array(); var catman = Components.classes["@mozilla.org/categorymanager;1"] .getService(Components.interfaces.nsICategoryManager); var catenum = catman.enumerateCategory('cal-importers'); while (catenum.hasMoreElements()) { var entry = catenum.getNext(); entry = entry.QueryInterface(Components.interfaces.nsISupportsCString); var contractid = catman.getCategoryEntry('cal-importers', entry); var exporter = Components.classes[contractid] .getService(Components.interfaces.calIImporter); var types = exporter.getFileTypes({}); var type; for each (type in types) { fp.appendFilter(type.description, type.extensionFilter); contractids.push(contractid); } } fp.show(); if (fp.file && fp.file.path && fp.file.path.length > 0) { var filePath = fp.file.path; var importer = Components.classes[contractids[fp.filterIndex]] .getService(Components.interfaces.calIImporter); const nsIFileInputStream = Components.interfaces.nsIFileInputStream; const nsIScriptableInputStream = Components.interfaces.nsIScriptableInputStream; var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"] .createInstance(nsIFileInputStream); try { inputStream.init( fp.file, MODE_RDONLY, 0444, {} ); gItems = importer.importFromStream(inputStream, {}); inputStream.close(); } catch(ex) { showError(getCalStringBundle().GetStringFromName("unableToRead") + filePath + "\n"+ex ); } if (aCalendar) { putItemsIntoCal(aCalendar); return; } var count = new Object(); var calendars = getCalendarManager().getCalendars(count); if (count.value == 1) { // There's only one calendar, so it's silly to ask what calendar // the user wants to import into. putItemsIntoCal(calendars[0]); } else { // Ask what calendar to import into var args = new Object(); args.onOk = putItemsIntoCal; args.promptText = getCalStringBundle().GetStringFromName("importPrompt"); openDialog("chrome://calendar/content/chooseCalendarDialog.xul", "_blank", "chrome,titlebar,modal,resizable", args); } } } function putItemsIntoCal(destCal) { // Set batch for the undo/redo transaction manager startBatchTransaction(); // And set batch mode on the calendar, to tell the views to not // redraw until all items are imported destCal.startBatch(); // This listener is needed to find out when the last addItem really // finished. Using a counter to find the last item (which might not // be the last item added) var count = 0; var failedCount = 0; // Used to store the last error. Only the last error, because we don't // wan't to bomb the user with thousands of error messages in case // something went really wrong. // (example of something very wrong: importing the same file twice. // quite easy to trigger, so we really should do this) var lastError; var listener = { onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDetail) { count++; if (!Components.isSuccessCode(aStatus)) { failedCount++; lastError = aStatus; } // See if it is time to end the calendar's batch. if (count == gItems.length) { destCal.endBatch(); if (failedCount) showError(failedCount+" items failed to import. The last error was: "+lastError.toString()); } } } for each (item in gItems) { // XXX prompt when finding a duplicate. try { destCal.addItem(item, listener); } catch(e) { failedCount++; lastError = e; // Call the listener's operationComplete, to increase the // counter and not miss failed items. Otherwise, endBatch might // never be called. listener.onOperationComplete(null, null, null, null, null); Components.utils.reportError("Import error: "+e); } } // End transmgr batch endBatchTransaction(); } /** * saveEventsToFile * * Save data to a file. Create the file or overwrite an existing file. * * @param calendarEventArray (required) Array of calendar events that should * be saved to file. * @param aDefaultFileName (optional) Initial filename shown in SaveAs dialog. */ function saveEventsToFile(calendarEventArray, aDefaultFileName) { if (!calendarEventArray) return; if (!calendarEventArray.length) { alert(getCalStringBundle().GetStringFromName("noEventsToSave")); return; } // Show the 'Save As' dialog and ask for a filename to save to const nsIFilePicker = Components.interfaces.nsIFilePicker; var fp = Components.classes["@mozilla.org/filepicker;1"] .createInstance(nsIFilePicker); fp.init(window, getCalStringBundle().GetStringFromName("SaveAs"), nsIFilePicker.modeSave); if (aDefaultFileName && aDefaultFileName.length && aDefaultFileName.length > 0) { fp.defaultString = aDefaultFileName; } else if (calendarEventArray.length == 1 && calendarEventArray[0].title) { fp.defaultString = calendarEventArray[0].title; } else { fp.defaultString = getCalStringBundle().GetStringFromName("defaultFileName"); } fp.defaultExtension = "ics"; // Get a list of exporters var contractids = new Array(); var catman = Components.classes["@mozilla.org/categorymanager;1"] .getService(Components.interfaces.nsICategoryManager); var catenum = catman.enumerateCategory('cal-exporters'); while (catenum.hasMoreElements()) { var entry = catenum.getNext(); entry = entry.QueryInterface(Components.interfaces.nsISupportsCString); var contractid = catman.getCategoryEntry('cal-exporters', entry); var exporter = Components.classes[contractid] .getService(Components.interfaces.calIExporter); var types = exporter.getFileTypes({}); var type; for each (type in types) { fp.appendFilter(type.description, type.extensionFilter); contractids.push(contractid); } } fp.show(); // Now find out as what to save, convert the events and save to file. if (fp.file && fp.file.path.length > 0) { const UTF8 = "UTF-8"; var aDataStream; var extension; var charset; var exporter = Components.classes[contractids[fp.filterIndex]] .getService(Components.interfaces.calIExporter); var filePath = fp.file.path; if(filePath.indexOf(".") == -1 ) filePath += "."+exporter.getFileTypes({})[0].defaultExtension; const LOCALFILE_CTRID = "@mozilla.org/file/local;1"; const FILEOUT_CTRID = "@mozilla.org/network/file-output-stream;1"; const nsILocalFile = Components.interfaces.nsILocalFile; const nsIFileOutputStream = Components.interfaces.nsIFileOutputStream; var outputStream; var localFileInstance = Components.classes[LOCALFILE_CTRID] .createInstance(nsILocalFile); localFileInstance.initWithPath(filePath); outputStream = Components.classes[FILEOUT_CTRID] .createInstance(nsIFileOutputStream); try { outputStream.init(localFileInstance, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, 0664, 0); // XXX Do the right thing with unicode and stuff. Or, again, should the // exporter handle that? exporter.exportToStream(outputStream, calendarEventArray.length, calendarEventArray); outputStream.close(); } catch(ex) { showError(getCalStringBundle().GetStringFromName("unableToWrite") + filePath ); } } } /* Exports all the events and tasks in a calendar. If aCalendar is not specified, * the user will be prompted with a list of calendars to choose which one to export. */ function exportEntireCalendar(aCalendar) { var itemArray = []; var getListener = { onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDetail) { saveEventsToFile(itemArray, aCalendar.name); }, onGetResult: function(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) { for each (item in aItems) { itemArray.push(item); } } }; function getItemsFromCal(aCal) { aCal.getItems(Components.interfaces.calICalendar.ITEM_FILTER_ALL_ITEMS, 0, null, null, getListener); } if (!aCalendar) { var count = new Object(); var calendars = getCalendarManager().getCalendars(count); if (count.value == 1) { // There's only one calendar, so it's silly to ask what calendar // the user wants to import into. getItemsFromCal(calendars[0]); } else { // Ask what calendar to import into var args = new Object(); args.onOk = getItemsFromCal; args.promptText = getCalStringBundle().GetStringFromName("exportPrompt"); openDialog("chrome://calendar/content/chooseCalendarDialog.xul", "_blank", "chrome,titlebar,modal,resizable", args); } } else { getItemsFromCal(aCalendar); } } function getCalStringBundle() { var strBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"] .getService(Components.interfaces.nsIStringBundleService); return strBundleService.createBundle("chrome://calendar/locale/calendar.properties"); } function showError(aMsg) { var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); promptService.alert(null, getCalStringBundle().GetStringFromName('errorTitle'), aMsg); }